Skip to content

Commit 0334202

Browse files
committed
feat(slashing): lazyly slash reluctant block proposers
1 parent ccbb294 commit 0334202

File tree

4 files changed

+48
-41
lines changed

4 files changed

+48
-41
lines changed

data_structures/src/staking/stake.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ where
158158
}
159159

160160
/// Set the epoch for a certain capability. Most normally, the epoch is the current epoch.
161-
pub fn reset_age(&mut self, capability: Capability, current_epoch: Epoch) {
162-
self.epochs.update(capability, current_epoch);
161+
pub fn reset_age(&mut self, capability: Capability, reset_epoch: Epoch) {
162+
self.epochs.update(capability, reset_epoch);
163163
}
164164
}
165165

data_structures/src/staking/stakes.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,50 @@ where
413413
}
414414
}
415415

416+
/// First, order stake entries by mining power rank, as to find first occurance for given validator.
417+
/// Once found, update the entry's mining epoch on that stake entry and all others with a better mining rank.
418+
/// The better the rank, the more in the future will the entry's next mining epoch be set to.
419+
pub fn reset_mining_age<ISK>(
420+
&mut self,
421+
validator: ISK,
422+
current_epoch: Epoch
423+
) -> StakesResult<(), Address, Coins, Epoch>
424+
where
425+
ISK: Into<Address>,
426+
{
427+
let validator = validator.into();
428+
429+
// order mining stake entries by rank, as for given current_epoch:
430+
let mut by_rank = self.by_rank(Capability::Mining, current_epoch);
431+
432+
// locate first entry whose validator matches the one searched for:
433+
let winner_rank = by_rank.position(move |(key, _)| key.validator == validator);
434+
435+
if let Some(winner_rank) = winner_rank {
436+
let stakers: Vec<StakeKey<Address>> = by_rank
437+
.take(winner_rank + 1)
438+
.map(|(key, _)| key)
439+
.collect();
440+
// proportionally reset coin age on located entry and all those with a better mining rank:
441+
let mut index: usize = 0;
442+
stakers.iter().for_each(|key| {
443+
let stake_entry = self.by_key.get_mut(key);
444+
if let Some(stake_entry) = stake_entry {
445+
let penalty_epochs = Epoch::from((1 + winner_rank - index) as u32);
446+
log::debug!("Delaying {} as block candidate during +{} epochs", key, penalty_epochs);
447+
stake_entry
448+
.value
449+
.write()
450+
.unwrap()
451+
.reset_age(Capability::Mining, current_epoch + penalty_epochs);
452+
}
453+
index += 1;
454+
});
455+
}
456+
457+
Ok(())
458+
}
459+
416460
/// Set the epoch for a certain address and capability. Most normally, the epoch is the current
417461
/// epoch.
418462
pub fn reset_age<ISK>(

node/src/actors/chain_manager/handlers.rs

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@ use actix::{prelude::*, ActorFutureExt, WrapFuture};
1010
use futures::future::Either;
1111

1212
use witnet_data_structures::{
13-
capabilities::Capability,
1413
chain::{
1514
tapi::ActiveWips, Block, ChainInfo, ChainState, CheckpointBeacon, DataRequestInfo, Epoch,
1615
Hash, Hashable, NodeStats, PublicKeyHash, SuperBlockVote, SupplyInfo, ValueTransferOutput,
1716
},
1817
error::{ChainInfoError, TransactionError::DataRequestNotFound},
1918
get_protocol_version,
20-
proto::versioning::ProtocolVersion,
2119
refresh_protocol_version,
2220
staking::{errors::StakesError, prelude::StakeKey},
2321
transaction::{
@@ -167,7 +165,6 @@ impl Handler<EpochNotification<EveryEpochPayload>> for ChainManager {
167165
match self.chain_state {
168166
ChainState {
169167
reputation_engine: Some(_),
170-
ref mut stakes,
171168
..
172169
} => {
173170
if self.epoch_constants.is_none() || self.vrf_ctx.is_none() {
@@ -186,35 +183,6 @@ impl Handler<EpochNotification<EveryEpochPayload>> for ChainManager {
186183
{
187184
// Persist block and update ChainState
188185
self.consolidate_block(ctx, block, utxo_diff, priorities, false);
189-
} else if msg.checkpoint > 0 {
190-
let previous_epoch = msg.checkpoint - 1;
191-
log::warn!(
192-
"There was no valid block candidate to consolidate for epoch {}",
193-
previous_epoch
194-
);
195-
if ProtocolVersion::from_epoch(previous_epoch) == ProtocolVersion::V2_0
196-
{
197-
let replication_factor = self
198-
.consensus_constants_wit2
199-
.get_replication_factor(previous_epoch);
200-
let rank_subset: Vec<_> = stakes
201-
.rank(Capability::Mining, previous_epoch)
202-
.take(replication_factor.into())
203-
.collect();
204-
for (i, (validator, _)) in rank_subset.into_iter().enumerate() {
205-
log::warn!(
206-
"Slashed the power of {} as it did not propose a block",
207-
validator
208-
);
209-
let _ = stakes.reset_age(
210-
validator,
211-
Capability::Mining,
212-
msg.checkpoint,
213-
// This should never fail
214-
(i + 1).try_into().unwrap(),
215-
);
216-
}
217-
}
218186
}
219187

220188
// Send last beacon on block consolidation

node/src/actors/chain_manager/mod.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,13 +1231,8 @@ impl ChainManager {
12311231

12321232
// IMPORTANT: Always perform age resets after adding rewards
12331233

1234-
// Reset mining power for miner
1235-
log::debug!(
1236-
"Resetting mining age for {} to {}",
1237-
miner_pkh,
1238-
block_epoch + 1,
1239-
);
1240-
let _ = stakes.reset_age(miner_pkh, Capability::Mining, block_epoch + 1, 1);
1234+
// Reset mining power to block proposer, and higher ranked eligible candidates:
1235+
let _ = stakes.reset_mining_age(miner_pkh, block_epoch);
12411236

12421237
// Reset witnessing power
12431238
for co_tx in &block.txns.commit_txns {

0 commit comments

Comments
 (0)