Feature/configure profit (#1008)

* Introduce a method to update mixnode configuration

Right now, only for profit_margin_percent

* Check that the new profit margin is valid

* Extend a bit the test coverage of mixnode update

* Create validator client function

* [ci skip] Generate TS types

* Update wallet

* Update the bond height as well, as if a rebond was made

Co-authored-by: neacsu <neacsu@users.noreply.github.com>
This commit is contained in:
Bogdan-Ștefan Neacşu
2022-01-06 13:03:14 +01:00
committed by GitHub
parent ec4955f814
commit 30e93c33bb
8 changed files with 159 additions and 0 deletions
@@ -20,6 +20,7 @@ pub enum Operation {
BondMixnodeOnBehalf,
UnbondMixnode,
UnbondMixnodeOnBehalf,
UpdateMixnodeConfig,
DelegateToMixnode,
DelegateToMixnodeOnBehalf,
UndelegateFromMixnode,
@@ -57,6 +58,7 @@ impl fmt::Display for Operation {
Operation::BondMixnode => f.write_str("BondMixnode"),
Operation::BondMixnodeOnBehalf => f.write_str("BondMixnodeOnBehalf"),
Operation::UnbondMixnode => f.write_str("UnbondMixnode"),
Operation::UpdateMixnodeConfig => f.write_str("UpdateMixnodeConfig"),
Operation::UnbondMixnodeOnBehalf => f.write_str("UnbondMixnodeOnBehalf"),
Operation::BondGateway => f.write_str("BondGateway"),
Operation::BondGatewayOnBehalf => f.write_str("BondGatewayOnBehalf"),
@@ -94,6 +96,7 @@ impl Operation {
Operation::BondMixnodeOnBehalf => 200_000u64.into(),
Operation::UnbondMixnode => 175_000u64.into(),
Operation::UnbondMixnodeOnBehalf => 175_000u64.into(),
Operation::UpdateMixnodeConfig => 175_000u64.into(),
Operation::DelegateToMixnode => 175_000u64.into(),
Operation::DelegateToMixnodeOnBehalf => 175_000u64.into(),
Operation::UndelegateFromMixnode => 175_000u64.into(),
@@ -773,6 +773,31 @@ impl<C> NymdClient<C> {
.await
}
/// Update the configuration of a mixnode. Right now, only possible for profit margin.
pub async fn update_mixnode_config(
&self,
profit_margin_percent: u8,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.operation_fee(Operation::UpdateMixnodeConfig);
let req = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
};
self.client
.execute(
self.address(),
self.mixnet_contract_address()?,
&req,
fee,
"Updating mixnode configuration from rust!",
Vec::new(),
)
.await
}
/// Delegates specified amount of stake to particular mixnode.
pub async fn delegate_to_mixnode(
&self,
+3
View File
@@ -20,6 +20,9 @@ pub enum ExecuteMsg {
owner_signature: String,
},
UnbondMixnode {},
UpdateMixnodeConfig {
profit_margin_percent: u8,
},
BondGateway {
gateway: Gateway,
owner_signature: String,
+8
View File
@@ -104,6 +104,14 @@ pub fn execute(
ExecuteMsg::UnbondMixnode {} => {
crate::mixnodes::transactions::try_remove_mixnode(deps, info)
}
ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
} => crate::mixnodes::transactions::try_update_mixnode_config(
deps,
env,
info,
profit_margin_percent,
),
ExecuteMsg::BondGateway {
gateway,
owner_signature,
@@ -204,6 +204,42 @@ pub(crate) fn _try_remove_mixnode(
Ok(response)
}
pub(crate) fn try_update_mixnode_config(
deps: DepsMut,
env: Env,
info: MessageInfo,
profit_margin_percent: u8,
) -> Result<Response, ContractError> {
let owner = deps.api.addr_validate(info.sender.as_ref())?;
let mix_identity = storage::mixnodes()
.idx
.owner
.item(deps.storage, owner.clone())?
.ok_or(ContractError::NoAssociatedMixNodeBond { owner })?
.1
.identity()
.clone();
// We don't have to check lower bound as its an u8
if profit_margin_percent > 100 {
return Err(ContractError::InvalidProfitMarginPercent(
profit_margin_percent,
));
}
storage::mixnodes().update(deps.storage, &mix_identity, |mixnode_bond_opt| {
mixnode_bond_opt
.map(|mut mixnode_bond| {
mixnode_bond.mix_node.profit_margin_percent = profit_margin_percent;
mixnode_bond.block_height = env.block.height;
mixnode_bond
})
.ok_or(ContractError::NoBondFound)
})?;
Ok(Response::new())
}
fn validate_mixnode_pledge(
mut pledge: Vec<Coin>,
minimum_pledge: Uint128,
@@ -587,6 +623,77 @@ pub mod tests {
);
}
#[test]
fn updating_mixnode_config() {
let sender = "bob";
let mut deps = test_helpers::init_contract();
let info = mock_info(sender, &[]);
// try updating a non existing mixnode bond
let msg = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent: 10,
};
let ret = execute(deps.as_mut(), mock_env(), info.clone(), msg);
assert_eq!(
ret,
Err(ContractError::NoAssociatedMixNodeBond {
owner: Addr::unchecked(sender)
})
);
test_helpers::add_mixnode(
sender,
tests::fixtures::good_mixnode_pledge(),
deps.as_mut(),
);
// check the initial profit margin is set to the fixture value
let fixture_profit_margin = tests::fixtures::mix_node_fixture().profit_margin_percent;
assert_eq!(
fixture_profit_margin,
storage::mixnodes()
.idx
.owner
.item(deps.as_ref().storage, Addr::unchecked("bob"))
.unwrap()
.unwrap()
.1
.mix_node
.profit_margin_percent
);
// try updating with an invalid value
let profit_margin_percent = 101;
let msg = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
};
let ret = execute(deps.as_mut(), mock_env(), info.clone(), msg);
assert_eq!(
ret,
Err(ContractError::InvalidProfitMarginPercent(
profit_margin_percent
))
);
let profit_margin_percent = fixture_profit_margin + 10;
let msg = ExecuteMsg::UpdateMixnodeConfig {
profit_margin_percent,
};
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(
profit_margin_percent,
storage::mixnodes()
.idx
.owner
.item(deps.as_ref().storage, Addr::unchecked("bob"))
.unwrap()
.unwrap()
.1
.mix_node
.profit_margin_percent
);
}
#[test]
fn validating_mixnode_bond() {
// you must send SOME funds
+1
View File
@@ -36,6 +36,7 @@ fn main() {
mixnet::bond::bond_mixnode,
mixnet::bond::unbond_gateway,
mixnet::bond::unbond_mixnode,
mixnet::bond::update_mixnode,
mixnet::bond::mixnode_bond_details,
mixnet::bond::gateway_bond_details,
mixnet::delegate::delegate_to_mixnode,
@@ -50,6 +50,17 @@ pub async fn bond_mixnode(
Ok(())
}
#[tauri::command]
pub async fn update_mixnode(
profit_margin_percent: u8,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
client!(state)
.update_mixnode_config(profit_margin_percent)
.await?;
Ok(())
}
#[tauri::command]
pub async fn mixnode_bond_details(
state: tauri::State<'_, Arc<RwLock<State>>>,
+1
View File
@@ -8,6 +8,7 @@ export type Operation =
| "BondMixnodeOnBehalf"
| "UnbondMixnode"
| "UnbondMixnodeOnBehalf"
| "UpdateMixnodeConfig"
| "DelegateToMixnode"
| "DelegateToMixnodeOnBehalf"
| "UndelegateFromMixnode"