Skip to content

Commit

Permalink
Merge pull request #109 from osmosis-labs/f/valset-updates
Browse files Browse the repository at this point in the history
F/valset updates
  • Loading branch information
JakeHartnell authored Aug 28, 2023
2 parents 2f0ea46 + 6361b7a commit 5c2ea14
Show file tree
Hide file tree
Showing 10 changed files with 360 additions and 25 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 37 additions & 3 deletions contracts/consumer/converter/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use cosmwasm_std::{
ensure_eq, to_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, Deps, DepsMut, Event, IbcMsg,
Reply, Response, SubMsg, SubMsgResponse, WasmMsg,
Reply, Response, SubMsg, SubMsgResponse, Validator, WasmMsg,
};
use cw2::set_contract_version;
use cw_storage_plus::Item;
Expand All @@ -14,7 +14,7 @@ use mesh_apis::price_feed_api;
use mesh_apis::virtual_staking_api;

use crate::error::ContractError;
use crate::ibc::{packet_timeout_rewards, IBC_CHANNEL};
use crate::ibc::{add_validators_msg, packet_timeout_rewards, IBC_CHANNEL};
use crate::msg::ConfigResponse;
use crate::state::Config;

Expand All @@ -36,7 +36,7 @@ impl ConverterContract<'_> {
pub const fn new() -> Self {
Self {
config: Item::new("config"),
virtual_stake: Item::new("bonded"),
virtual_stake: Item::new("virtual_stake"),
}
}

Expand Down Expand Up @@ -325,4 +325,38 @@ impl ConverterApi for ConverterContract<'_> {
}
Ok(resp)
}

/// Valset updates.
///
/// Sent validator set additions (entering the active validator set) to the external staking
/// contract on the Consumer via IBC.
#[msg(exec)]
fn valset_update(
&self,
ctx: ExecCtx,
additions: Vec<Validator>,
) -> Result<Response, Self::Error> {
let virtual_stake = self.virtual_stake.load(ctx.deps.storage)?;
ensure_eq!(
ctx.info.sender,
virtual_stake,
ContractError::Unauthorized {}
);

// Send over IBC to the Consumer
let channel = IBC_CHANNEL.load(ctx.deps.storage)?;
let msg = add_validators_msg(&ctx.env, channel, &additions)?;

let event = Event::new("valset_update").add_attribute(
"additions",
additions
.iter()
.map(|v| v.address.clone())
.collect::<Vec<String>>()
.join(","),
);
let resp = Response::new().add_event(event).add_message(msg);

Ok(resp)
}
}
26 changes: 18 additions & 8 deletions contracts/consumer/converter/src/ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use cosmwasm_std::{
from_slice, to_binary, DepsMut, Env, Event, Ibc3ChannelOpenResponse, IbcBasicResponse,
IbcChannel, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg,
IbcChannelOpenResponse, IbcMsg, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg,
IbcReceiveResponse, IbcTimeout,
IbcReceiveResponse, IbcTimeout, Validator,
};
use cw_storage_plus::Item;

Expand Down Expand Up @@ -114,13 +114,24 @@ pub fn ibc_channel_connect(

// Send a validator sync packet to arrive with the newly established channel
let validators = deps.querier.query_all_validators()?;
let msg = add_validators_msg(&env, channel, &validators)?;

Ok(IbcBasicResponse::new().add_message(msg))
}

pub(crate) fn add_validators_msg(
env: &Env,
channel: IbcChannel,
validators: &[Validator],
) -> Result<IbcMsg, ContractError> {
let updates = validators
.into_iter()
.iter()
.map(|v| AddValidator {
valoper: v.address,
// TODO: not yet available in CosmWasm APIs
valoper: v.address.clone(),
// TODO: not yet available in CosmWasm APIs. See https://github.com/CosmWasm/cosmwasm/issues/1828
pub_key: "TODO".to_string(),
// Use current height/time as start height/time (no slashing before mesh starts)
// Use current height/time as start height/time (no slashing before mesh starts).
// Warning: These will be updated as well when updating an already existing validator.
start_height: env.block.height,
start_time: env.block.time.seconds(),
})
Expand All @@ -129,10 +140,9 @@ pub fn ibc_channel_connect(
let msg = IbcMsg::SendPacket {
channel_id: channel.endpoint.channel_id,
data: to_binary(&packet)?,
timeout: packet_timeout_validator(&env),
timeout: packet_timeout_validator(env),
};

Ok(IbcBasicResponse::new().add_message(msg))
Ok(msg)
}

#[cfg_attr(not(feature = "library"), entry_point)]
Expand Down
67 changes: 66 additions & 1 deletion contracts/consumer/converter/src/multitest.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
mod virtual_staking_mock;

use cosmwasm_std::{coin, Addr, Decimal, Uint128};
use cosmwasm_std::{coin, Addr, Decimal, StdError, Uint128, Validator};
use cw_multi_test::App as MtApp;
use sylvia::multitest::App;

use crate::contract;
use crate::contract::test_utils::ConverterApi;
use crate::error::ContractError;
use crate::error::ContractError::Unauthorized;

const JUNO: &str = "ujuno";

Expand Down Expand Up @@ -195,3 +198,65 @@ fn ibc_stake_and_unstake() {
]
);
}

#[test]
fn valset_update_works() {
let app = App::default();

let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract)
let admin = "theman";
let discount = Decimal::percent(10); // 1 OSMO worth of JUNO should give 0.9 OSMO of stake
let native_per_foreign = Decimal::percent(40); // 1 JUNO is worth 0.4 OSMO

let SetupResponse {
price_feed: _,
converter,
virtual_staking,
} = setup(
&app,
SetupArgs {
owner,
admin,
discount,
native_per_foreign,
},
);

// Send a valset update
let new_validators = vec![
Validator {
address: "validator1".to_string(),
commission: Default::default(),
max_commission: Default::default(),
max_change_rate: Default::default(),
},
Validator {
address: "validator3".to_string(),
commission: Default::default(),
max_commission: Default::default(),
max_change_rate: Default::default(),
},
];

// Check that only the virtual staking contract can call this handler
let res = converter
.converter_api_proxy()
.valset_update(vec![])
.call(owner);
assert_eq!(res.unwrap_err(), Unauthorized {});

let res = converter
.converter_api_proxy()
.valset_update(new_validators)
.call(virtual_staking.contract_addr.as_ref());

// This fails because of lack of IBC support in mt now.
// Cannot be tested further in this setup.
// TODO: Change this when IBC support is there in mt.
assert_eq!(
res.unwrap_err(),
ContractError::Std(StdError::NotFound {
kind: "cosmwasm_std::ibc::IbcChannel".to_string()
})
);
}
12 changes: 7 additions & 5 deletions contracts/consumer/virtual-staking/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ mt = ["library", "sylvia/mt"]

[dependencies]
mesh-apis = { workspace = true }
mesh-bindings = { workspace = true }
mesh-bindings = { workspace = true }

sylvia = { workspace = true }
cosmwasm-schema = { workspace = true }
Expand All @@ -35,10 +35,12 @@ serde = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
cw-multi-test = { workspace = true }
test-case = { workspace = true }
derivative = { workspace = true }
anyhow = { workspace = true }
mesh-simple-price-feed = { workspace = true, features = ["mt"] }
mesh-converter = { workspace = true, features = ["mt"] }
cw-multi-test = { workspace = true }
test-case = { workspace = true }
derivative = { workspace = true }
anyhow = { workspace = true }

[[bin]]
name = "schema"
Expand Down
35 changes: 34 additions & 1 deletion contracts/consumer/virtual-staking/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::collections::BTreeMap;
use cosmwasm_std::{
coin, ensure_eq, entry_point, to_binary, Coin, CosmosMsg, CustomQuery, DepsMut,
DistributionMsg, Env, Event, Reply, Response, StdResult, SubMsg, SubMsgResponse, Uint128,
WasmMsg,
Validator, WasmMsg,
};
use cw2::set_contract_version;
use cw_storage_plus::{Item, Map};
Expand Down Expand Up @@ -44,6 +44,7 @@ pub struct VirtualStakingContract<'a> {
#[contract]
#[error(ContractError)]
#[messages(virtual_staking_api as VirtualStakingApi)]
// #[sv::override_entry_point(sudo=sudo(SudoMsg))] // Disabled because lack of custom query support
impl VirtualStakingContract<'_> {
pub const fn new() -> Self {
Self {
Expand Down Expand Up @@ -134,6 +135,34 @@ impl VirtualStakingContract<'_> {
Ok(resp)
}

/**
* This is called every time there's a change of the active validator set.
*
*/
fn handle_valset_update(
&self,
deps: DepsMut<VirtualStakeCustomQuery>,
additions: &[Validator],
removals: &[Validator],
) -> Result<Response<VirtualStakeCustomMsg>, ContractError> {
// TODO: Store/process removals (and additions) locally, so that they are filtered out from
// the `bonded` list
let _ = removals;

// Send additions to the converter. Removals are considered non-permanent and ignored
let cfg = self.config.load(deps.storage)?;
let msg = converter_api::ExecMsg::ValsetUpdate {
additions: additions.to_vec(),
};
let msg = WasmMsg::Execute {
contract_addr: cfg.converter.to_string(),
msg: to_binary(&msg)?,
funds: vec![],
};
let resp = Response::new().add_message(msg);
Ok(resp)
}

#[msg(reply)]
fn reply(&self, ctx: ReplyCtx, reply: Reply) -> Result<Response, ContractError> {
match (reply.id, reply.result.into_result()) {
Expand Down Expand Up @@ -325,5 +354,9 @@ pub fn sudo(
) -> Result<Response<VirtualStakeCustomMsg>, ContractError> {
match msg {
SudoMsg::Rebalance {} => VirtualStakingContract::new().handle_epoch(deps, env),
SudoMsg::ValsetUpdate {
additions,
removals,
} => VirtualStakingContract::new().handle_valset_update(deps, &additions, &removals),
}
}
2 changes: 2 additions & 0 deletions contracts/consumer/virtual-staking/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pub mod contract;
pub mod error;
pub mod msg;
#[cfg(test)]
mod multitest;
pub mod state;
Loading

0 comments on commit 5c2ea14

Please sign in to comment.