Skip to content

Commit

Permalink
Restore original jailing logic
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanfrey committed May 10, 2024
1 parent 3fa51d7 commit 19b17c2
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 76 deletions.
10 changes: 7 additions & 3 deletions contracts/consumer/converter/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use cosmwasm_std::{
ensure_eq, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, Deps, DepsMut, Event, Fraction, MessageInfo, Reply, Response, StdError, SubMsg, SubMsgResponse, Uint128, Validator, WasmMsg
ensure_eq, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, Deps, DepsMut, Event,
Fraction, MessageInfo, Reply, Response, StdError, SubMsg, SubMsgResponse, Uint128, Validator,
WasmMsg,
};
use cw2::set_contract_version;
use cw_storage_plus::Item;
Expand Down Expand Up @@ -278,7 +280,8 @@ impl ConverterContract<'_> {
use sylvia::types::Remote;
// NOTE: Jan, this feels hacky... I'm not sure if this is the right way to do it
// Also, I am sticking in a random error type here, not what I will get (which is unknown)
let remote = Remote::<&dyn price_feed_api::PriceFeedApi<Error=StdError>>::new(config.price_feed);
let remote =
Remote::<&dyn price_feed_api::PriceFeedApi<Error = StdError>>::new(config.price_feed);
let price = remote.querier(&deps.querier).price()?.native_per_foreign;
let converted = (amount.amount * price) * config.price_adjustment;

Expand All @@ -305,7 +308,8 @@ impl ConverterContract<'_> {
use sylvia::types::Remote;
// NOTE: Jan, this feels hacky... I'm not sure if this is the right way to do it
// Also, I am sticking in a random error type here, not what I will get (which is unknown)
let remote = Remote::<&dyn price_feed_api::PriceFeedApi<Error=StdError>>::new(config.price_feed);
let remote =
Remote::<&dyn price_feed_api::PriceFeedApi<Error = StdError>>::new(config.price_feed);
let price = remote.querier(&deps.querier).price()?.native_per_foreign;
let converted = (amount.amount * price.inv().ok_or(ContractError::InvalidPrice {})?)
* config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ pub struct ConfigResponse {

impl VirtualStakingApi for VirtualStakingMock<'_> {
type Error = ContractError;
type ExecC = cosmwasm_std::Empty;
type QueryC = cosmwasm_std::Empty;

/// Requests to bond tokens to a validator. This will be actually handled at the next epoch.
/// If the virtual staking module is over the max cap, it will trigger a rebalance.
Expand Down
1 change: 0 additions & 1 deletion contracts/consumer/virtual-staking/src/multitest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ fn instantiation() {
#[test]
#[ignore] // FIXME: Enable / finish this test once custom query support is added to sylvia
fn valset_update_sudo() {

let app = new_app();

let owner = "sunny"; // Owner of the staking contract (i. e. the vault contract)
Expand Down
88 changes: 86 additions & 2 deletions contracts/provider/native-staking/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use cosmwasm_std::Order::Ascending;
use cosmwasm_std::{
from_json, Addr, Decimal, DepsMut, Reply, Response, StdResult, SubMsgResponse, WasmMsg,
from_json, Addr, Decimal, DepsMut, Event, Reply, Response, StdResult, SubMsgResponse, WasmMsg,
};
use cw2::set_contract_version;
use cw_storage_plus::{Item, Map};
use cw_utils::parse_instantiate_response_data;
use sylvia::types::{InstantiateCtx, QueryCtx, ReplyCtx};
use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, ReplyCtx, SudoCtx};
use sylvia::{contract, schemars};

use mesh_apis::local_staking_api;
Expand Down Expand Up @@ -79,6 +79,46 @@ impl NativeStakingContract<'_> {
Ok(Response::new())
}

/// This is called every time there's a change of the active validator set that implies slashing.
/// In test code, this is called from `test_handle_jailing`.
/// In non-test code, this is called from `sudo`.
fn handle_jailing(
&self,
mut deps: DepsMut,
jailed: Option<Vec<String>>,
tombstoned: Option<Vec<String>>,
) -> Result<Response, ContractError> {
let jailed = &jailed.unwrap_or_default();
let tombstoned = &tombstoned.unwrap_or_default();

let cfg = self.config.load(deps.storage)?;
let mut msgs = vec![];
for validator in tombstoned {
// Slash the validator (if bonded)
let slash_msg =
self.handle_slashing(&mut deps, &cfg, validator, SlashingReason::DoubleSign)?;
if let Some(msg) = slash_msg {
msgs.push(msg)
}
}
for validator in jailed {
// Slash the validator (if bonded)
let slash_msg =
self.handle_slashing(&mut deps, &cfg, validator, SlashingReason::Offline)?;
if let Some(msg) = slash_msg {
msgs.push(msg)
}
}
let mut evt = Event::new("jailing");
if !jailed.is_empty() {
evt = evt.add_attribute("jailed", jailed.join(","));
}
if !tombstoned.is_empty() {
evt = evt.add_attribute("tombstoned", tombstoned.join(","));
}
Ok(Response::new().add_event(evt).add_messages(msgs))
}

pub(crate) fn handle_slashing(
&self,
deps: &mut DepsMut,
Expand Down Expand Up @@ -193,4 +233,48 @@ impl NativeStakingContract<'_> {
owner: owner_addr.to_string(),
})
}

/// Jails validators temporarily or permanently.
/// Method used for test only.
#[sv::msg(exec)]
fn test_handle_jailing(
&self,
ctx: ExecCtx,
jailed: Vec<String>,
tombstoned: Vec<String>,
) -> Result<Response, ContractError> {
#[cfg(any(feature = "mt", test))]
{
let jailed = if jailed.is_empty() {
None
} else {
Some(jailed)
};
let tombstoned = if tombstoned.is_empty() {
None
} else {
Some(tombstoned)
};
NativeStakingContract::new().handle_jailing(ctx.deps, jailed, tombstoned)
}
#[cfg(not(any(feature = "mt", test)))]
{
let _ = (ctx, jailed, tombstoned);
Err(ContractError::Unauthorized {})
}
}

/// `SudoMsg::Jailing` should be called every time there's a validator set update that implies
/// slashing.
/// - Temporary removal of a validator from the active set due to jailing.
/// - Permanent removal (i.e. tombstoning) of a validator from the active set.
#[sv::msg(sudo)]
fn jailing(
&self,
ctx: SudoCtx,
jailed: Option<Vec<String>>,
tombstoned: Option<Vec<String>>,
) -> Result<Response, ContractError> {
self.handle_jailing(ctx.deps, jailed, tombstoned)
}
}
48 changes: 3 additions & 45 deletions contracts/provider/native-staking/src/local_staking_api.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use cosmwasm_std::{
ensure_eq, from_json, to_json_binary, Binary, Coin, Event, Response, SubMsg, WasmMsg,
};
use cosmwasm_std::{ensure_eq, from_json, to_json_binary, Binary, Coin, Response, SubMsg, WasmMsg};
use cw_utils::{must_pay, nonpayable};
use sylvia::types::{ExecCtx, QueryCtx, SudoCtx};
use sylvia::types::{ExecCtx, QueryCtx};

#[allow(unused_imports)]
use mesh_apis::local_staking_api::{self, LocalStakingApi, SlashRatioResponse};

use crate::contract::{NativeStakingContract, SlashingReason, REPLY_ID_INSTANTIATE};
use crate::contract::{NativeStakingContract, REPLY_ID_INSTANTIATE};
use crate::error::ContractError;
use crate::msg::StakeMsg;

Expand Down Expand Up @@ -122,46 +120,6 @@ impl LocalStakingApi for NativeStakingContract<'_> {
}
}

/// This is called every time there's a change of the active validator set that implies slashing.
/// In test code, this is called from `test_handle_jailing`.
/// In non-test code, this is called from `sudo`.
fn jailing(
&self,
mut ctx: SudoCtx,
jailed: Option<Vec<String>>,
tombstoned: Option<Vec<String>>,
) -> Result<Response, ContractError> {
let jailed = &jailed.unwrap_or_default();
let tombstoned = &tombstoned.unwrap_or_default();

let cfg = self.config.load(ctx.deps.storage)?;
let mut msgs = vec![];
for validator in tombstoned {
// Slash the validator (if bonded)
let slash_msg =
self.handle_slashing(&mut ctx.deps, &cfg, validator, SlashingReason::DoubleSign)?;
if let Some(msg) = slash_msg {
msgs.push(msg)
}
}
for validator in jailed {
// Slash the validator (if bonded)
let slash_msg =
self.handle_slashing(&mut ctx.deps, &cfg, validator, SlashingReason::Offline)?;
if let Some(msg) = slash_msg {
msgs.push(msg)
}
}
let mut evt = Event::new("jailing");
if !jailed.is_empty() {
evt = evt.add_attribute("jailed", jailed.join(","));
}
if !tombstoned.is_empty() {
evt = evt.add_attribute("tombstoned", tombstoned.join(","));
}
Ok(Response::new().add_event(evt).add_messages(msgs))
}

/// Returns the maximum percentage that can be slashed
fn max_slash(&self, ctx: QueryCtx) -> Result<SlashRatioResponse, Self::Error> {
let Config {
Expand Down
10 changes: 0 additions & 10 deletions contracts/provider/vault/src/multitest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2599,11 +2599,6 @@ fn cross_slash_pending_unbonding() {

/// Scenario 7:
/// Same as scenario 1 but with native slashing, because of double-signing.
///
/// TODO: re-enable. It requires `test_handle_jailing` to be implemented, which is
/// also referenced in native-staking::local_staking_apis::jailing but doesn't seem to be
/// actually implemented anywhere.
#[cfg(feature = "todo")]
#[test]
fn native_slashing_tombstoning() {
let owner = "owner";
Expand Down Expand Up @@ -2728,11 +2723,6 @@ fn native_slashing_tombstoning() {

/// Scenario 8:
/// Same as scenario 1 but with native slashing, because of offline.
///
/// TODO: re-enable. It requires `test_handle_jailing` to be implemented, which is
/// also referenced in native-staking::local_staking_apis::jailing but doesn't seem to be
/// actually implemented anywhere.
#[cfg(feature = "todo")]
#[test]
fn native_slashing_jailing() {
let owner = "owner";
Expand Down
14 changes: 1 addition & 13 deletions packages/apis/src/local_staking_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use cosmwasm_schema::cw_serde;
use cosmwasm_std::{
to_json_binary, Addr, Binary, Coin, Decimal, Deps, Response, StdError, WasmMsg,
};
use sylvia::types::{ExecCtx, QueryCtx, SudoCtx};
use sylvia::types::{ExecCtx, QueryCtx};
use sylvia::{interface, schemars};

#[cw_serde]
Expand Down Expand Up @@ -49,18 +49,6 @@ pub trait LocalStakingApi {
/// Returns the maximum percentage that can be slashed
#[sv::msg(query)]
fn max_slash(&self, ctx: QueryCtx) -> Result<SlashRatioResponse, Self::Error>;

/// `SudoMsg::Jailing` should be called every time there's a validator set update that implies
/// slashing.
/// - Temporary removal of a validator from the active set due to jailing.
/// - Permanent removal (i.e. tombstoning) of a validator from the active set.
#[sv::msg(sudo)]
fn jailing(
&self,
ctx: SudoCtx,
jailed: Option<Vec<String>>,
tombstoned: Option<Vec<String>>,
) -> Result<Response, Self::Error>;
}

#[cw_serde]
Expand Down
12 changes: 10 additions & 2 deletions packages/apis/src/virtual_staking_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ pub trait VirtualStakingApi {
/// If the virtual staking module is over the max cap, it will trigger a rebalance.
/// If the max cap is 0, then this will immediately return an error.
#[sv::msg(exec)]
fn bond(&self, ctx: ExecCtx<Self::QueryC>, validator: String, amount: Coin) -> Result<Response<Self::ExecC>, Self::Error>;
fn bond(
&self,
ctx: ExecCtx<Self::QueryC>,
validator: String,
amount: Coin,
) -> Result<Response<Self::ExecC>, Self::Error>;

/// Requests to unbond tokens from a validator. This will be actually handled at the next epoch.
/// If the virtual staking module is over the max cap, it will trigger a rebalance in addition to unbond.
Expand Down Expand Up @@ -49,7 +54,10 @@ pub trait VirtualStakingApi {
///
/// It should also withdraw all pending rewards here, and send them to the converter contract.
#[sv::msg(sudo)]
fn handle_epoch(&self, ctx: SudoCtx<Self::QueryC>) -> Result<Response<Self::ExecC>, Self::Error>;
fn handle_epoch(
&self,
ctx: SudoCtx<Self::QueryC>,
) -> Result<Response<Self::ExecC>, Self::Error>;

/// SudoMsg::ValsetUpdate{} should be called every time there's a validator set update:
/// - Addition of a new validator to the active validator set.
Expand Down

0 comments on commit 19b17c2

Please sign in to comment.