epoch paged
try_load_performance returns per kind load_measurement_kind & unit test Naming stale submission unit test & note
This commit is contained in:
+4
-4
@@ -14,8 +14,8 @@ pub use nym_performance_contract_common::{
|
||||
EpochMeasurementsPagedResponse, EpochNodePerformance, EpochPerformancePagedResponse,
|
||||
FullHistoricalPerformancePagedResponse, HistoricalPerformance, LastSubmission,
|
||||
NetworkMonitorInformation, NetworkMonitorsPagedResponse, NodeId, NodeMeasurement,
|
||||
NodeMeasurementsResponse, NodePerformancePagedResponse, NodePerformanceResponse,
|
||||
NodePerformanceSpecific, RetiredNetworkMonitor, RetiredNetworkMonitorsPagedResponse,
|
||||
NodeMeasurementsPerKindResponse, NodePerformance, NodePerformancePagedResponse,
|
||||
NodePerformanceResponse, RetiredNetworkMonitor, RetiredNetworkMonitorsPagedResponse,
|
||||
};
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -60,7 +60,7 @@ pub trait PerformanceQueryClient {
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
node_id: NodeId,
|
||||
) -> Result<NodeMeasurementsResponse, NyxdError> {
|
||||
) -> Result<NodeMeasurementsPerKindResponse, NyxdError> {
|
||||
self.query_performance_contract(PerformanceQueryMsg::NodeMeasurements { epoch_id, node_id })
|
||||
.await
|
||||
}
|
||||
@@ -167,7 +167,7 @@ pub trait PagedPerformanceQueryClient: PerformanceQueryClient {
|
||||
async fn get_all_epoch_performance(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
) -> Result<Vec<NodePerformanceSpecific>, NyxdError> {
|
||||
) -> Result<Vec<NodePerformance>, NyxdError> {
|
||||
collect_paged!(self, get_epoch_performance_paged, performance, epoch_id)
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -10,7 +10,7 @@ use crate::nyxd::{Fee, SigningCosmWasmClient};
|
||||
use crate::signing::signer::OfflineSigner;
|
||||
use async_trait::async_trait;
|
||||
use nym_performance_contract_common::{
|
||||
EpochId, ExecuteMsg as PerformanceExecuteMsg, NodeId, NodePerformanceSpecific,
|
||||
EpochId, ExecuteMsg as PerformanceExecuteMsg, NodeId, NodePerformance,
|
||||
RemoveEpochMeasurementsResponse,
|
||||
};
|
||||
|
||||
@@ -42,7 +42,7 @@ pub trait PerformanceSigningClient {
|
||||
async fn submit_performance(
|
||||
&self,
|
||||
epoch: EpochId,
|
||||
data: NodePerformanceSpecific,
|
||||
data: NodePerformance,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_performance_contract(
|
||||
@@ -57,7 +57,7 @@ pub trait PerformanceSigningClient {
|
||||
async fn batch_submit_performance(
|
||||
&self,
|
||||
epoch: EpochId,
|
||||
data: Vec<NodePerformanceSpecific>,
|
||||
data: Vec<NodePerformance>,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_performance_contract(
|
||||
|
||||
@@ -9,7 +9,6 @@ pub mod storage_keys {
|
||||
pub const AUTHORISED_COUNT: &str = "authorised-count";
|
||||
pub const AUTHORISED: &str = "authorised";
|
||||
pub const RETIRED: &str = "retired";
|
||||
pub const PERFORMANCE_RESULTS: &str = "performance-results";
|
||||
pub const PERFORMANCE_RESULTS_PER_KIND: &str = "performance-results-per-kind";
|
||||
pub const PERFORMANCE_DEFINED_KINDS: &str = "performance-defined-kinds";
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{EpochId, NodeId, NodePerformanceSpecific};
|
||||
use crate::{EpochId, NodeId, NodePerformance};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
|
||||
#[cfg(feature = "schema")]
|
||||
use crate::types::{
|
||||
EpochMeasurementsPagedResponse, EpochPerformancePagedResponse,
|
||||
FullHistoricalPerformancePagedResponse, LastSubmission, NetworkMonitorResponse,
|
||||
NetworkMonitorsPagedResponse, NodeMeasurementsResponse, NodePerformancePagedResponse,
|
||||
NetworkMonitorsPagedResponse, NodeMeasurementsPerKindResponse, NodePerformancePagedResponse,
|
||||
NodePerformanceResponse, RetiredNetworkMonitorsPagedResponse,
|
||||
};
|
||||
|
||||
@@ -23,17 +23,16 @@ pub enum ExecuteMsg {
|
||||
/// Change the admin
|
||||
UpdateAdmin { admin: String },
|
||||
|
||||
// TODO dz rename to remove speicifc suffix
|
||||
/// Attempt to submit performance data of a particular node for given epoch
|
||||
SubmitSpecific {
|
||||
Submit {
|
||||
epoch: EpochId,
|
||||
data: NodePerformanceSpecific,
|
||||
data: NodePerformance,
|
||||
},
|
||||
|
||||
/// Attempt to submit performance data of a batch of nodes for given epoch
|
||||
BatchSubmit {
|
||||
epoch: EpochId,
|
||||
data: Vec<NodePerformanceSpecific>,
|
||||
data: Vec<NodePerformance>,
|
||||
},
|
||||
|
||||
/// Measurement kind needs to be defined by the admin before measurements of
|
||||
@@ -78,11 +77,9 @@ pub enum QueryMsg {
|
||||
limit: Option<u32>,
|
||||
},
|
||||
|
||||
/// Returns all submitted measurements for the particular node
|
||||
/// Returns all measurements of a specific kind for the particular node
|
||||
#[cfg_attr(feature = "schema", returns(NodeMeasurementsResponse))]
|
||||
NodeMeasurements { epoch_id: EpochId, node_id: NodeId },
|
||||
|
||||
NodeMeasurementsSpecific {
|
||||
NodeMeasurements {
|
||||
epoch_id: EpochId,
|
||||
node_id: NodeId,
|
||||
kind: String,
|
||||
|
||||
@@ -23,7 +23,7 @@ pub struct LastSubmission {
|
||||
pub struct LastSubmittedData {
|
||||
pub sender: Addr,
|
||||
pub epoch_id: EpochId,
|
||||
pub data: NodePerformanceSpecific,
|
||||
pub data: NodePerformance,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
@@ -50,14 +50,13 @@ pub struct RetiredNetworkMonitor {
|
||||
pub retired_at_height: u64,
|
||||
}
|
||||
|
||||
// TODO dz remove specific from name
|
||||
#[cw_serde]
|
||||
pub struct NodePerformanceSpecific {
|
||||
pub struct NodePerformance {
|
||||
#[serde(rename = "n")]
|
||||
pub node_id: NodeId,
|
||||
|
||||
#[serde(rename = "m")]
|
||||
pub measurement_kind: String,
|
||||
pub measurement_kind: MeasurementKind,
|
||||
|
||||
// note: value is rounded to 2 decimal places.
|
||||
#[serde(rename = "p")]
|
||||
@@ -106,13 +105,17 @@ impl NodeResults {
|
||||
}
|
||||
}
|
||||
|
||||
pub type MeasurementKind = String;
|
||||
|
||||
/// maps measurement kind to the value of that measurement for a node
|
||||
/// (present only if measured)
|
||||
#[cw_serde]
|
||||
pub struct NodePerformanceResponse {
|
||||
pub performance: Option<Percent>,
|
||||
pub performance: HashMap<MeasurementKind, Percent>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct NodeMeasurementsResponse {
|
||||
pub struct NodeMeasurementsPerKindResponse {
|
||||
pub measurements: Option<NodeResults>,
|
||||
}
|
||||
|
||||
@@ -120,14 +123,13 @@ pub struct NodeMeasurementsResponse {
|
||||
pub struct AllNodeMeasurementsResponse {
|
||||
// Option is used because if a measurement has been defined, that doesn't
|
||||
// mean the node had actually been measured at the time of the query
|
||||
pub measurements: HashMap<String, Option<NodeResults>>,
|
||||
pub measurements: HashMap<MeasurementKind, Option<NodeResults>>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub struct EpochNodePerformance {
|
||||
pub epoch: EpochId,
|
||||
pub performance: Option<Percent>,
|
||||
pub performance: HashMap<MeasurementKind, Percent>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
@@ -140,20 +142,20 @@ pub struct NodePerformancePagedResponse {
|
||||
#[cw_serde]
|
||||
pub struct EpochPerformancePagedResponse {
|
||||
pub epoch_id: EpochId,
|
||||
pub performance: Vec<NodePerformanceSpecific>,
|
||||
pub performance: Vec<NodePerformance>,
|
||||
pub start_next_after: Option<NodeId>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct NodeMeasurement {
|
||||
pub struct NodeMeasurements {
|
||||
pub node_id: NodeId,
|
||||
pub measurements: NodeResults,
|
||||
pub measurements_per_kind: HashMap<String, NodeResults>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct EpochMeasurementsPagedResponse {
|
||||
pub epoch_id: EpochId,
|
||||
pub measurements: Vec<NodeMeasurement>,
|
||||
pub measurements: Vec<NodeMeasurements>,
|
||||
pub start_next_after: Option<NodeId>,
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
use crate::queries::{
|
||||
query_admin, query_all_node_measurements, query_epoch_measurements_paged,
|
||||
query_epoch_performance_paged, query_full_historical_performance_paged, query_last_submission,
|
||||
query_network_monitor_details, query_network_monitors_paged, query_node_measurements,
|
||||
query_node_measurements_specific, query_node_performance, query_node_performance_paged,
|
||||
query_retired_network_monitors_paged,
|
||||
query_network_monitor_details, query_network_monitors_paged, query_node_measurements_for_kind,
|
||||
query_node_performance, query_node_performance_paged, query_retired_network_monitors_paged,
|
||||
};
|
||||
use crate::storage::NYM_PERFORMANCE_CONTRACT_STORAGE;
|
||||
use crate::transactions::{
|
||||
@@ -58,7 +57,7 @@ pub fn execute(
|
||||
) -> Result<Response, NymPerformanceContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::UpdateAdmin { admin } => try_update_contract_admin(deps, info, admin),
|
||||
ExecuteMsg::SubmitSpecific { epoch, data } => {
|
||||
ExecuteMsg::Submit { epoch, data } => {
|
||||
try_submit_performance_results(deps, env, info, epoch, data)
|
||||
}
|
||||
ExecuteMsg::BatchSubmit { epoch, data } => {
|
||||
@@ -126,18 +125,12 @@ pub fn query(deps: Deps, _: Env, msg: QueryMsg) -> Result<Binary, NymPerformance
|
||||
QueryMsg::RetiredNetworkMonitorsPaged { start_after, limit } => Ok(to_json_binary(
|
||||
&query_retired_network_monitors_paged(deps, start_after, limit)?,
|
||||
)?),
|
||||
QueryMsg::NodeMeasurements { epoch_id, node_id } => Ok(to_json_binary(
|
||||
&query_node_measurements(deps, epoch_id, node_id)?,
|
||||
)?),
|
||||
QueryMsg::NodeMeasurementsSpecific {
|
||||
QueryMsg::NodeMeasurements {
|
||||
epoch_id,
|
||||
node_id,
|
||||
kind: measurement_kind,
|
||||
} => Ok(to_json_binary(&query_node_measurements_specific(
|
||||
deps,
|
||||
epoch_id,
|
||||
node_id,
|
||||
measurement_kind,
|
||||
kind,
|
||||
} => Ok(to_json_binary(&query_node_measurements_for_kind(
|
||||
deps, epoch_id, node_id, kind,
|
||||
)?)?),
|
||||
QueryMsg::AllNodeMeasurements { epoch_id, node_id } => Ok(to_json_binary(
|
||||
&query_all_node_measurements(deps, epoch_id, node_id)?,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::storage::{NYM_PERFORMANCE_CONTRACT_STORAGE, retrieval_limits};
|
||||
use crate::storage::{MeasurementKind, NYM_PERFORMANCE_CONTRACT_STORAGE, retrieval_limits};
|
||||
use cosmwasm_std::{Addr, Deps, Order, StdResult};
|
||||
use cw_controllers::AdminResponse;
|
||||
use cw_storage_plus::Bound;
|
||||
@@ -11,8 +11,8 @@ use nym_performance_contract_common::{
|
||||
AllNodeMeasurementsResponse, EpochId, EpochMeasurementsPagedResponse, EpochNodePerformance,
|
||||
EpochPerformancePagedResponse, FullHistoricalPerformancePagedResponse, HistoricalPerformance,
|
||||
LastSubmission, NetworkMonitorInformation, NetworkMonitorResponse,
|
||||
NetworkMonitorsPagedResponse, NodeId, NodeMeasurement, NodeMeasurementsResponse,
|
||||
NodePerformancePagedResponse, NodePerformanceResponse, NodePerformanceSpecific,
|
||||
NetworkMonitorsPagedResponse, NodeId, NodeMeasurements, NodeMeasurementsPerKindResponse,
|
||||
NodePerformance, NodePerformancePagedResponse, NodePerformanceResponse, NodeResults,
|
||||
NymPerformanceContractError, RetiredNetworkMonitorsPagedResponse,
|
||||
};
|
||||
|
||||
@@ -23,7 +23,6 @@ pub fn query_admin(deps: Deps) -> Result<AdminResponse, NymPerformanceContractEr
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
// TODO dz return measurement_kind in response
|
||||
pub fn query_node_performance(
|
||||
deps: Deps,
|
||||
epoch_id: EpochId,
|
||||
@@ -34,44 +33,28 @@ pub fn query_node_performance(
|
||||
Ok(NodePerformanceResponse { performance })
|
||||
}
|
||||
|
||||
pub fn query_node_measurements(
|
||||
deps: Deps,
|
||||
epoch_id: EpochId,
|
||||
node_id: NodeId,
|
||||
) -> Result<NodeMeasurementsResponse, NymPerformanceContractError> {
|
||||
let measurements = NYM_PERFORMANCE_CONTRACT_STORAGE
|
||||
.performance_results
|
||||
.results
|
||||
.may_load(deps.storage, (epoch_id, node_id))?;
|
||||
Ok(NodeMeasurementsResponse { measurements })
|
||||
}
|
||||
|
||||
// TODO dz unit test
|
||||
pub fn query_node_measurements_specific(
|
||||
pub fn query_node_measurements_for_kind(
|
||||
deps: Deps,
|
||||
epoch_id: EpochId,
|
||||
node_id: NodeId,
|
||||
measurement_kind: String,
|
||||
) -> Result<NodeMeasurementsResponse, NymPerformanceContractError> {
|
||||
let measurements = NYM_PERFORMANCE_CONTRACT_STORAGE
|
||||
.performance_results
|
||||
.results_specific;
|
||||
) -> Result<NodeMeasurementsPerKindResponse, NymPerformanceContractError> {
|
||||
let measurements = NYM_PERFORMANCE_CONTRACT_STORAGE.try_load_measurement_kind(
|
||||
deps.storage,
|
||||
epoch_id,
|
||||
node_id,
|
||||
measurement_kind,
|
||||
)?;
|
||||
|
||||
let key = (epoch_id, node_id, measurement_kind);
|
||||
let measurements = measurements.may_load(deps.storage, key)?;
|
||||
|
||||
Ok(NodeMeasurementsResponse { measurements })
|
||||
Ok(NodeMeasurementsPerKindResponse { measurements })
|
||||
}
|
||||
|
||||
// TODO dz unit test
|
||||
pub fn query_all_node_measurements(
|
||||
deps: Deps,
|
||||
epoch_id: EpochId,
|
||||
node_id: NodeId,
|
||||
) -> Result<AllNodeMeasurementsResponse, NymPerformanceContractError> {
|
||||
let measurements = NYM_PERFORMANCE_CONTRACT_STORAGE
|
||||
.performance_results
|
||||
.results_specific;
|
||||
let measurements = NYM_PERFORMANCE_CONTRACT_STORAGE.performance_results.results;
|
||||
|
||||
// retrieve a list of currently defined measurements, only return results for those
|
||||
// (storage may contain measurements that have since been deleted by admin -
|
||||
@@ -146,7 +129,6 @@ pub fn query_node_performance_paged(
|
||||
})
|
||||
}
|
||||
|
||||
// TODO dz unit test changes
|
||||
pub fn query_epoch_performance_paged(
|
||||
deps: Deps,
|
||||
epoch_id: EpochId,
|
||||
@@ -161,18 +143,16 @@ pub fn query_epoch_performance_paged(
|
||||
|
||||
let performance = NYM_PERFORMANCE_CONTRACT_STORAGE
|
||||
.performance_results
|
||||
.results_specific
|
||||
.results
|
||||
.sub_prefix(epoch_id)
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|record| {
|
||||
record.map(
|
||||
|((node_id, measurement_kind), results)| NodePerformanceSpecific {
|
||||
node_id,
|
||||
performance: results.median(),
|
||||
measurement_kind,
|
||||
},
|
||||
)
|
||||
record.map(|((node_id, measurement_kind), results)| NodePerformance {
|
||||
node_id,
|
||||
performance: results.median(),
|
||||
measurement_kind,
|
||||
})
|
||||
})
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
@@ -195,27 +175,51 @@ pub fn query_epoch_measurements_paged(
|
||||
.unwrap_or(retrieval_limits::NODE_EPOCH_MEASUREMENTS_DEFAULT_LIMIT)
|
||||
.min(retrieval_limits::NODE_EPOCH_MEASUREMENTS_MAX_LIMIT) as usize;
|
||||
|
||||
let start = start_after.map(Bound::exclusive);
|
||||
let start = start_after.map(|node_id| Bound::exclusive((node_id + 1, String::new())));
|
||||
|
||||
// because API aggregates per NodeId, and the storage doesn't, we have to
|
||||
// first collect all different measurements for a node and use an
|
||||
// intermediary struct to map from storage to the object returned on the API
|
||||
let mut measurements_per_node: HashMap<NodeId, Vec<(MeasurementKind, NodeResults)>> =
|
||||
HashMap::new();
|
||||
let measurements = NYM_PERFORMANCE_CONTRACT_STORAGE
|
||||
.performance_results
|
||||
.results
|
||||
.prefix(epoch_id)
|
||||
.sub_prefix(epoch_id)
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|record| {
|
||||
record.map(|(node_id, measurements)| NodeMeasurement {
|
||||
node_id,
|
||||
measurements,
|
||||
record.inspect(|((node_id, kind), measurements)| {
|
||||
measurements_per_node
|
||||
.entry(*node_id)
|
||||
.and_modify(|vec| vec.push((kind.to_string(), measurements.to_owned())))
|
||||
.or_insert_with(|| vec![(kind.to_string(), measurements.to_owned())]);
|
||||
})
|
||||
})
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
let start_next_after = measurements.last().map(|last| last.node_id);
|
||||
// transforming collected data into a returning type
|
||||
let mut returning = Vec::new();
|
||||
for (node_id, measurements_per_kind) in measurements_per_node.into_iter() {
|
||||
let mut measurements = HashMap::new();
|
||||
for (measurement_kind, results) in measurements_per_kind {
|
||||
measurements.insert(measurement_kind, results);
|
||||
}
|
||||
returning.push(NodeMeasurements {
|
||||
node_id,
|
||||
measurements_per_kind: measurements,
|
||||
});
|
||||
}
|
||||
|
||||
// storage keeps nodes in ascending order for pagination
|
||||
// intermediary hashmap doesn't have deterministic order so we need to order
|
||||
// explicitly here before returning
|
||||
returning.sort_by_key(|elem| elem.node_id);
|
||||
let start_next_after = measurements.last().map(|((last, _), _)| *last);
|
||||
|
||||
Ok(EpochMeasurementsPagedResponse {
|
||||
epoch_id,
|
||||
measurements,
|
||||
measurements: returning,
|
||||
start_next_after,
|
||||
})
|
||||
}
|
||||
@@ -229,7 +233,7 @@ pub fn query_full_historical_performance_paged(
|
||||
.unwrap_or(retrieval_limits::NODE_HISTORICAL_PERFORMANCE_DEFAULT_LIMIT)
|
||||
.min(retrieval_limits::NODE_HISTORICAL_PERFORMANCE_MAX_LIMIT) as usize;
|
||||
|
||||
let start = start_after.map(Bound::exclusive);
|
||||
let start = start_after.map(|(n, e)| Bound::exclusive((n, e, String::new())));
|
||||
|
||||
let performance = NYM_PERFORMANCE_CONTRACT_STORAGE
|
||||
.performance_results
|
||||
@@ -237,7 +241,8 @@ pub fn query_full_historical_performance_paged(
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|record| {
|
||||
record.map(|((epoch_id, node_id), results)| HistoricalPerformance {
|
||||
record.map(|((epoch_id, node_id, _), results)| HistoricalPerformance {
|
||||
// TODO dz map kind as well
|
||||
epoch_id,
|
||||
node_id,
|
||||
performance: results.median(),
|
||||
@@ -368,9 +373,11 @@ pub fn query_last_submission(deps: Deps) -> Result<LastSubmission, NymPerformanc
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::testing::{PerformanceContractTesterExt, init_contract_tester};
|
||||
use nym_contracts_common_testing::{ChainOpts, ContractOpts, RandExt};
|
||||
use nym_performance_contract_common::{LastSubmittedData, NodePerformanceSpecific};
|
||||
use crate::testing::{
|
||||
PerformanceContractTesterExt, epoch_node_performance_unchecked, init_contract_tester,
|
||||
};
|
||||
use nym_contracts_common_testing::{AdminExt, ChainOpts, ContractOpts, RandExt};
|
||||
use nym_performance_contract_common::{ExecuteMsg, LastSubmittedData, NodePerformance};
|
||||
|
||||
#[cfg(test)]
|
||||
mod admin_query {
|
||||
@@ -409,6 +416,160 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn querying_node_measurements_kind() -> anyhow::Result<()> {
|
||||
let mut test = init_contract_tester();
|
||||
|
||||
// test setup
|
||||
let nm1 = test.generate_account();
|
||||
let nm2 = test.generate_account();
|
||||
test.authorise_network_monitor(&nm1)?;
|
||||
test.authorise_network_monitor(&nm2)?;
|
||||
|
||||
let admin = test.admin_unchecked();
|
||||
let kind_mixnet = String::from("mixnet");
|
||||
let kind_wireguard = String::from("wireguard");
|
||||
test.execute_raw(
|
||||
admin.clone(),
|
||||
ExecuteMsg::DefineMeasurementKind {
|
||||
measurement_kind: kind_mixnet.clone(),
|
||||
},
|
||||
)?;
|
||||
test.execute_raw(
|
||||
admin,
|
||||
ExecuteMsg::DefineMeasurementKind {
|
||||
measurement_kind: kind_wireguard.clone(),
|
||||
},
|
||||
)?;
|
||||
|
||||
let node1 = test.bond_dummy_nymnode()?;
|
||||
let node2 = test.bond_dummy_nymnode()?;
|
||||
|
||||
let epoch_id = 10;
|
||||
test.set_mixnet_epoch(epoch_id)?;
|
||||
|
||||
let deps = test.deps();
|
||||
|
||||
// ===== Test: undefined measurement kind =====
|
||||
let undefined_kind = String::from("undefined");
|
||||
let res = query_node_measurements_for_kind(deps, epoch_id, node1, undefined_kind);
|
||||
assert!(res.is_err());
|
||||
assert!(matches!(
|
||||
res.unwrap_err(),
|
||||
NymPerformanceContractError::UnsupportedMeasurementKind { .. }
|
||||
));
|
||||
|
||||
// ===== Test: query returns None for defined kind with no data =====
|
||||
let res = query_node_measurements_for_kind(deps, epoch_id, node1, kind_mixnet.clone())?;
|
||||
assert!(res.measurements.is_none());
|
||||
|
||||
// ===== Test happy path: single measurement from one monitor =====
|
||||
test.insert_raw_performance(&nm1, node1, kind_mixnet.clone(), "0.5")?;
|
||||
let res =
|
||||
query_node_measurements_for_kind(test.deps(), epoch_id, node1, kind_mixnet.clone())?;
|
||||
let measurements = res.measurements.unwrap();
|
||||
assert_eq!(measurements.inner().len(), 1);
|
||||
assert_eq!(measurements.inner()[0], "0.5".parse()?);
|
||||
|
||||
// Verify against raw storage
|
||||
let expected = test.read_raw_scores(epoch_id, node1, kind_mixnet.clone())?;
|
||||
assert_eq!(measurements.inner(), expected.inner());
|
||||
|
||||
// ===== Test: multiple measurements from different monitors =====
|
||||
// each monitor can only submit once per (epoch, node) pair
|
||||
test.insert_raw_performance(&nm2, node1, kind_mixnet.clone(), "0.3")?;
|
||||
let res =
|
||||
query_node_measurements_for_kind(test.deps(), epoch_id, node1, kind_mixnet.clone())?;
|
||||
let measurements = res.measurements.unwrap();
|
||||
assert_eq!(measurements.inner().len(), 2);
|
||||
|
||||
// ===== Test: multiple measurement kinds are independent =====
|
||||
// we need a new epoch since monitors already submitted in this epoch
|
||||
let epoch_11 = 11;
|
||||
test.set_mixnet_epoch(epoch_11)?;
|
||||
|
||||
// now submit
|
||||
test.insert_raw_performance(&nm1, node1, kind_wireguard.clone(), "0.8")?;
|
||||
test.insert_raw_performance(&nm2, node1, kind_wireguard.clone(), "0.9")?;
|
||||
|
||||
// verify data for submitted kind is there
|
||||
let res_wireguard_e11 =
|
||||
query_node_measurements_for_kind(test.deps(), epoch_11, node1, kind_wireguard.clone())?;
|
||||
let wg_measurements = res_wireguard_e11.measurements.unwrap();
|
||||
assert_eq!(wg_measurements.inner().len(), 2);
|
||||
assert_eq!(wg_measurements.inner()[0], "0.8".parse()?);
|
||||
assert_eq!(wg_measurements.inner()[1], "0.9".parse()?);
|
||||
|
||||
// not submitted for this kind in this epoch: should have no data
|
||||
let res_mixnet_e11 =
|
||||
query_node_measurements_for_kind(test.deps(), epoch_11, node1, kind_mixnet.clone())?;
|
||||
assert!(res_mixnet_e11.measurements.is_none());
|
||||
|
||||
// however, mixnet kind should still have old data in previous epoch
|
||||
let res_mixnet_e10 =
|
||||
query_node_measurements_for_kind(test.deps(), epoch_id, node1, kind_mixnet.clone())?;
|
||||
let mixnet_measurements = res_mixnet_e10.measurements.unwrap();
|
||||
assert_eq!(mixnet_measurements.inner().len(), 2);
|
||||
assert_eq!(mixnet_measurements.inner()[0], "0.3".parse()?);
|
||||
assert_eq!(mixnet_measurements.inner()[1], "0.5".parse()?);
|
||||
|
||||
// ===== Test: different epochs are independent =====
|
||||
// advance epoch again & submit something
|
||||
let epoch_12 = 12;
|
||||
test.set_mixnet_epoch(epoch_12)?;
|
||||
test.insert_raw_performance(&nm1, node1, kind_mixnet.clone(), "0.25")?;
|
||||
|
||||
// epoch 12 should have new data
|
||||
let res_epoch12 =
|
||||
query_node_measurements_for_kind(test.deps(), epoch_12, node1, kind_mixnet.clone())?;
|
||||
assert!(res_epoch12.measurements.is_some());
|
||||
let epoch12_measurements = res_epoch12.measurements.unwrap();
|
||||
assert_eq!(epoch12_measurements.inner().len(), 1);
|
||||
assert_eq!(epoch12_measurements.inner()[0], "0.25".parse()?);
|
||||
|
||||
// epoch 10 should still have old data
|
||||
let res_epoch10 =
|
||||
query_node_measurements_for_kind(test.deps(), epoch_id, node1, kind_mixnet.clone())?;
|
||||
assert_eq!(res_epoch10.measurements.unwrap().inner().len(), 2);
|
||||
|
||||
// epoch 11 with mixnet (no data) should return None
|
||||
let res_epoch11_mixnet =
|
||||
query_node_measurements_for_kind(test.deps(), epoch_11, node1, kind_mixnet.clone())?;
|
||||
assert!(res_epoch11_mixnet.measurements.is_none());
|
||||
|
||||
// ===== Test: different nodes are independent =====
|
||||
// nm1 can now submit for node2 in epoch 12 since node2 > node1
|
||||
test.insert_raw_performance(&nm1, node2, kind_mixnet.clone(), "0.42")?;
|
||||
|
||||
// Query node1 in epoch 12 - should have nm1's data
|
||||
let res_node1_e12 =
|
||||
query_node_measurements_for_kind(test.deps(), epoch_12, node1, kind_mixnet.clone())?;
|
||||
assert_eq!(res_node1_e12.measurements.unwrap().inner().len(), 1);
|
||||
|
||||
// Query node2 in epoch 12 - should have different data
|
||||
let res_node2_e12 =
|
||||
query_node_measurements_for_kind(test.deps(), epoch_12, node2, kind_mixnet.clone())?;
|
||||
let node2_measurements = res_node2_e12.measurements.unwrap();
|
||||
assert_eq!(node2_measurements.inner().len(), 1);
|
||||
assert_eq!(node2_measurements.inner()[0], "0.42".parse()?);
|
||||
|
||||
// Query node2 in epoch 10 (no data) - should return None
|
||||
let res_node2_e10 =
|
||||
query_node_measurements_for_kind(test.deps(), epoch_id, node2, kind_mixnet.clone())?;
|
||||
assert!(res_node2_e10.measurements.is_none());
|
||||
|
||||
// verify against raw data
|
||||
let raw_scores = test.read_raw_scores(epoch_id, node1, kind_mixnet.clone())?;
|
||||
let query_result =
|
||||
query_node_measurements_for_kind(test.deps(), epoch_id, node1, kind_mixnet.clone())?;
|
||||
assert_eq!(
|
||||
query_result.measurements.unwrap().inner(),
|
||||
raw_scores.inner()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn querying_node_performance_paged() -> anyhow::Result<()> {
|
||||
let mut test = init_contract_tester();
|
||||
@@ -454,10 +615,11 @@ mod tests {
|
||||
assert!(res.start_next_after.is_none());
|
||||
assert_eq!(
|
||||
res.performance,
|
||||
vec![EpochNodePerformance {
|
||||
epoch: 5,
|
||||
performance: Some("0.5".parse()?),
|
||||
}]
|
||||
vec![epoch_node_performance_unchecked(
|
||||
5,
|
||||
measurement_kind.clone(),
|
||||
"0.5"
|
||||
)]
|
||||
);
|
||||
|
||||
let res = query_node_performance_paged(deps, node_id, Some(2), None)?;
|
||||
@@ -465,18 +627,9 @@ mod tests {
|
||||
assert_eq!(
|
||||
res.performance,
|
||||
vec![
|
||||
EpochNodePerformance {
|
||||
epoch: 3,
|
||||
performance: Some("0.3".parse()?),
|
||||
},
|
||||
EpochNodePerformance {
|
||||
epoch: 4,
|
||||
performance: Some("0.4".parse()?),
|
||||
},
|
||||
EpochNodePerformance {
|
||||
epoch: 5,
|
||||
performance: Some("0.5".parse()?),
|
||||
}
|
||||
epoch_node_performance_unchecked(3, measurement_kind.clone(), "0.3"),
|
||||
epoch_node_performance_unchecked(4, measurement_kind.clone(), "0.4"),
|
||||
epoch_node_performance_unchecked(5, measurement_kind.clone(), "0.5"),
|
||||
]
|
||||
);
|
||||
|
||||
@@ -485,30 +638,12 @@ mod tests {
|
||||
assert_eq!(
|
||||
res.performance,
|
||||
vec![
|
||||
EpochNodePerformance {
|
||||
epoch: 0,
|
||||
performance: Some("0".parse()?),
|
||||
},
|
||||
EpochNodePerformance {
|
||||
epoch: 1,
|
||||
performance: Some("0.1".parse()?),
|
||||
},
|
||||
EpochNodePerformance {
|
||||
epoch: 2,
|
||||
performance: Some("0.2".parse()?),
|
||||
},
|
||||
EpochNodePerformance {
|
||||
epoch: 3,
|
||||
performance: Some("0.3".parse()?),
|
||||
},
|
||||
EpochNodePerformance {
|
||||
epoch: 4,
|
||||
performance: Some("0.4".parse()?),
|
||||
},
|
||||
EpochNodePerformance {
|
||||
epoch: 5,
|
||||
performance: Some("0.5".parse()?),
|
||||
}
|
||||
epoch_node_performance_unchecked(0, measurement_kind.clone(), "0"),
|
||||
epoch_node_performance_unchecked(1, measurement_kind.clone(), "0.1"),
|
||||
epoch_node_performance_unchecked(2, measurement_kind.clone(), "0.2"),
|
||||
epoch_node_performance_unchecked(3, measurement_kind.clone(), "0.3"),
|
||||
epoch_node_performance_unchecked(4, measurement_kind.clone(), "0.4"),
|
||||
epoch_node_performance_unchecked(5, measurement_kind.clone(), "0.5"),
|
||||
]
|
||||
);
|
||||
|
||||
@@ -516,10 +651,11 @@ mod tests {
|
||||
assert_eq!(res.start_next_after, Some(3));
|
||||
assert_eq!(
|
||||
res.performance,
|
||||
vec![EpochNodePerformance {
|
||||
epoch: 3,
|
||||
performance: Some("0.3".parse()?),
|
||||
}]
|
||||
vec![epoch_node_performance_unchecked(
|
||||
3,
|
||||
measurement_kind.clone(),
|
||||
"0.3"
|
||||
)]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -562,12 +698,12 @@ mod tests {
|
||||
assert_eq!(
|
||||
res.performance,
|
||||
vec![
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[5],
|
||||
performance: "0.5".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[6],
|
||||
performance: "0.6".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -579,12 +715,12 @@ mod tests {
|
||||
assert_eq!(
|
||||
res.performance,
|
||||
vec![
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[5],
|
||||
performance: "0.5".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[6],
|
||||
performance: "0.6".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -597,17 +733,17 @@ mod tests {
|
||||
assert_eq!(
|
||||
res.performance,
|
||||
vec![
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[3],
|
||||
performance: "0.3".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[5],
|
||||
performance: "0.5".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[6],
|
||||
performance: "0.6".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -620,27 +756,27 @@ mod tests {
|
||||
assert_eq!(
|
||||
res.performance,
|
||||
vec![
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[1],
|
||||
performance: "0.1".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[2],
|
||||
performance: "0.2".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[3],
|
||||
performance: "0.3".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[5],
|
||||
performance: "0.5".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[6],
|
||||
performance: "0.6".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -652,7 +788,7 @@ mod tests {
|
||||
assert_eq!(res.start_next_after, Some(nodes[3]));
|
||||
assert_eq!(
|
||||
res.performance,
|
||||
vec![NodePerformanceSpecific {
|
||||
vec![NodePerformance {
|
||||
node_id: nodes[3],
|
||||
performance: "0.3".parse()?,
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -662,6 +798,270 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn querying_epoch_measurements_paged() -> anyhow::Result<()> {
|
||||
let mut test = init_contract_tester();
|
||||
|
||||
let nm = test.generate_account();
|
||||
test.authorise_network_monitor(&nm)?;
|
||||
let measurement_kind = test.define_dummy_measurement_kind().unwrap();
|
||||
|
||||
let mut nodes = Vec::new();
|
||||
for _ in 0..10 {
|
||||
nodes.push(test.bond_dummy_nymnode()?);
|
||||
}
|
||||
|
||||
let epoch_id = 5;
|
||||
test.set_mixnet_epoch(epoch_id)?;
|
||||
|
||||
test.insert_raw_performance(&nm, nodes[1], measurement_kind.clone(), "0.1")?;
|
||||
test.insert_raw_performance(&nm, nodes[2], measurement_kind.clone(), "0.2")?;
|
||||
test.insert_raw_performance(&nm, nodes[3], measurement_kind.clone(), "0.3")?;
|
||||
// 4 is missing
|
||||
test.insert_raw_performance(&nm, nodes[5], measurement_kind.clone(), "0.5")?;
|
||||
test.insert_raw_performance(&nm, nodes[6], measurement_kind.clone(), "0.6")?;
|
||||
|
||||
let deps = test.deps();
|
||||
|
||||
// query starting after nodes[6]
|
||||
let res = query_epoch_measurements_paged(deps, epoch_id, Some(nodes[6]), None)?;
|
||||
assert!(res.start_next_after.is_none());
|
||||
assert!(res.measurements.is_empty());
|
||||
|
||||
// query after non-existent high node ID
|
||||
let res = query_epoch_measurements_paged(deps, epoch_id, Some(42), None)?;
|
||||
assert!(res.start_next_after.is_none());
|
||||
assert!(res.measurements.is_empty());
|
||||
|
||||
// query starting after nodes[4] (should return nodes 5 and 6)
|
||||
let res = query_epoch_measurements_paged(deps, epoch_id, Some(nodes[4]), None)?;
|
||||
assert_eq!(res.start_next_after, Some(nodes[6]));
|
||||
assert_eq!(res.measurements.len(), 2);
|
||||
|
||||
assert_eq!(res.measurements[0].node_id, nodes[5]);
|
||||
assert_eq!(res.measurements[1].node_id, nodes[6]);
|
||||
|
||||
// verify returned data against raw results
|
||||
let node5_results = res.measurements[0]
|
||||
.measurements_per_kind
|
||||
.get(&measurement_kind)
|
||||
.unwrap();
|
||||
let expected_results =
|
||||
test.read_raw_scores(epoch_id, nodes[5], measurement_kind.clone())?;
|
||||
assert_eq!(node5_results.inner(), expected_results.inner());
|
||||
|
||||
let node6_results = res.measurements[1]
|
||||
.measurements_per_kind
|
||||
.get(&measurement_kind)
|
||||
.unwrap();
|
||||
let expected_results =
|
||||
test.read_raw_scores(epoch_id, nodes[6], measurement_kind.clone())?;
|
||||
assert_eq!(node6_results.inner(), expected_results.inner());
|
||||
|
||||
// query starting after nodes[3]
|
||||
// should skip nodes[3] entirely and start from nodes[5] (nodes[4] doesn't exist)
|
||||
let res = query_epoch_measurements_paged(deps, epoch_id, Some(nodes[3]), None)?;
|
||||
assert_eq!(res.start_next_after, Some(nodes[6]));
|
||||
// Verify only nodes[5] and nodes[6] are present (nodes[3] is skipped)
|
||||
assert_eq!(res.measurements.len(), 2);
|
||||
assert_eq!(res.measurements[0].node_id, nodes[5]);
|
||||
assert_eq!(res.measurements[1].node_id, nodes[6]);
|
||||
|
||||
// query with start_after = nodes[2]
|
||||
let res = query_epoch_measurements_paged(deps, epoch_id, Some(nodes[2]), None)?;
|
||||
assert_eq!(res.start_next_after, Some(nodes[6]));
|
||||
assert_eq!(res.measurements.len(), 3);
|
||||
// only nodes[3], nodes[5], nodes[6] are present (nodes[2] is skipped)
|
||||
assert_eq!(res.measurements[0].node_id, nodes[3]);
|
||||
assert_eq!(res.measurements[1].node_id, nodes[5]);
|
||||
assert_eq!(res.measurements[2].node_id, nodes[6]);
|
||||
|
||||
// measurements HashMap structure for all nodes
|
||||
for measurement in &res.measurements {
|
||||
assert!(
|
||||
measurement
|
||||
.measurements_per_kind
|
||||
.contains_key(&measurement_kind)
|
||||
);
|
||||
}
|
||||
|
||||
// query from beginning (no start_after) - should return all nodes
|
||||
let res = query_epoch_measurements_paged(deps, epoch_id, None, None)?;
|
||||
assert_eq!(res.start_next_after, Some(nodes[6]));
|
||||
assert_eq!(res.measurements.len(), 5);
|
||||
// verify all expected nodes are present IN SORTED ORDER
|
||||
assert_eq!(res.measurements[0].node_id, nodes[1]);
|
||||
assert_eq!(res.measurements[1].node_id, nodes[2]);
|
||||
assert_eq!(res.measurements[2].node_id, nodes[3]);
|
||||
assert_eq!(res.measurements[3].node_id, nodes[5]);
|
||||
assert_eq!(res.measurements[4].node_id, nodes[6]);
|
||||
|
||||
// query with custom limit
|
||||
// With limit=1, we fetch 1 storage item starting from nodes[3] (nodes[2] + 1)
|
||||
let res = query_epoch_measurements_paged(deps, epoch_id, Some(nodes[2]), Some(1))?;
|
||||
assert_eq!(res.start_next_after, Some(nodes[3]));
|
||||
assert_eq!(res.measurements.len(), 1);
|
||||
assert_eq!(res.measurements[0].node_id, nodes[3]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn querying_epoch_measurements_paged_multiple_kinds() -> anyhow::Result<()> {
|
||||
let mut test = init_contract_tester();
|
||||
|
||||
// Use two different network monitors for different measurement kinds
|
||||
let nm1 = test.generate_account();
|
||||
let nm2 = test.generate_account();
|
||||
test.authorise_network_monitor(&nm1)?;
|
||||
test.authorise_network_monitor(&nm2)?;
|
||||
|
||||
// define two different measurement kinds
|
||||
let admin = test.admin_unchecked();
|
||||
let kind1 = String::from("mixnet");
|
||||
let kind2 = String::from("wireguard");
|
||||
|
||||
test.execute_raw(
|
||||
admin.clone(),
|
||||
ExecuteMsg::DefineMeasurementKind {
|
||||
measurement_kind: kind1.clone(),
|
||||
},
|
||||
)?;
|
||||
test.execute_raw(
|
||||
admin,
|
||||
ExecuteMsg::DefineMeasurementKind {
|
||||
measurement_kind: kind2.clone(),
|
||||
},
|
||||
)?;
|
||||
|
||||
// bond some nodes
|
||||
let node1 = test.bond_dummy_nymnode()?;
|
||||
let node2 = test.bond_dummy_nymnode()?;
|
||||
let node3 = test.bond_dummy_nymnode()?;
|
||||
|
||||
let epoch_id = 10;
|
||||
test.set_mixnet_epoch(epoch_id)?;
|
||||
|
||||
// both measurement kinds (different network monitors)
|
||||
test.insert_raw_performance(&nm1, node1, kind1.clone(), "0.11")?;
|
||||
test.insert_raw_performance(&nm2, node1, kind2.clone(), "0.12")?;
|
||||
|
||||
// only first kind
|
||||
test.insert_raw_performance(&nm1, node2, kind1.clone(), "0.21")?;
|
||||
|
||||
// both kinds (different network monitors)
|
||||
test.insert_raw_performance(&nm1, node3, kind1.clone(), "0.31")?;
|
||||
test.insert_raw_performance(&nm2, node3, kind2.clone(), "0.32")?;
|
||||
|
||||
let deps = test.deps();
|
||||
|
||||
// query all measurements for this epoch
|
||||
let res = query_epoch_measurements_paged(deps, epoch_id, None, None)?;
|
||||
assert_eq!(res.epoch_id, epoch_id);
|
||||
assert_eq!(res.measurements.len(), 3);
|
||||
|
||||
assert_eq!(res.measurements[0].node_id, node1);
|
||||
assert_eq!(res.measurements[1].node_id, node2);
|
||||
assert_eq!(res.measurements[2].node_id, node3);
|
||||
|
||||
let node1_measurements = &res.measurements[0];
|
||||
let node2_measurements = &res.measurements[1];
|
||||
let node3_measurements = &res.measurements[2];
|
||||
|
||||
// verify node 1 has 2 measurement kinds
|
||||
assert_eq!(node1_measurements.measurements_per_kind.len(), 2);
|
||||
assert!(
|
||||
node1_measurements
|
||||
.measurements_per_kind
|
||||
.contains_key(&kind1)
|
||||
);
|
||||
assert!(
|
||||
node1_measurements
|
||||
.measurements_per_kind
|
||||
.contains_key(&kind2)
|
||||
);
|
||||
|
||||
// raw data for node 1
|
||||
let node1_kind1_results = node1_measurements
|
||||
.measurements_per_kind
|
||||
.get(&kind1)
|
||||
.unwrap();
|
||||
let expected = test.read_raw_scores(epoch_id, node1, kind1.clone())?;
|
||||
assert_eq!(node1_kind1_results.inner(), expected.inner());
|
||||
|
||||
let node1_kind2_results = node1_measurements
|
||||
.measurements_per_kind
|
||||
.get(&kind2)
|
||||
.unwrap();
|
||||
let expected = test.read_raw_scores(epoch_id, node1, kind2.clone())?;
|
||||
assert_eq!(node1_kind2_results.inner(), expected.inner());
|
||||
|
||||
// node 2 has only 1 measurement kind
|
||||
assert_eq!(node2_measurements.measurements_per_kind.len(), 1);
|
||||
assert!(
|
||||
node2_measurements
|
||||
.measurements_per_kind
|
||||
.contains_key(&kind1)
|
||||
);
|
||||
assert!(
|
||||
!node2_measurements
|
||||
.measurements_per_kind
|
||||
.contains_key(&kind2)
|
||||
);
|
||||
|
||||
// raw data for node 2
|
||||
let node2_kind1_results = node2_measurements
|
||||
.measurements_per_kind
|
||||
.get(&kind1)
|
||||
.unwrap();
|
||||
let expected = test.read_raw_scores(epoch_id, node2, kind1.clone())?;
|
||||
assert_eq!(node2_kind1_results.inner(), expected.inner());
|
||||
|
||||
// node 3 has 2 measurement kinds
|
||||
assert_eq!(node3_measurements.measurements_per_kind.len(), 2);
|
||||
assert!(
|
||||
node3_measurements
|
||||
.measurements_per_kind
|
||||
.contains_key(&kind1)
|
||||
);
|
||||
assert!(
|
||||
node3_measurements
|
||||
.measurements_per_kind
|
||||
.contains_key(&kind2)
|
||||
);
|
||||
|
||||
// raw data for node 3
|
||||
let node3_kind1_results = node3_measurements
|
||||
.measurements_per_kind
|
||||
.get(&kind1)
|
||||
.unwrap();
|
||||
let expected = test.read_raw_scores(epoch_id, node3, kind1.clone())?;
|
||||
assert_eq!(node3_kind1_results.inner(), expected.inner());
|
||||
|
||||
let node3_kind2_results = node3_measurements
|
||||
.measurements_per_kind
|
||||
.get(&kind2)
|
||||
.unwrap();
|
||||
let expected = test.read_raw_scores(epoch_id, node3, kind2.clone())?;
|
||||
assert_eq!(node3_kind2_results.inner(), expected.inner());
|
||||
|
||||
// pagination with multiple kinds - query after node1
|
||||
let res = query_epoch_measurements_paged(deps, epoch_id, Some(node1), None)?;
|
||||
assert_eq!(res.measurements.len(), 2); // node2 and node3 only (node1 is skipped)
|
||||
assert_eq!(res.measurements[0].node_id, node2);
|
||||
assert_eq!(res.measurements[1].node_id, node3);
|
||||
assert_eq!(res.measurements[0].measurements_per_kind.len(), 1); // only latency
|
||||
assert_eq!(res.measurements[1].measurements_per_kind.len(), 2); // both kinds
|
||||
|
||||
// pagination after node2 - should only return node3
|
||||
let res = query_epoch_measurements_paged(deps, epoch_id, Some(node2), None)?;
|
||||
assert_eq!(res.measurements.len(), 1); // only node3
|
||||
assert_eq!(res.measurements[0].node_id, node3);
|
||||
assert_eq!(res.measurements[0].measurements_per_kind.len(), 2); // both kinds
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn last_submission_query() -> anyhow::Result<()> {
|
||||
let mut test = init_contract_tester();
|
||||
@@ -700,7 +1100,7 @@ mod tests {
|
||||
data: Some(LastSubmittedData {
|
||||
sender: nm1.clone(),
|
||||
epoch_id: 10,
|
||||
data: NodePerformanceSpecific {
|
||||
data: NodePerformance {
|
||||
node_id: id1,
|
||||
performance: "0.2".parse()?,
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -724,7 +1124,7 @@ mod tests {
|
||||
data: Some(LastSubmittedData {
|
||||
sender: nm2.clone(),
|
||||
epoch_id: 5,
|
||||
data: NodePerformanceSpecific {
|
||||
data: NodePerformance {
|
||||
node_id: id2,
|
||||
performance: "0.3".parse()?,
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -735,4 +1135,103 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
// TODO uncomment test:
|
||||
// currently logic for stale submission doesn't work well with different measurement kinds
|
||||
fn last_submission_query_multiple_kinds() -> anyhow::Result<()> {
|
||||
let mut test = init_contract_tester();
|
||||
let env = test.env();
|
||||
|
||||
// Bond one node and authorize one monitor
|
||||
let id1 = test.bond_dummy_nymnode()?;
|
||||
let nm1 = test.generate_account();
|
||||
test.authorise_network_monitor(&nm1)?;
|
||||
test.set_mixnet_epoch(10)?;
|
||||
|
||||
// Define TWO measurement kinds
|
||||
let measurement_mixnet = MeasurementKind::from("mixnet");
|
||||
let measurement_dvpn = MeasurementKind::from("dvpn");
|
||||
let admin = test.admin_unchecked();
|
||||
test.execute_raw(
|
||||
admin.clone(),
|
||||
ExecuteMsg::DefineMeasurementKind {
|
||||
measurement_kind: measurement_dvpn.clone(),
|
||||
},
|
||||
)?;
|
||||
test.execute_raw(
|
||||
admin,
|
||||
ExecuteMsg::DefineMeasurementKind {
|
||||
measurement_kind: measurement_mixnet.clone(),
|
||||
},
|
||||
)?;
|
||||
|
||||
// no submissions yet
|
||||
let data = query_last_submission(test.deps())?;
|
||||
assert_eq!(
|
||||
data,
|
||||
LastSubmission {
|
||||
block_height: env.block.height,
|
||||
block_time: env.block.time,
|
||||
data: None,
|
||||
}
|
||||
);
|
||||
|
||||
// Submit first measurement kind in epoch 10
|
||||
test.insert_raw_performance(&nm1, id1, measurement_dvpn.clone(), "0.75")?;
|
||||
|
||||
let data = query_last_submission(test.deps())?;
|
||||
assert_eq!(
|
||||
data,
|
||||
LastSubmission {
|
||||
block_height: env.block.height,
|
||||
block_time: env.block.time,
|
||||
data: Some(LastSubmittedData {
|
||||
sender: nm1.clone(),
|
||||
epoch_id: 10,
|
||||
data: NodePerformance {
|
||||
node_id: id1,
|
||||
performance: "0.75".parse()?,
|
||||
measurement_kind: measurement_dvpn.clone(),
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
let env = test.env();
|
||||
|
||||
// submit second measurement kind: same monitor, same node, same epoch
|
||||
test.insert_raw_performance(&nm1, id1, measurement_mixnet.clone(), "0.85")?;
|
||||
|
||||
// verify that last submission is updated with the new measurement kind
|
||||
let data = query_last_submission(test.deps())?;
|
||||
assert_eq!(
|
||||
data,
|
||||
LastSubmission {
|
||||
block_height: env.block.height,
|
||||
block_time: env.block.time,
|
||||
data: Some(LastSubmittedData {
|
||||
sender: nm1.clone(),
|
||||
epoch_id: 10,
|
||||
data: NodePerformance {
|
||||
node_id: id1,
|
||||
performance: "0.85".parse()?,
|
||||
measurement_kind: measurement_mixnet.clone(),
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
// verify both measurements are stored independently in the same epoch
|
||||
let bandwidth_results = test.read_raw_scores(10, id1, measurement_dvpn.clone())?;
|
||||
assert_eq!(bandwidth_results.inner().len(), 1);
|
||||
assert_eq!(bandwidth_results.inner()[0], "0.75".parse()?);
|
||||
|
||||
let latency_results = test.read_raw_scores(10, id1, measurement_mixnet.clone())?;
|
||||
assert_eq!(latency_results.inner().len(), 1);
|
||||
assert_eq!(latency_results.inner()[0], "0.85".parse()?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::helpers::MixnetContractQuerier;
|
||||
use cosmwasm_std::{Addr, Deps, DepsMut, Env, StdError, Storage};
|
||||
use cw_controllers::Admin;
|
||||
@@ -9,7 +11,7 @@ use nym_contracts_common::Percent;
|
||||
use nym_performance_contract_common::constants::storage_keys;
|
||||
use nym_performance_contract_common::{
|
||||
BatchSubmissionResult, EpochId, LastSubmission, LastSubmittedData, NetworkMonitorDetails,
|
||||
NetworkMonitorSubmissionMetadata, NodeId, NodePerformanceSpecific, NodeResults,
|
||||
NetworkMonitorSubmissionMetadata, NodeId, NodePerformance, NodeResults,
|
||||
NymPerformanceContractError, RemoveEpochMeasurementsResponse, RetiredNetworkMonitor,
|
||||
};
|
||||
|
||||
@@ -115,7 +117,7 @@ impl NymPerformanceContractStorage {
|
||||
env: Env,
|
||||
sender: &Addr,
|
||||
epoch_id: EpochId,
|
||||
data: NodePerformanceSpecific,
|
||||
data: NodePerformance,
|
||||
) -> Result<(), NymPerformanceContractError> {
|
||||
// 1. check if the sender is authorised to submit performance data
|
||||
self.network_monitors
|
||||
@@ -138,7 +140,7 @@ impl NymPerformanceContractStorage {
|
||||
|
||||
// 4 insert performance data into the storage
|
||||
self.performance_results
|
||||
.insert_performance_data_specific(deps.storage, epoch_id, &data)?;
|
||||
.insert_performance_data(deps.storage, epoch_id, &data)?;
|
||||
|
||||
// 5. update submission metadata based on the last result we submitted
|
||||
self.performance_results.update_submission_metadata(
|
||||
@@ -171,7 +173,7 @@ impl NymPerformanceContractStorage {
|
||||
env: Env,
|
||||
sender: &Addr,
|
||||
epoch_id: EpochId,
|
||||
data: Vec<NodePerformanceSpecific>,
|
||||
data: Vec<NodePerformance>,
|
||||
) -> Result<BatchSubmissionResult, NymPerformanceContractError> {
|
||||
// 1. check if the sender is authorised to submit performance data
|
||||
self.network_monitors
|
||||
@@ -196,11 +198,10 @@ impl NymPerformanceContractStorage {
|
||||
|
||||
// 3. submit it
|
||||
if self.node_bonded(deps.as_ref(), first.node_id)? {
|
||||
match self.performance_results.insert_performance_data_specific(
|
||||
deps.storage,
|
||||
epoch_id,
|
||||
first,
|
||||
) {
|
||||
match self
|
||||
.performance_results
|
||||
.insert_performance_data(deps.storage, epoch_id, first)
|
||||
{
|
||||
Ok(_) => accepted_scores += 1,
|
||||
Err(NymPerformanceContractError::UnsupportedMeasurementKind { kind }) => {
|
||||
non_existent_measurement_kind.push(kind);
|
||||
@@ -226,11 +227,8 @@ impl NymPerformanceContractStorage {
|
||||
|
||||
// 5. insert performance data into the storage
|
||||
if self.node_bonded(deps.as_ref(), perf.node_id)? {
|
||||
self.performance_results.insert_performance_data_specific(
|
||||
deps.storage,
|
||||
epoch_id,
|
||||
perf,
|
||||
)?;
|
||||
self.performance_results
|
||||
.insert_performance_data(deps.storage, epoch_id, perf)?;
|
||||
accepted_scores += 1;
|
||||
} else {
|
||||
non_existent_nodes.push(perf.node_id);
|
||||
@@ -332,45 +330,51 @@ impl NymPerformanceContractStorage {
|
||||
.retire(deps, &env, sender, &network_monitor)
|
||||
}
|
||||
|
||||
// TODO dz needs to support measurement_kind
|
||||
pub fn try_load_measurement_kind(
|
||||
&self,
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
node_id: NodeId,
|
||||
measurement_kind: MeasurementKind,
|
||||
) -> Result<Option<NodeResults>, NymPerformanceContractError> {
|
||||
self.performance_results
|
||||
.assert_measurement_defined(storage, measurement_kind.clone())?;
|
||||
|
||||
let key = (epoch_id, node_id, measurement_kind);
|
||||
self.performance_results
|
||||
.results
|
||||
.may_load(storage, key)
|
||||
.map_err(From::from)
|
||||
}
|
||||
|
||||
pub fn try_load_performance(
|
||||
&self,
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
node_id: NodeId,
|
||||
) -> Result<Option<Percent>, NymPerformanceContractError> {
|
||||
) -> Result<HashMap<String, Percent>, NymPerformanceContractError> {
|
||||
// Get all defined measurement kinds
|
||||
let measurement_kinds = self.performance_results.defined_measurements(storage)?;
|
||||
|
||||
// short-circuit if no measurement kinds are defined
|
||||
if measurement_kinds.is_empty() {
|
||||
return Ok(None);
|
||||
return Ok(HashMap::new());
|
||||
}
|
||||
|
||||
// Collect median values from all measurement kinds
|
||||
let mut medians = Vec::new();
|
||||
let mut performance_per_kind: HashMap<String, Percent> = HashMap::new();
|
||||
|
||||
// collect median values per measurement kind
|
||||
for kind in measurement_kinds {
|
||||
if let Some(results) = self
|
||||
.performance_results
|
||||
.results_specific
|
||||
.may_load(storage, (epoch_id, node_id, kind))?
|
||||
.results
|
||||
.may_load(storage, (epoch_id, node_id, kind.clone()))?
|
||||
{
|
||||
medians.push(results.median());
|
||||
performance_per_kind.insert(kind, results.median());
|
||||
}
|
||||
}
|
||||
|
||||
// If no data found for any measurement kind, return None
|
||||
if medians.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Build NodeResults from collected medians and return the median of medians
|
||||
let mut aggregated = NodeResults::new(medians[0]);
|
||||
for &median in &medians[1..] {
|
||||
aggregated.insert_new(median);
|
||||
}
|
||||
|
||||
Ok(Some(aggregated.median()))
|
||||
Ok(performance_per_kind)
|
||||
}
|
||||
|
||||
pub fn remove_node_measurements(
|
||||
@@ -388,7 +392,7 @@ impl NymPerformanceContractStorage {
|
||||
.defined_measurements(deps.storage)?;
|
||||
for kind in measurement_kinds {
|
||||
self.performance_results
|
||||
.results_specific
|
||||
.results
|
||||
.remove(deps.storage, (epoch_id, node_id, kind));
|
||||
}
|
||||
|
||||
@@ -404,18 +408,15 @@ impl NymPerformanceContractStorage {
|
||||
self.ensure_is_admin(deps.as_ref(), sender)?;
|
||||
|
||||
// 1. purge the entries according to the limit
|
||||
self.performance_results
|
||||
.results_specific
|
||||
.sub_prefix(epoch_id)
|
||||
.clear(
|
||||
deps.storage,
|
||||
Some(retrieval_limits::EPOCH_PERFORMANCE_PURGE_LIMIT),
|
||||
);
|
||||
self.performance_results.results.sub_prefix(epoch_id).clear(
|
||||
deps.storage,
|
||||
Some(retrieval_limits::EPOCH_PERFORMANCE_PURGE_LIMIT),
|
||||
);
|
||||
|
||||
// 2. see if there's anything left
|
||||
let additional_entries_to_remove_remaining = !self
|
||||
.performance_results
|
||||
.results_specific
|
||||
.results
|
||||
.sub_prefix(epoch_id)
|
||||
.is_empty(deps.storage);
|
||||
|
||||
@@ -509,9 +510,7 @@ impl NetworkMonitorsStorage {
|
||||
}
|
||||
|
||||
pub(crate) struct PerformanceResultsStorage {
|
||||
pub(crate) results: Map<(EpochId, NodeId), NodeResults>,
|
||||
|
||||
pub(crate) results_specific: Map<(EpochId, NodeId, MeasurementKind), NodeResults>,
|
||||
pub(crate) results: Map<(EpochId, NodeId, MeasurementKind), NodeResults>,
|
||||
|
||||
/// only measurements defined here can be submitted, as defined by contract admin
|
||||
defined_measurements: Map<MeasurementKind, ()>,
|
||||
@@ -529,8 +528,7 @@ impl PerformanceResultsStorage {
|
||||
#[allow(clippy::new_without_default)]
|
||||
const fn new() -> Self {
|
||||
PerformanceResultsStorage {
|
||||
results: Map::new(storage_keys::PERFORMANCE_RESULTS),
|
||||
results_specific: Map::new(storage_keys::PERFORMANCE_RESULTS_PER_KIND),
|
||||
results: Map::new(storage_keys::PERFORMANCE_RESULTS_PER_KIND),
|
||||
defined_measurements: Map::new(storage_keys::PERFORMANCE_DEFINED_KINDS),
|
||||
submission_metadata: Map::new(storage_keys::SUBMISSION_METADATA),
|
||||
}
|
||||
@@ -542,12 +540,14 @@ impl PerformanceResultsStorage {
|
||||
&self,
|
||||
storage: &mut dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
data: &NodePerformanceSpecific,
|
||||
data: &NodePerformance,
|
||||
) -> Result<(), NymPerformanceContractError> {
|
||||
self.assert_measurement_defined(storage, data.measurement_kind.clone())?;
|
||||
|
||||
let performance = data.performance;
|
||||
|
||||
let key = (epoch_id, data.node_id);
|
||||
let updated = match self.results.may_load(storage, key)? {
|
||||
let key = (epoch_id, data.node_id, data.measurement_kind.clone());
|
||||
let updated = match self.results.may_load(storage, key.clone())? {
|
||||
None => NodeResults::new(performance),
|
||||
Some(mut existing) => {
|
||||
existing.insert_new(performance);
|
||||
@@ -559,29 +559,6 @@ impl PerformanceResultsStorage {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_performance_data_specific(
|
||||
&self,
|
||||
storage: &mut dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
data: &NodePerformanceSpecific,
|
||||
) -> Result<(), NymPerformanceContractError> {
|
||||
self.assert_measurement_defined(storage, data.measurement_kind.clone())?;
|
||||
|
||||
let performance = data.performance;
|
||||
|
||||
let key = (epoch_id, data.node_id, data.measurement_kind.clone());
|
||||
let updated = match self.results_specific.may_load(storage, key.clone())? {
|
||||
None => NodeResults::new(performance),
|
||||
Some(mut existing) => {
|
||||
existing.insert_new(performance);
|
||||
existing
|
||||
}
|
||||
};
|
||||
|
||||
self.results_specific.save(storage, key, &updated)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn defined_measurements(
|
||||
&self,
|
||||
storage: &dyn Storage,
|
||||
@@ -594,7 +571,7 @@ impl PerformanceResultsStorage {
|
||||
|
||||
fn is_measurement_defined(
|
||||
&self,
|
||||
storage: &mut dyn Storage,
|
||||
storage: &dyn Storage,
|
||||
measurement_kind: MeasurementKind,
|
||||
) -> Result<bool, cosmwasm_std::StdError> {
|
||||
self.defined_measurements
|
||||
@@ -604,7 +581,7 @@ impl PerformanceResultsStorage {
|
||||
|
||||
fn assert_measurement_defined(
|
||||
&self,
|
||||
storage: &mut dyn Storage,
|
||||
storage: &dyn Storage,
|
||||
measurement_kind: MeasurementKind,
|
||||
) -> Result<(), NymPerformanceContractError> {
|
||||
if !self.is_measurement_defined(storage, measurement_kind.clone())? {
|
||||
@@ -670,6 +647,11 @@ impl PerformanceResultsStorage {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: this logic was written with ONE measurement kind in mind.
|
||||
// Whether different measurement kinds are submitted sequentially or not,
|
||||
// batched by node_id or not etc. is an open question. Until that is decided,
|
||||
// this implementation is flawed and needs to be FIXED.
|
||||
// There is a unit test (currently ignored) showing desired behaviour
|
||||
fn ensure_non_stale_submission(
|
||||
&self,
|
||||
storage: &dyn Storage,
|
||||
@@ -1015,12 +997,12 @@ mod tests {
|
||||
let id1 = tester.bond_dummy_nymnode()?;
|
||||
let id2 = tester.bond_dummy_nymnode()?;
|
||||
|
||||
let data = NodePerformanceSpecific {
|
||||
let data = NodePerformance {
|
||||
node_id: id1,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
};
|
||||
let another_data = NodePerformanceSpecific {
|
||||
let another_data = NodePerformance {
|
||||
node_id: id2,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind,
|
||||
@@ -1110,12 +1092,12 @@ mod tests {
|
||||
|
||||
let measurement_kind = tester.define_dummy_measurement_kind().unwrap();
|
||||
|
||||
let data = NodePerformanceSpecific {
|
||||
let data = NodePerformance {
|
||||
node_id: id1,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
};
|
||||
let another_data = NodePerformanceSpecific {
|
||||
let another_data = NodePerformance {
|
||||
node_id: id2,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind,
|
||||
@@ -1270,7 +1252,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
0,
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[0],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -1288,7 +1270,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
0,
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[3],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -1306,7 +1288,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
1,
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[1],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -1324,7 +1306,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
12345,
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[8],
|
||||
performance: Default::default(),
|
||||
measurement_kind,
|
||||
@@ -1359,7 +1341,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
0,
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[0],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -1374,7 +1356,7 @@ mod tests {
|
||||
data: Some(LastSubmittedData {
|
||||
sender: nm.clone(),
|
||||
epoch_id: 0,
|
||||
data: NodePerformanceSpecific {
|
||||
data: NodePerformance {
|
||||
node_id: nodes[0],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -1388,7 +1370,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
0,
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[6],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -1403,7 +1385,7 @@ mod tests {
|
||||
data: Some(LastSubmittedData {
|
||||
sender: nm.clone(),
|
||||
epoch_id: 0,
|
||||
data: NodePerformanceSpecific {
|
||||
data: NodePerformance {
|
||||
node_id: nodes[6],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -1417,7 +1399,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
1,
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[2],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -1432,7 +1414,7 @@ mod tests {
|
||||
data: Some(LastSubmittedData {
|
||||
sender: nm.clone(),
|
||||
epoch_id: 1,
|
||||
data: NodePerformanceSpecific {
|
||||
data: NodePerformance {
|
||||
node_id: nodes[2],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -1446,7 +1428,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
12345,
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[9],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -1461,7 +1443,7 @@ mod tests {
|
||||
data: Some(LastSubmittedData {
|
||||
sender: nm.clone(),
|
||||
epoch_id: 12345,
|
||||
data: NodePerformanceSpecific {
|
||||
data: NodePerformance {
|
||||
node_id: nodes[9],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -1483,7 +1465,7 @@ mod tests {
|
||||
tester.authorise_network_monitor(&nm)?;
|
||||
let measurement_kind = tester.define_dummy_measurement_kind().unwrap();
|
||||
|
||||
let dummy_perf = NodePerformanceSpecific {
|
||||
let dummy_perf = NodePerformance {
|
||||
node_id: 12345,
|
||||
performance: Percent::from_percentage_value(69)?,
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -1508,7 +1490,7 @@ mod tests {
|
||||
|
||||
// bonded nym-node
|
||||
let node_id = tester.bond_dummy_nymnode()?;
|
||||
let perf = NodePerformanceSpecific {
|
||||
let perf = NodePerformance {
|
||||
node_id,
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -1631,17 +1613,17 @@ mod tests {
|
||||
let id2 = tester.bond_dummy_nymnode()?;
|
||||
let id3 = tester.bond_dummy_nymnode()?;
|
||||
let measurement_kind = tester.define_dummy_measurement_kind().unwrap();
|
||||
let data = NodePerformanceSpecific {
|
||||
let data = NodePerformance {
|
||||
node_id: id1,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
};
|
||||
let another_data = NodePerformanceSpecific {
|
||||
let another_data = NodePerformance {
|
||||
node_id: id2,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
};
|
||||
let more_data = NodePerformanceSpecific {
|
||||
let more_data = NodePerformance {
|
||||
node_id: id3,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -1724,12 +1706,12 @@ mod tests {
|
||||
|
||||
let measurement_kind = tester.define_dummy_measurement_kind().unwrap();
|
||||
|
||||
let data = NodePerformanceSpecific {
|
||||
let data = NodePerformance {
|
||||
node_id: id1,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
};
|
||||
let another_data = NodePerformanceSpecific {
|
||||
let another_data = NodePerformance {
|
||||
node_id: id2,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -1831,12 +1813,12 @@ mod tests {
|
||||
|
||||
let measurement_kind = tester.define_dummy_measurement_kind().unwrap();
|
||||
|
||||
let data = NodePerformanceSpecific {
|
||||
let data = NodePerformance {
|
||||
node_id: id1,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
};
|
||||
let another_data = NodePerformanceSpecific {
|
||||
let another_data = NodePerformance {
|
||||
node_id: id2,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -2016,7 +1998,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
0,
|
||||
vec![NodePerformanceSpecific {
|
||||
vec![NodePerformance {
|
||||
node_id: nodes[0],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -2035,7 +2017,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
1,
|
||||
vec![NodePerformanceSpecific {
|
||||
vec![NodePerformance {
|
||||
node_id: nodes[1],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -2055,17 +2037,17 @@ mod tests {
|
||||
&nm,
|
||||
1,
|
||||
vec![
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[2],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[3],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[4],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -2086,17 +2068,17 @@ mod tests {
|
||||
&nm,
|
||||
2,
|
||||
vec![
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[1],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[6],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[8],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -2134,7 +2116,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
0,
|
||||
vec![NodePerformanceSpecific {
|
||||
vec![NodePerformance {
|
||||
node_id: nodes[0],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -2149,7 +2131,7 @@ mod tests {
|
||||
data: Some(LastSubmittedData {
|
||||
sender: nm.clone(),
|
||||
epoch_id: 0,
|
||||
data: NodePerformanceSpecific {
|
||||
data: NodePerformance {
|
||||
node_id: nodes[0],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -2164,7 +2146,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
1,
|
||||
vec![NodePerformanceSpecific {
|
||||
vec![NodePerformance {
|
||||
node_id: nodes[1],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -2179,7 +2161,7 @@ mod tests {
|
||||
data: Some(LastSubmittedData {
|
||||
sender: nm.clone(),
|
||||
epoch_id: 1,
|
||||
data: NodePerformanceSpecific {
|
||||
data: NodePerformance {
|
||||
node_id: nodes[1],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -2195,17 +2177,17 @@ mod tests {
|
||||
&nm,
|
||||
1,
|
||||
vec![
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[2],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[3],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[4],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -2221,7 +2203,7 @@ mod tests {
|
||||
data: Some(LastSubmittedData {
|
||||
sender: nm.clone(),
|
||||
epoch_id: 1,
|
||||
data: NodePerformanceSpecific {
|
||||
data: NodePerformance {
|
||||
node_id: nodes[4],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -2237,17 +2219,17 @@ mod tests {
|
||||
&nm,
|
||||
2,
|
||||
vec![
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[1],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[7],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nodes[8],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -2263,7 +2245,7 @@ mod tests {
|
||||
data: Some(LastSubmittedData {
|
||||
sender: nm.clone(),
|
||||
epoch_id: 2,
|
||||
data: NodePerformanceSpecific {
|
||||
data: NodePerformance {
|
||||
node_id: nodes[8],
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone()
|
||||
@@ -2304,7 +2286,7 @@ mod tests {
|
||||
env.clone(),
|
||||
&nm,
|
||||
0,
|
||||
vec![NodePerformanceSpecific {
|
||||
vec![NodePerformance {
|
||||
node_id: 999999,
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -2320,12 +2302,12 @@ mod tests {
|
||||
&nm,
|
||||
1,
|
||||
vec![
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nym_node1,
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: 999999,
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -2342,22 +2324,22 @@ mod tests {
|
||||
&nm,
|
||||
2,
|
||||
vec![
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: 2,
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nym_node1,
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nym_node_between,
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
},
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id: nym_node2,
|
||||
performance: Default::default(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
@@ -2651,7 +2633,10 @@ mod tests {
|
||||
|
||||
// no results
|
||||
let node_id = tester.bond_dummy_nymnode()?;
|
||||
assert_eq!(storage.try_load_performance(&tester, 0, node_id)?, None);
|
||||
assert_eq!(
|
||||
storage.try_load_performance(&tester, 0, node_id)?,
|
||||
HashMap::new()
|
||||
);
|
||||
|
||||
//
|
||||
// always returns median value with 2decimal places precision
|
||||
@@ -2664,6 +2649,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
storage
|
||||
.try_load_performance(&tester, 0, node_id)?
|
||||
.get(&measurement_kind)
|
||||
.unwrap()
|
||||
.value()
|
||||
.to_string(),
|
||||
@@ -2677,6 +2663,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
storage
|
||||
.try_load_performance(&tester, 0, node_id)?
|
||||
.get(&measurement_kind)
|
||||
.unwrap()
|
||||
.value()
|
||||
.to_string(),
|
||||
@@ -2690,6 +2677,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
storage
|
||||
.try_load_performance(&tester, 0, node_id)?
|
||||
.get(&measurement_kind)
|
||||
.unwrap()
|
||||
.value()
|
||||
.to_string(),
|
||||
@@ -2704,6 +2692,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
storage
|
||||
.try_load_performance(&tester, 0, node_id)?
|
||||
.get(&measurement_kind)
|
||||
.unwrap()
|
||||
.value()
|
||||
.to_string(),
|
||||
@@ -2720,6 +2709,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
storage
|
||||
.try_load_performance(&tester, 0, node_id)?
|
||||
.get(&measurement_kind)
|
||||
.unwrap()
|
||||
.value()
|
||||
.to_string(),
|
||||
@@ -2737,6 +2727,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
storage
|
||||
.try_load_performance(&tester, 0, node_id)?
|
||||
.get(&measurement_kind)
|
||||
.unwrap()
|
||||
.value()
|
||||
.to_string(),
|
||||
@@ -2848,7 +2839,7 @@ mod tests {
|
||||
|
||||
let before = storage
|
||||
.performance_results
|
||||
.results_specific
|
||||
.results
|
||||
.prefix((epoch_id, id1))
|
||||
.all_values(tester.storage())
|
||||
.unwrap();
|
||||
@@ -2858,7 +2849,7 @@ mod tests {
|
||||
|
||||
let after = storage
|
||||
.performance_results
|
||||
.results_specific
|
||||
.results
|
||||
.prefix((epoch_id, id1))
|
||||
.all_values(tester.storage())
|
||||
.unwrap();
|
||||
@@ -2871,7 +2862,7 @@ mod tests {
|
||||
|
||||
let before = storage
|
||||
.performance_results
|
||||
.results_specific
|
||||
.results
|
||||
.prefix((epoch_id, id2))
|
||||
.all_values(tester.storage())
|
||||
.unwrap();
|
||||
@@ -2881,7 +2872,7 @@ mod tests {
|
||||
|
||||
let after = storage
|
||||
.performance_results
|
||||
.results_specific
|
||||
.results
|
||||
.prefix((epoch_id, id2))
|
||||
.all_values(tester.storage())
|
||||
.unwrap();
|
||||
@@ -2998,7 +2989,7 @@ mod tests {
|
||||
|
||||
let before = storage
|
||||
.performance_results
|
||||
.results_specific
|
||||
.results
|
||||
.sub_prefix(epoch_id)
|
||||
.all_values(&tester)?;
|
||||
assert_eq!(before.len(), 10);
|
||||
@@ -3008,7 +2999,7 @@ mod tests {
|
||||
let after = storage
|
||||
.performance_results
|
||||
.results
|
||||
.prefix(epoch_id)
|
||||
.sub_prefix(epoch_id)
|
||||
.all_values(&tester)?;
|
||||
|
||||
assert!(after.is_empty());
|
||||
@@ -3031,7 +3022,7 @@ mod tests {
|
||||
let after = storage
|
||||
.performance_results
|
||||
.results
|
||||
.prefix(epoch_id)
|
||||
.sub_prefix(epoch_id)
|
||||
.all_values(&tester)?;
|
||||
|
||||
assert!(after.is_empty());
|
||||
@@ -3065,7 +3056,7 @@ mod tests {
|
||||
|
||||
let before = storage
|
||||
.performance_results
|
||||
.results_specific
|
||||
.results
|
||||
.sub_prefix(epoch_id)
|
||||
.all_values(&tester)?;
|
||||
assert_eq!(
|
||||
@@ -3077,7 +3068,7 @@ mod tests {
|
||||
assert!(res.additional_entries_to_remove_remaining);
|
||||
let after = storage
|
||||
.performance_results
|
||||
.results_specific
|
||||
.results
|
||||
.sub_prefix(epoch_id)
|
||||
.all_values(&tester)?;
|
||||
|
||||
@@ -3090,7 +3081,7 @@ mod tests {
|
||||
assert!(res.additional_entries_to_remove_remaining);
|
||||
let after = storage
|
||||
.performance_results
|
||||
.results_specific
|
||||
.results
|
||||
.sub_prefix(epoch_id)
|
||||
.all_values(&tester)?;
|
||||
|
||||
@@ -3100,7 +3091,7 @@ mod tests {
|
||||
assert!(!res.additional_entries_to_remove_remaining);
|
||||
let after = storage
|
||||
.performance_results
|
||||
.results_specific
|
||||
.results
|
||||
.sub_prefix(epoch_id)
|
||||
.all_values(&tester)?;
|
||||
|
||||
@@ -3266,69 +3257,88 @@ mod tests {
|
||||
let node_id2 = 456;
|
||||
let measurement_kind = tester.define_dummy_measurement_kind().unwrap();
|
||||
|
||||
let data1 = NodePerformanceSpecific {
|
||||
let data1 = NodePerformance {
|
||||
node_id: node_id1,
|
||||
performance: Percent::from_str("0.23")?,
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
};
|
||||
|
||||
let data2 = NodePerformanceSpecific {
|
||||
let data2 = NodePerformance {
|
||||
node_id: node_id1,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
};
|
||||
|
||||
let data3 = NodePerformanceSpecific {
|
||||
let data3 = NodePerformance {
|
||||
node_id: node_id2,
|
||||
performance: Percent::from_str("0.23643634")?,
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
};
|
||||
|
||||
let data4 = NodePerformanceSpecific {
|
||||
let data4 = NodePerformance {
|
||||
node_id: node_id2,
|
||||
performance: Percent::hundred(),
|
||||
measurement_kind: measurement_kind.clone(),
|
||||
};
|
||||
|
||||
assert!(storage.results.may_load(&tester, (1, node_id1))?.is_none());
|
||||
assert!(storage.results.may_load(&tester, (1, node_id2))?.is_none());
|
||||
|
||||
assert!(
|
||||
storage
|
||||
.results
|
||||
.may_load(&tester, (1, node_id1, measurement_kind.clone()))?
|
||||
.is_none()
|
||||
);
|
||||
assert!(
|
||||
storage
|
||||
.results
|
||||
.may_load(&tester, (1, node_id2, measurement_kind.clone()))?
|
||||
.is_none()
|
||||
);
|
||||
storage.insert_performance_data(&mut tester, 1, &data1)?;
|
||||
assert_eq!(
|
||||
tester.read_raw_scores(1, node_id1)?.inner(),
|
||||
tester
|
||||
.read_raw_scores(1, node_id1, measurement_kind.clone())?
|
||||
.inner(),
|
||||
&[data1.performance]
|
||||
);
|
||||
storage.insert_performance_data(&mut tester, 1, &data2)?;
|
||||
assert_eq!(
|
||||
tester.read_raw_scores(1, node_id1)?.inner(),
|
||||
tester
|
||||
.read_raw_scores(1, node_id1, measurement_kind.clone())?
|
||||
.inner(),
|
||||
&[data1.performance, data2.performance]
|
||||
);
|
||||
|
||||
storage.insert_performance_data(&mut tester, 1, &data3)?;
|
||||
assert_eq!(
|
||||
tester.read_raw_scores(1, node_id2)?.inner(),
|
||||
tester
|
||||
.read_raw_scores(1, node_id2, measurement_kind.clone())?
|
||||
.inner(),
|
||||
&[data3.performance.round_to_two_decimal_places()]
|
||||
);
|
||||
storage.insert_performance_data(&mut tester, 1, &data4)?;
|
||||
assert_eq!(
|
||||
tester.read_raw_scores(1, node_id2)?.inner(),
|
||||
tester
|
||||
.read_raw_scores(1, node_id2, measurement_kind.clone())?
|
||||
.inner(),
|
||||
&[
|
||||
data3.performance.round_to_two_decimal_places(),
|
||||
data4.performance
|
||||
]
|
||||
);
|
||||
|
||||
storage.insert_performance_data(&mut tester, 2, &data2)?;
|
||||
storage.insert_performance_data(&mut tester, 2, &data2)?;
|
||||
assert_eq!(
|
||||
tester.read_raw_scores(2, node_id1)?.inner(),
|
||||
tester
|
||||
.read_raw_scores(2, node_id1, measurement_kind.clone())?
|
||||
.inner(),
|
||||
&[data2.performance, data2.performance]
|
||||
);
|
||||
|
||||
storage.insert_performance_data(&mut tester, 2, &data4)?;
|
||||
storage.insert_performance_data(&mut tester, 2, &data4)?;
|
||||
assert_eq!(
|
||||
tester.read_raw_scores(2, node_id2)?.inner(),
|
||||
tester
|
||||
.read_raw_scores(2, node_id2, measurement_kind.clone())?
|
||||
.inner(),
|
||||
&[data4.performance, data4.performance]
|
||||
);
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ use nym_mixnet_contract_common::{
|
||||
};
|
||||
use nym_performance_contract_common::constants::storage_keys;
|
||||
use nym_performance_contract_common::{
|
||||
ExecuteMsg, InstantiateMsg, MigrateMsg, NodeId, NodePerformanceSpecific, NodeResults,
|
||||
NymPerformanceContractError, QueryMsg,
|
||||
EpochNodePerformance, ExecuteMsg, InstantiateMsg, MigrateMsg, NodeId, NodePerformance,
|
||||
NodeResults, NymPerformanceContractError, QueryMsg,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
@@ -94,6 +94,20 @@ pub fn init_contract_tester() -> ContractTester<PerformanceContract> {
|
||||
.with_common_storage_key(CommonStorageKeys::Admin, storage_keys::CONTRACT_ADMIN)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
// shorthand factory to avoid verbosity in tests
|
||||
pub(crate) fn epoch_node_performance_unchecked(
|
||||
epoch: EpochId,
|
||||
measurement_kind: MeasurementKind,
|
||||
performance: &str,
|
||||
) -> EpochNodePerformance {
|
||||
let performance = performance.parse().unwrap();
|
||||
EpochNodePerformance {
|
||||
epoch,
|
||||
performance: [(measurement_kind, performance)].into_iter().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
// we need to be able to test instantiation, but for that we require
|
||||
// deps in a state that already includes instantiated mixnet contract
|
||||
pub(crate) struct PreInitContract {
|
||||
@@ -378,10 +392,10 @@ pub(crate) trait PerformanceContractTesterExt:
|
||||
Ok(measurement_kind)
|
||||
}
|
||||
|
||||
fn dummy_node_performance(&mut self) -> NodePerformanceSpecific {
|
||||
fn dummy_node_performance(&mut self) -> NodePerformance {
|
||||
let node_id = self.bond_dummy_nymnode().unwrap();
|
||||
let measurement_kind = self.dummy_measurement_kind();
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id,
|
||||
performance: Percent::from_percentage_value(69).unwrap(),
|
||||
measurement_kind,
|
||||
@@ -413,7 +427,7 @@ pub(crate) trait PerformanceContractTesterExt:
|
||||
env,
|
||||
addr,
|
||||
epoch_id,
|
||||
NodePerformanceSpecific {
|
||||
NodePerformance {
|
||||
node_id,
|
||||
performance,
|
||||
measurement_kind,
|
||||
@@ -455,11 +469,12 @@ pub(crate) trait PerformanceContractTesterExt:
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
node_id: NodeId,
|
||||
measurement_kind: MeasurementKind,
|
||||
) -> Result<NodeResults, NymPerformanceContractError> {
|
||||
let scores = NYM_PERFORMANCE_CONTRACT_STORAGE
|
||||
.performance_results
|
||||
.results
|
||||
.load(self.deps().storage, (epoch_id, node_id))?;
|
||||
.load(self.deps().storage, (epoch_id, node_id, measurement_kind))?;
|
||||
Ok(scores)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use crate::storage::{MeasurementKind, NYM_PERFORMANCE_CONTRACT_STORAGE};
|
||||
use cosmwasm_std::{Addr, DepsMut, Env, Event, MessageInfo, Response, to_json_binary};
|
||||
use nym_performance_contract_common::{
|
||||
EpochId, NodeId, NodePerformanceSpecific, NymPerformanceContractError,
|
||||
EpochId, NodeId, NodePerformance, NymPerformanceContractError,
|
||||
};
|
||||
|
||||
pub fn try_update_contract_admin(
|
||||
@@ -30,6 +30,18 @@ pub fn try_define_measurement_kind(
|
||||
.contract_admin
|
||||
.assert_admin(deps.as_ref(), sender)?;
|
||||
|
||||
// validation
|
||||
if measurement_kind.len() < 2
|
||||
|| measurement_kind.len() > 20
|
||||
|| !measurement_kind.is_ascii()
|
||||
|| measurement_kind.contains(char::is_whitespace)
|
||||
{
|
||||
return Err(NymPerformanceContractError::InvalidInput(format!(
|
||||
"Cannot define {} as measurement kind",
|
||||
measurement_kind
|
||||
)));
|
||||
}
|
||||
|
||||
NYM_PERFORMANCE_CONTRACT_STORAGE
|
||||
.performance_results
|
||||
.define_new_measurement_kind(deps.storage, measurement_kind)?;
|
||||
@@ -58,7 +70,7 @@ pub fn try_submit_performance_results(
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
epoch_id: EpochId,
|
||||
data: NodePerformanceSpecific,
|
||||
data: NodePerformance,
|
||||
) -> Result<Response, NymPerformanceContractError> {
|
||||
NYM_PERFORMANCE_CONTRACT_STORAGE.submit_performance_data(
|
||||
deps,
|
||||
@@ -77,7 +89,7 @@ pub fn try_batch_submit_performance_results(
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
epoch_id: EpochId,
|
||||
data: Vec<NodePerformanceSpecific>,
|
||||
data: Vec<NodePerformance>,
|
||||
) -> Result<Response, NymPerformanceContractError> {
|
||||
let res = NYM_PERFORMANCE_CONTRACT_STORAGE.batch_submit_performance_results(
|
||||
deps,
|
||||
|
||||
@@ -6,7 +6,7 @@ use nym_api_requests::models::RoutingScore;
|
||||
use nym_contracts_common::NaiveFloat;
|
||||
use nym_mixnet_contract_common::reward_params::Performance;
|
||||
use nym_mixnet_contract_common::{EpochId, NodeId};
|
||||
use nym_validator_client::nyxd::contract_traits::performance_query_client::NodePerformanceSpecific;
|
||||
use nym_validator_client::nyxd::contract_traits::performance_query_client::NodePerformance;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
pub(crate) struct PerformanceContractEpochCacheData {
|
||||
@@ -16,7 +16,7 @@ pub(crate) struct PerformanceContractEpochCacheData {
|
||||
|
||||
impl PerformanceContractEpochCacheData {
|
||||
pub(crate) fn from_node_performance(
|
||||
performance: Vec<NodePerformanceSpecific>,
|
||||
performance: Vec<NodePerformance>,
|
||||
epoch_id: EpochId,
|
||||
) -> Self {
|
||||
let median_performance = performance
|
||||
|
||||
@@ -36,7 +36,7 @@ use nym_mixnet_contract_common::{
|
||||
use nym_validator_client::coconut::EcashApiError;
|
||||
use nym_validator_client::nyxd::contract_traits::mixnet_query_client::MixnetQueryClientExt;
|
||||
use nym_validator_client::nyxd::contract_traits::performance_query_client::{
|
||||
LastSubmission, NodePerformanceSpecific,
|
||||
LastSubmission, NodePerformance,
|
||||
};
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
PagedDkgQueryClient, PagedPerformanceQueryClient, PerformanceQueryClient,
|
||||
@@ -414,7 +414,7 @@ impl Client {
|
||||
pub(crate) async fn get_full_epoch_performance(
|
||||
&self,
|
||||
epoch_id: nym_mixnet_contract_common::EpochId,
|
||||
) -> Result<Vec<NodePerformanceSpecific>, NyxdError> {
|
||||
) -> Result<Vec<NodePerformance>, NyxdError> {
|
||||
nyxd_query!(self, get_all_epoch_performance(epoch_id).await)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user