From fb91abd3ca03b018a659b80d28dc2fd6a02aa401 Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Wed, 20 Nov 2024 14:25:36 +0200 Subject: [PATCH 1/6] Update on_top on validator set --- contracts/validators-set/src/contract.rs | 21 ++- contracts/validators-set/src/tests.rs | 170 ++++++++++++++++++++++- packages/base/src/msg/validatorset.rs | 4 +- 3 files changed, 182 insertions(+), 13 deletions(-) diff --git a/contracts/validators-set/src/contract.rs b/contracts/validators-set/src/contract.rs index 4f54bbec..e3e31872 100644 --- a/contracts/validators-set/src/contract.rs +++ b/contracts/validators-set/src/contract.rs @@ -15,6 +15,8 @@ use drop_staking_base::state::validatorset::{ use neutron_sdk::bindings::msg::NeutronMsg; use neutron_sdk::bindings::query::NeutronQuery; +use std::collections::HashMap; + const CONTRACT_NAME: &str = concat!("crates.io:drop-staking__", env!("CARGO_PKG_NAME")); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -138,14 +140,25 @@ fn execute_update_validators( let total_count = validators.len(); - // TODO: implement notification of the validator stats contract about new validators set + let old_validator_set: HashMap = VALIDATORS_SET + .range_raw(deps.storage, None, None, Order::Ascending) + .map(|item| item.and_then(|(_key, value)| Ok((value.valoper_address.to_string(), value)))) + .collect::>>()? + .into_iter() + .collect(); + VALIDATORS_SET.clear(deps.storage); for validator in validators { + let on_top_value = old_validator_set + .get(&validator.valoper_address) + .map(|validator_info| validator_info.on_top) + .unwrap_or_default(); + let validator_info = ValidatorInfo { valoper_address: validator.valoper_address, weight: validator.weight, - on_top: validator.on_top, + on_top: validator.on_top.unwrap_or(on_top_value), last_processed_remote_height: None, last_processed_local_height: None, last_validated_height: None, @@ -330,12 +343,12 @@ fn execute_edit_on_top( validator_info.on_top = validator_info.on_top.checked_add(amount)?; validator_info } - OnTopEditOperation::Subtract { + OnTopEditOperation::Set { validator_address, amount, } => { let mut validator_info = VALIDATORS_SET.load(deps.storage, &validator_address)?; - validator_info.on_top = validator_info.on_top.checked_sub(amount)?; + validator_info.on_top = amount; validator_info } }; diff --git a/contracts/validators-set/src/tests.rs b/contracts/validators-set/src/tests.rs index fc34bb33..596ff3ac 100644 --- a/contracts/validators-set/src/tests.rs +++ b/contracts/validators-set/src/tests.rs @@ -177,7 +177,7 @@ fn update_validators_wrong_owner() { validators: vec![drop_staking_base::msg::validatorset::ValidatorData { valoper_address: "valoper_address".to_string(), weight: 1, - on_top: Uint128::zero(), + on_top: Some(Uint128::zero()), }], }, ) @@ -211,12 +211,12 @@ fn update_validators_ok() { drop_staking_base::msg::validatorset::ValidatorData { valoper_address: "valoper_address1".to_string(), weight: 1, - on_top: Uint128::new(10), + on_top: Some(Uint128::new(10)), }, drop_staking_base::msg::validatorset::ValidatorData { valoper_address: "valoper_address2".to_string(), weight: 1, - on_top: Uint128::zero(), + on_top: Some(Uint128::zero()), }, ], }, @@ -268,6 +268,162 @@ fn update_validators_ok() { ); } +#[test] +fn update_validators_without_ontop_ok() { + let mut deps = mock_dependencies(&[]); + + let deps_mut = deps.as_mut(); + + let _result = cw_ownable::initialize_owner( + deps_mut.storage, + deps_mut.api, + Some(Addr::unchecked("core").as_ref()), + ); + + let response = crate::contract::execute( + deps.as_mut(), + mock_env(), + mock_info("core", &[]), + drop_staking_base::msg::validatorset::ExecuteMsg::UpdateValidators { + validators: vec![ + drop_staking_base::msg::validatorset::ValidatorData { + valoper_address: "valoper_address1".to_string(), + weight: 1, + on_top: Some(Uint128::new(20)), + }, + drop_staking_base::msg::validatorset::ValidatorData { + valoper_address: "valoper_address2".to_string(), + weight: 1, + on_top: None, + }, + ], + }, + ) + .unwrap(); + assert_eq!(response.messages.len(), 0); + + let validator = crate::contract::query( + deps.as_ref(), + mock_env(), + drop_staking_base::msg::validatorset::QueryMsg::Validators {}, + ) + .unwrap(); + assert_eq!( + validator, + to_json_binary(&vec![ + drop_staking_base::state::validatorset::ValidatorInfo { + valoper_address: "valoper_address1".to_string(), + weight: 1, + on_top: Uint128::new(20), + last_processed_remote_height: None, + last_processed_local_height: None, + last_validated_height: None, + last_commission_in_range: None, + uptime: Decimal::zero(), + tombstone: false, + jailed_number: None, + init_proposal: None, + total_passed_proposals: 0, + total_voted_proposals: 0, + }, + drop_staking_base::state::validatorset::ValidatorInfo { + valoper_address: "valoper_address2".to_string(), + weight: 1, + on_top: Uint128::zero(), + last_processed_remote_height: None, + last_processed_local_height: None, + last_validated_height: None, + last_commission_in_range: None, + uptime: Decimal::zero(), + tombstone: false, + jailed_number: None, + init_proposal: None, + total_passed_proposals: 0, + total_voted_proposals: 0, + } + ]) + .unwrap() + ); +} + +#[test] +fn update_validators_use_last_ontop() { + let mut deps = mock_dependencies(&[]); + + let deps_mut = deps.as_mut(); + + let _result = cw_ownable::initialize_owner( + deps_mut.storage, + deps_mut.api, + Some(Addr::unchecked("core").as_ref()), + ); + + drop_staking_base::state::validatorset::VALIDATORS_SET + .save( + deps.as_mut().storage, + "voter", + &drop_staking_base::state::validatorset::ValidatorInfo { + valoper_address: "valoper_address1".to_string(), + weight: 0u64, + on_top: Uint128::new(30), + last_processed_remote_height: None, + last_processed_local_height: None, + last_validated_height: None, + last_commission_in_range: None, + uptime: Decimal::zero(), + tombstone: false, + jailed_number: None, + init_proposal: None, + total_passed_proposals: 0u64, + total_voted_proposals: 0u64, + }, + ) + .unwrap(); + + let response = crate::contract::execute( + deps.as_mut(), + mock_env(), + mock_info("core", &[]), + drop_staking_base::msg::validatorset::ExecuteMsg::UpdateValidators { + validators: vec![drop_staking_base::msg::validatorset::ValidatorData { + valoper_address: "valoper_address1".to_string(), + weight: 1, + on_top: None, + }], + }, + ) + .unwrap(); + assert_eq!(response.messages.len(), 0); + + let validator = crate::contract::query( + deps.as_ref(), + mock_env(), + drop_staking_base::msg::validatorset::QueryMsg::Validators {}, + ) + .unwrap(); + assert_eq!( + validator, + to_json_binary(&vec![ + drop_staking_base::state::validatorset::ValidatorInfo { + valoper_address: "valoper_address1".to_string(), + weight: 1, + on_top: Uint128::new(30), + last_processed_remote_height: None, + last_processed_local_height: None, + last_validated_height: None, + last_commission_in_range: None, + uptime: Decimal::zero(), + tombstone: false, + jailed_number: None, + init_proposal: None, + total_passed_proposals: 0, + total_voted_proposals: 0, + }, + ]) + .unwrap() + ); +} + #[test] fn update_validators_info_wrong_sender() { let mut deps = mock_dependencies(&[]); @@ -299,7 +455,7 @@ fn update_validators_info_wrong_sender() { validators: vec![drop_staking_base::msg::validatorset::ValidatorData { valoper_address: "valoper_address".to_string(), weight: 1, - on_top: Uint128::zero(), + on_top: Some(Uint128::zero()), }], }, ) @@ -360,7 +516,7 @@ fn update_validators_info_ok() { validators: vec![drop_staking_base::msg::validatorset::ValidatorData { valoper_address: "valoper_address".to_string(), weight: 1, - on_top: Uint128::new(2), + on_top: Some(Uint128::new(2)), }], }, ) @@ -914,7 +1070,7 @@ fn execute_edit_on_top_subtract() { mock_info("val_ref_contract", &[]), drop_staking_base::msg::validatorset::ExecuteMsg::EditOnTop { operations: vec![ - drop_staking_base::msg::validatorset::OnTopEditOperation::Subtract { + drop_staking_base::msg::validatorset::OnTopEditOperation::Set { validator_address: String::from("valoperX"), amount: Uint128::new(100), }, @@ -1017,7 +1173,7 @@ fn execute_edit_on_top_mixed() { mock_info("val_ref_contract", &[]), drop_staking_base::msg::validatorset::ExecuteMsg::EditOnTop { operations: vec![ - drop_staking_base::msg::validatorset::OnTopEditOperation::Subtract { + drop_staking_base::msg::validatorset::OnTopEditOperation::Set { validator_address: String::from("valoperX"), amount: Uint128::new(100), }, diff --git a/packages/base/src/msg/validatorset.rs b/packages/base/src/msg/validatorset.rs index 284a9c0f..3aa99386 100644 --- a/packages/base/src/msg/validatorset.rs +++ b/packages/base/src/msg/validatorset.rs @@ -16,7 +16,7 @@ pub struct InstantiateMsg { pub struct ValidatorData { pub valoper_address: String, pub weight: u64, - pub on_top: Uint128, + pub on_top: Option, } #[cw_serde] @@ -57,7 +57,7 @@ pub enum OnTopEditOperation { validator_address: String, amount: Uint128, }, - Subtract { + Set { validator_address: String, amount: Uint128, }, From 5ba15b743b96cbb9a6ec10e97504e3dd610b71a1 Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Wed, 20 Nov 2024 14:25:48 +0200 Subject: [PATCH 2/6] Ignore on_top on unknown validator error --- contracts/val-ref/src/contract.rs | 35 +++++++++++++++++++++---------- contracts/val-ref/src/error.rs | 3 +++ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/contracts/val-ref/src/contract.rs b/contracts/val-ref/src/contract.rs index c520080f..c8c1dd33 100644 --- a/contracts/val-ref/src/contract.rs +++ b/contracts/val-ref/src/contract.rs @@ -1,7 +1,7 @@ use crate::error::{ContractError, ContractResult}; use cosmwasm_std::{ attr, entry_point, to_json_binary, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, Order, - Response, StdResult, SubMsg, WasmMsg, + Reply, Response, StdResult, SubMsg, WasmMsg, }; use drop_helpers::answer::response; use drop_staking_base::{ @@ -17,6 +17,8 @@ use neutron_sdk::bindings::{msg::NeutronMsg, query::NeutronQuery}; pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const EDIT_ON_TOP_REPLY_ID: u64 = 1; + #[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)] pub fn instantiate( deps: DepsMut, @@ -89,16 +91,19 @@ fn execute_bond_hook( &drop_staking_base::msg::core::QueryMsg::ExchangeRate {}, )?; let on_top_increase = bond_hook.dasset_minted * exchange_rate; - messages.push(SubMsg::new(WasmMsg::Execute { - contract_addr: VALIDATORS_SET_ADDRESS.load(deps.storage)?.into_string(), - funds: vec![], - msg: to_json_binary(&ValidatorSetExecuteMsg::EditOnTop { - operations: vec![OnTopEditOperation::Add { - validator_address, - amount: on_top_increase, - }], - })?, - })); + messages.push(SubMsg::reply_on_error( + WasmMsg::Execute { + contract_addr: VALIDATORS_SET_ADDRESS.load(deps.storage)?.into_string(), + funds: vec![], + msg: to_json_binary(&ValidatorSetExecuteMsg::EditOnTop { + operations: vec![OnTopEditOperation::Add { + validator_address, + amount: on_top_increase, + }], + })?, + }, + EDIT_ON_TOP_REPLY_ID, + )); attrs.push(attr("on_top_increase", on_top_increase)); } else { attrs.push(attr("validator", "None")); @@ -180,6 +185,14 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> ContractResu } } +#[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)] +pub fn reply(_deps: DepsMut, _env: Env, msg: Reply) -> ContractResult { + match msg.id { + EDIT_ON_TOP_REPLY_ID => Ok(Response::new()), + id => Err(ContractError::UnknownReplyId { id }), + } +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> ContractResult> { let version: semver::Version = CONTRACT_VERSION.parse()?; diff --git a/contracts/val-ref/src/error.rs b/contracts/val-ref/src/error.rs index 8d2705b5..a3744c80 100644 --- a/contracts/val-ref/src/error.rs +++ b/contracts/val-ref/src/error.rs @@ -11,6 +11,9 @@ pub enum ContractError { #[error("Semver parsing error: {0}")] SemVer(String), + + #[error("unknown reply id: {id}")] + UnknownReplyId { id: u64 }, } impl From for ContractError { From cef21744796640b9771a1352373a082df88d0edd Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Wed, 20 Nov 2024 15:05:53 +0200 Subject: [PATCH 3/6] tests fix --- contracts/factory/src/tests.rs | 12 ++++----- contracts/val-ref/src/tests.rs | 32 ++++++++++++++---------- contracts/validators-set/src/contract.rs | 2 +- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/contracts/factory/src/tests.rs b/contracts/factory/src/tests.rs index 780bafe5..4fe38dc9 100644 --- a/contracts/factory/src/tests.rs +++ b/contracts/factory/src/tests.rs @@ -713,12 +713,12 @@ fn test_proxy_validators_set_update_validators_unauthorized() { drop_staking_base::msg::validatorset::ValidatorData { valoper_address: "valoper_address1".to_string(), weight: 10u64, - on_top: Uint128::zero(), + on_top: Some(Uint128::zero()), }, drop_staking_base::msg::validatorset::ValidatorData { valoper_address: "valoper_address2".to_string(), weight: 10u64, - on_top: Uint128::zero(), + on_top: Some(Uint128::zero()), }, ], }, @@ -750,12 +750,12 @@ fn test_proxy_validators_set_update_validators() { drop_staking_base::msg::validatorset::ValidatorData { valoper_address: "valoper_address1".to_string(), weight: 10u64, - on_top: Uint128::zero(), + on_top: Some(Uint128::zero()), }, drop_staking_base::msg::validatorset::ValidatorData { valoper_address: "valoper_address2".to_string(), weight: 10u64, - on_top: Uint128::zero(), + on_top: Some(Uint128::zero()), }, ], }, @@ -774,12 +774,12 @@ fn test_proxy_validators_set_update_validators() { drop_staking_base::msg::validatorset::ValidatorData { valoper_address: "valoper_address1".to_string(), weight: 10u64, - on_top: Uint128::zero(), + on_top: Some(Uint128::zero()), }, drop_staking_base::msg::validatorset::ValidatorData { valoper_address: "valoper_address2".to_string(), weight: 10u64, - on_top: Uint128::zero(), + on_top: Some(Uint128::zero()), }, ], }) diff --git a/contracts/val-ref/src/tests.rs b/contracts/val-ref/src/tests.rs index 206809ca..4dc53cd5 100644 --- a/contracts/val-ref/src/tests.rs +++ b/contracts/val-ref/src/tests.rs @@ -1,8 +1,11 @@ -use crate::{contract, error::ContractError}; +use crate::{ + contract::{self, EDIT_ON_TOP_REPLY_ID}, + error::ContractError, +}; use cosmwasm_std::{ from_json, testing::{mock_env, mock_info}, - to_json_binary, Addr, Decimal, Event, Order, Response, StdResult, Uint128, WasmMsg, + to_json_binary, Addr, Decimal, Event, Order, Response, StdResult, SubMsg, Uint128, WasmMsg, }; use drop_helpers::testing::mock_dependencies; use drop_staking_base::{ @@ -262,17 +265,20 @@ fn execute_bond_hook_known_validator() { assert_eq!( response, Response::new() - .add_message(WasmMsg::Execute { - contract_addr: String::from("validators_set"), - msg: to_json_binary(&ValidatorSetExecuteMsg::EditOnTop { - operations: vec![OnTopEditOperation::Add { - validator_address: String::from("valoperX"), - amount: Uint128::new(150), - }] - }) - .unwrap(), - funds: vec![] - }) + .add_submessage(SubMsg::reply_on_error( + WasmMsg::Execute { + contract_addr: String::from("validators_set"), + msg: to_json_binary(&ValidatorSetExecuteMsg::EditOnTop { + operations: vec![OnTopEditOperation::Add { + validator_address: String::from("valoperX"), + amount: Uint128::new(150), + }] + }) + .unwrap(), + funds: vec![] + }, + EDIT_ON_TOP_REPLY_ID + )) .add_event( Event::new("drop-val-ref-execute-bond-hook").add_attributes([ ("ref", "X"), diff --git a/contracts/validators-set/src/contract.rs b/contracts/validators-set/src/contract.rs index e3e31872..ed4830b4 100644 --- a/contracts/validators-set/src/contract.rs +++ b/contracts/validators-set/src/contract.rs @@ -142,7 +142,7 @@ fn execute_update_validators( let old_validator_set: HashMap = VALIDATORS_SET .range_raw(deps.storage, None, None, Order::Ascending) - .map(|item| item.and_then(|(_key, value)| Ok((value.valoper_address.to_string(), value)))) + .map(|item| item.map(|(_key, value)| (value.valoper_address.to_string(), value))) .collect::>>()? .into_iter() .collect(); From fd84d3f441037cfd4a9ec6f66964c538f78eef44 Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Fri, 22 Nov 2024 14:00:42 +0200 Subject: [PATCH 4/6] Signed Commit Signed-off-by: Albert Andrejev From e52d7713099ae9314244c3b5f1bab371ed97a14e Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Fri, 22 Nov 2024 20:48:58 +0200 Subject: [PATCH 5/6] Update contracts/validators-set/src/contract.rs Co-authored-by: Sergey R --- contracts/validators-set/src/contract.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/contracts/validators-set/src/contract.rs b/contracts/validators-set/src/contract.rs index ed4830b4..db87bf50 100644 --- a/contracts/validators-set/src/contract.rs +++ b/contracts/validators-set/src/contract.rs @@ -140,12 +140,10 @@ fn execute_update_validators( let total_count = validators.len(); - let old_validator_set: HashMap = VALIDATORS_SET +let old_validator_set: HashMap = VALIDATORS_SET .range_raw(deps.storage, None, None, Order::Ascending) .map(|item| item.map(|(_key, value)| (value.valoper_address.to_string(), value))) - .collect::>>()? - .into_iter() - .collect(); + .collect::>()?; VALIDATORS_SET.clear(deps.storage); From 89258d4690f80806b65c583de7d2f369e6c1b5b7 Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Fri, 22 Nov 2024 22:29:30 +0200 Subject: [PATCH 6/6] implement review remarks --- contracts/val-ref/src/contract.rs | 6 +++++- contracts/validators-set/src/contract.rs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/contracts/val-ref/src/contract.rs b/contracts/val-ref/src/contract.rs index c8c1dd33..35d29a17 100644 --- a/contracts/val-ref/src/contract.rs +++ b/contracts/val-ref/src/contract.rs @@ -188,7 +188,11 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> ContractResu #[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)] pub fn reply(_deps: DepsMut, _env: Env, msg: Reply) -> ContractResult { match msg.id { - EDIT_ON_TOP_REPLY_ID => Ok(Response::new()), + EDIT_ON_TOP_REPLY_ID => Ok(response( + "reply", + CONTRACT_NAME, + [attr("edit_on_top_error", true.to_string())], + )), id => Err(ContractError::UnknownReplyId { id }), } } diff --git a/contracts/validators-set/src/contract.rs b/contracts/validators-set/src/contract.rs index db87bf50..24ce4e73 100644 --- a/contracts/validators-set/src/contract.rs +++ b/contracts/validators-set/src/contract.rs @@ -140,7 +140,7 @@ fn execute_update_validators( let total_count = validators.len(); -let old_validator_set: HashMap = VALIDATORS_SET + let old_validator_set: HashMap = VALIDATORS_SET .range_raw(deps.storage, None, None, Order::Ascending) .map(|item| item.map(|(_key, value)| (value.valoper_address.to_string(), value))) .collect::>()?;