Skip to content

Commit 9beccb6

Browse files
authored
Merge pull request #137 from osmosis-labs/f/slash-jailed-validators
F/slash jailed validators
2 parents a2583a5 + 808c1d2 commit 9beccb6

File tree

7 files changed

+83
-22
lines changed

7 files changed

+83
-22
lines changed

contracts/consumer/converter/src/contract.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use mesh_apis::price_feed_api;
1414
use mesh_apis::virtual_staking_api;
1515

1616
use crate::error::ContractError;
17-
use crate::ibc::{add_validators_msg, make_ibc_packet, tombstone_validators_msg, IBC_CHANNEL};
17+
use crate::ibc::{
18+
add_validators_msg, jail_validators_msg, make_ibc_packet, tombstone_validators_msg, IBC_CHANNEL,
19+
};
1820
use crate::msg::ConfigResponse;
1921
use crate::state::Config;
2022

@@ -352,13 +354,15 @@ impl ConverterApi for ConverterContract<'_> {
352354
ctx: ExecCtx,
353355
additions: Vec<Validator>,
354356
tombstoned: Vec<String>,
357+
jailed: Vec<String>,
355358
) -> Result<Response, Self::Error> {
356359
self.ensure_authorized(&ctx.deps, &ctx.info)?;
357360

358361
// Send over IBC to the Consumer
359362
let channel = IBC_CHANNEL.load(ctx.deps.storage)?;
360363
let add_msg = add_validators_msg(&ctx.env, &channel, &additions)?;
361364
let tomb_msg = tombstone_validators_msg(&ctx.env, &channel, &tombstoned)?;
365+
let jail_msg = jail_validators_msg(&ctx.env, &channel, &jailed)?;
362366

363367
let event = Event::new("valset_update").add_attribute(
364368
"additions",
@@ -368,11 +372,13 @@ impl ConverterApi for ConverterContract<'_> {
368372
.collect::<Vec<String>>()
369373
.join(","),
370374
);
375+
let event = event.add_attribute("jailed", jailed.join(","));
371376
let event = event.add_attribute("tombstoned", tombstoned.join(","));
372377
let resp = Response::new()
373378
.add_event(event)
374379
.add_message(add_msg)
375-
.add_message(tomb_msg);
380+
.add_message(tomb_msg)
381+
.add_message(jail_msg);
376382

377383
Ok(resp)
378384
}

contracts/consumer/converter/src/ibc.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,29 @@ pub(crate) fn add_validators_msg(
146146
Ok(msg)
147147
}
148148

149+
pub(crate) fn jail_validators_msg(
150+
env: &Env,
151+
channel: &IbcChannel,
152+
validators: &[String],
153+
) -> Result<IbcMsg, ContractError> {
154+
let packet = ConsumerPacket::JailValidators(
155+
validators
156+
.iter()
157+
.map(|v| RemoveValidator {
158+
valoper: v.to_string(),
159+
height: env.block.height,
160+
time: env.block.time.seconds(),
161+
})
162+
.collect(),
163+
);
164+
let msg = IbcMsg::SendPacket {
165+
channel_id: channel.endpoint.channel_id.clone(),
166+
data: to_binary(&packet)?,
167+
timeout: packet_timeout_validator(env),
168+
};
169+
Ok(msg)
170+
}
171+
149172
pub(crate) fn tombstone_validators_msg(
150173
env: &Env,
151174
channel: &IbcChannel,
@@ -156,8 +179,8 @@ pub(crate) fn tombstone_validators_msg(
156179
.iter()
157180
.map(|v| RemoveValidator {
158181
valoper: v.to_string(),
159-
end_height: env.block.height,
160-
end_time: env.block.time.seconds(),
182+
height: env.block.height,
183+
time: env.block.time.seconds(),
161184
})
162185
.collect(),
163186
);

contracts/consumer/converter/src/multitest.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -243,13 +243,13 @@ fn valset_update_works() {
243243
// Check that only the virtual staking contract can call this handler
244244
let res = converter
245245
.converter_api_proxy()
246-
.valset_update(vec![], vec![])
246+
.valset_update(vec![], vec![], vec![])
247247
.call(owner);
248248
assert_eq!(res.unwrap_err(), Unauthorized {});
249249

250250
let res = converter
251251
.converter_api_proxy()
252-
.valset_update(add_validators, rem_validators)
252+
.valset_update(add_validators, rem_validators, vec![])
253253
.call(virtual_staking.contract_addr.as_ref());
254254

255255
// This fails because of lack of IBC support in mt now.
@@ -304,7 +304,7 @@ fn unauthorized() {
304304

305305
let err = converter
306306
.converter_api_proxy()
307-
.valset_update(vec![], vec![])
307+
.valset_update(vec![], vec![], vec![])
308308
.call("mallory")
309309
.unwrap_err();
310310

contracts/consumer/virtual-staking/src/contract.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,13 @@ impl VirtualStakingContract<'_> {
156156
// the `bonded` list
157157
let _ = (removals, updated, jailed, unjailed);
158158

159-
// Send additions and tombstones to the Converter. Removals are non-permanent and ignored
160-
// TODO: Send jailed even when they are non-permanent, for slashing
161-
// FIXME: Account for tombstoned validators `bond_requests`. Add a `slashings` field to the config,
162-
// and avoid unbondings of slashed amounts.
163-
// FIXME: Account for jailed validators `bond_requests`. avoid unbondings of slashed
164-
// amounts. Requires computing the slashing amount, i.e. obtaining the chain's slash_ratio.
159+
// Send additions and tombstones to the Converter. Removals are non-permanent and ignored.
160+
// Send jailed even when they are non-permanent, for slashing.
165161
let cfg = self.config.load(deps.storage)?;
166162
let msg = converter_api::ExecMsg::ValsetUpdate {
167163
additions: additions.to_vec(),
168164
tombstoned: tombstoned.to_vec(),
165+
jailed: jailed.to_vec(),
169166
};
170167
let msg = WasmMsg::Execute {
171168
contract_addr: cfg.converter.to_string(),

contracts/provider/external-staking/src/ibc.rs

+30-4
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use cosmwasm_std::{
99
use cw_storage_plus::Item;
1010
use mesh_apis::ibc::{
1111
ack_success, validate_channel_order, AckWrapper, AddValidator, AddValidatorsAck,
12-
ConsumerPacket, DistributeAck, ProtocolVersion, ProviderPacket, RemoveValidator,
13-
RemoveValidatorsAck,
12+
ConsumerPacket, DistributeAck, JailValidatorsAck, ProtocolVersion, ProviderPacket,
13+
RemoveValidator, RemoveValidatorsAck,
1414
};
1515

1616
use crate::contract::ExternalStakingContract;
@@ -149,8 +149,8 @@ pub fn ibc_packet_receive(
149149
let mut msgs = vec![];
150150
for RemoveValidator {
151151
valoper,
152-
end_height,
153-
end_time: _end_time,
152+
height: end_height,
153+
time: _end_time,
154154
} in to_remove
155155
{
156156
// Check that the validator is active at height and slash it if that is the case
@@ -170,6 +170,32 @@ pub fn ibc_packet_receive(
170170
let ack = ack_success(&RemoveValidatorsAck {})?;
171171
IbcReceiveResponse::new().set_ack(ack).add_messages(msgs)
172172
}
173+
ConsumerPacket::JailValidators(to_jail) => {
174+
let mut msgs = vec![];
175+
for RemoveValidator {
176+
valoper,
177+
height: end_height,
178+
time: _end_time,
179+
} in to_jail
180+
{
181+
// Check that the validator is active at height and slash it if that is the case
182+
let active = contract.val_set.is_active_validator_at_height(
183+
deps.storage,
184+
&valoper,
185+
end_height,
186+
)?;
187+
// We don't change the validator's state here, as that's currently not supported
188+
// (only Active and Tombstoned)
189+
if active {
190+
// slash the validator
191+
// TODO: Slash with a different slash ratio! (downtime / offline slash ratio)
192+
let msg = contract.handle_slashing(&env, deps.storage, &valoper)?;
193+
msgs.push(msg);
194+
}
195+
}
196+
let ack = ack_success(&JailValidatorsAck {})?;
197+
IbcReceiveResponse::new().set_ack(ack).add_messages(msgs)
198+
}
173199
ConsumerPacket::Distribute { validator, rewards } => {
174200
let contract = ExternalStakingContract::new();
175201
let evt = contract.distribute_rewards(deps, &validator, rewards)?;

packages/apis/src/converter_api.rs

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub trait ConverterApi {
4242
ctx: ExecCtx,
4343
additions: Vec<Validator>,
4444
tombstoned: Vec<String>,
45+
jailed: Vec<String>,
4546
) -> Result<Response, Self::Error>;
4647
}
4748

packages/apis/src/ibc/packet.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ pub enum ConsumerPacket {
6565
/// but when it is no longer a valid target to delegate to.
6666
/// It contains a list of `valoper_address` to be removed, along with the removal's height.
6767
RemoveValidators(Vec<RemoveValidator>),
68+
/// This is sent when a validator is jailed.
69+
/// It contains a list of `valoper_address` to be slashed for temporary jailing, along with the
70+
/// jail event's block height.
71+
JailValidators(Vec<RemoveValidator>),
6872
/// This is part of the rewards protocol
6973
Distribute {
7074
/// The validator whose stakers should receive these rewards
@@ -117,15 +121,15 @@ pub struct RemoveValidator {
117121
/// This is the validator operator (valoper) address used for delegations and rewards
118122
pub valoper: String,
119123

120-
/// This is the height the validator is being tombstoned.
121-
/// It is used to detect slashing conditions, that is, avoid slashing an already tombstoned
124+
/// This is the height the validator is being removed.
125+
/// It is used to detect slashing conditions, that is, avoid slashing an already jailed or tombstoned
122126
/// validator.
123-
pub end_height: u64,
127+
pub height: u64,
124128

125-
/// This is the timestamp of the block the validator was tombstoned.
129+
/// This is the timestamp of the block the validator was removed.
126130
/// It may be used for unbonding_period issues, maybe just for informational purposes.
127131
/// Stored as unix seconds.
128-
pub end_time: u64,
132+
pub time: u64,
129133
}
130134

131135
/// Ack sent for ConsumerPacket::AddValidators
@@ -136,6 +140,10 @@ pub struct AddValidatorsAck {}
136140
#[cw_serde]
137141
pub struct RemoveValidatorsAck {}
138142

143+
/// Ack sent for ConsumerPacket::JailValidators
144+
#[cw_serde]
145+
pub struct JailValidatorsAck {}
146+
139147
/// Ack sent for ConsumerPacket::Distribute
140148
#[cw_serde]
141149
pub struct DistributeAck {}

0 commit comments

Comments
 (0)