Skip to content

Commit 36de342

Browse files
paritytech-release-backport-bot[bot]serban300github-actions[bot]acatangiu
authored
[stable2412] Backport #8427 (#8449)
Backport #8427 into `stable2412` from serban300. See the [documentation](https://github.com/paritytech/polkadot-sdk/blob/master/docs/BACKPORT.md) on how to use this bot. <!-- # To be used by other automation, do not modify: original-pr-number: #${pull_number} --> Co-authored-by: Serban Iorga <serban@parity.io> Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Adrian Catangiu <adrian@parity.io>
1 parent e2ebe53 commit 36de342

File tree

3 files changed

+114
-29
lines changed

3 files changed

+114
-29
lines changed

prdoc/pr_8427.prdoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
title: 'BEEFY: adjust equivocation slash fraction'
2+
doc:
3+
- audience: Runtime Dev
4+
description: |-
5+
Use 50% slash fraction for fork voting and future block voting (in accordance with the https://eprint.iacr.org/2025/057.pdf paper).
6+
In order to account for the possible risk of accidental/non-malicious double voting, keep the current formula for double voting proof.
7+
crates:
8+
- name: pallet-beefy
9+
bump: patch

substrate/frame/beefy/src/equivocation.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ where
8181
pub validator_set_count: u32,
8282
/// The authority which produced this equivocation.
8383
pub offender: Offender,
84+
/// Optional slash fraction
85+
maybe_slash_fraction: Option<Perbill>,
8486
}
8587

8688
impl<Offender: Clone, N> Offence<Offender> for EquivocationOffence<Offender, N>
@@ -106,10 +108,14 @@ where
106108
self.time_slot
107109
}
108110

109-
// The formula is min((3k / n)^2, 1)
110-
// where k = offenders_number and n = validators_number
111111
fn slash_fraction(&self, offenders_count: u32) -> Perbill {
112-
// Perbill type domain is [0, 1] by definition
112+
if let Some(slash_fraction) = self.maybe_slash_fraction {
113+
return slash_fraction;
114+
}
115+
116+
// `Perbill` type domain is [0, 1] by definition
117+
// The formula is min((3k / n)^2, 1)
118+
// where k = offenders_number and n = validators_number
113119
Perbill::from_rational(3 * offenders_count, self.validator_set_count).square()
114120
}
115121
}
@@ -257,6 +263,14 @@ impl<T: Config> EquivocationEvidenceFor<T> {
257263
},
258264
}
259265
}
266+
267+
fn slash_fraction(&self) -> Option<Perbill> {
268+
match self {
269+
EquivocationEvidenceFor::DoubleVotingProof(_, _) => None,
270+
EquivocationEvidenceFor::ForkVotingProof(_, _) |
271+
EquivocationEvidenceFor::FutureBlockVotingProof(_, _) => Some(Perbill::from_percent(50)),
272+
}
273+
}
260274
}
261275

262276
impl<T, R, P, L> OffenceReportSystem<Option<T::AccountId>, EquivocationEvidenceFor<T>>
@@ -305,6 +319,7 @@ where
305319
reporter: Option<T::AccountId>,
306320
evidence: EquivocationEvidenceFor<T>,
307321
) -> Result<(), DispatchError> {
322+
let maybe_slash_fraction = evidence.slash_fraction();
308323
let reporter = reporter.or_else(|| pallet_authorship::Pallet::<T>::author());
309324

310325
// We check the equivocation within the context of its set id (and associated session).
@@ -333,6 +348,7 @@ where
333348
session_index,
334349
validator_set_count: validator_count,
335350
offender,
351+
maybe_slash_fraction,
336352
};
337353
R::report_offence(reporter.into_iter().collect(), offence)
338354
.map_err(|_| Error::<T>::DuplicateOffenceReport.into())

substrate/frame/beefy/src/tests.rs

Lines changed: 86 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
// limitations under the License.
1717

1818
use codec::Encode;
19-
use std::vec;
19+
use std::{
20+
ops::{Mul, Sub},
21+
vec,
22+
};
2023

2124
use frame_support::{
2225
assert_err, assert_ok,
@@ -32,7 +35,7 @@ use sp_consensus_beefy::{
3235
},
3336
Payload, ValidatorSet, ValidatorSetId, KEY_TYPE as BEEFY_KEY_TYPE,
3437
};
35-
use sp_runtime::DigestItem;
38+
use sp_runtime::{DigestItem, Perbill};
3639
use sp_session::MembershipProof;
3740

3841
use crate::{self as beefy, mock::*, Call, Config, Error, WeightInfoExt};
@@ -309,8 +312,13 @@ fn report_double_voting(
309312
)
310313
}
311314

312-
fn report_equivocation_current_set_works(mut f: impl ReportEquivocationFn) {
315+
fn report_equivocation_current_set_works(
316+
mut f: impl ReportEquivocationFn,
317+
hardcoded_slash_fraction: Option<Perbill>,
318+
) {
313319
let authorities = test_authorities();
320+
let initial_balance = 10_000_000;
321+
let initial_slashable_balance = 10_000;
314322

315323
ExtBuilder::default().add_authorities(authorities).build_and_execute(|| {
316324
assert_eq!(Staking::current_era(), Some(0));
@@ -326,12 +334,16 @@ fn report_equivocation_current_set_works(mut f: impl ReportEquivocationFn) {
326334

327335
// make sure that all validators have the same balance
328336
for validator in &validators {
329-
assert_eq!(Balances::total_balance(validator), 10_000_000);
330-
assert_eq!(Staking::slashable_balance_of(validator), 10_000);
337+
assert_eq!(Balances::total_balance(validator), initial_balance);
338+
assert_eq!(Staking::slashable_balance_of(validator), initial_slashable_balance);
331339

332340
assert_eq!(
333341
Staking::eras_stakers(1, &validator),
334-
pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] },
342+
pallet_staking::Exposure {
343+
total: initial_slashable_balance,
344+
own: initial_slashable_balance,
345+
others: vec![]
346+
},
335347
);
336348
}
337349

@@ -351,8 +363,22 @@ fn report_equivocation_current_set_works(mut f: impl ReportEquivocationFn) {
351363
// check that the balance of 0-th validator is slashed 100%.
352364
let equivocation_validator_id = validators[equivocation_authority_index];
353365

354-
assert_eq!(Balances::total_balance(&equivocation_validator_id), 10_000_000 - 10_000);
355-
assert_eq!(Staking::slashable_balance_of(&equivocation_validator_id), 0);
366+
if let Some(slash_fraction) = hardcoded_slash_fraction {
367+
assert_eq!(
368+
Balances::total_balance(&equivocation_validator_id),
369+
initial_balance - slash_fraction.mul(initial_slashable_balance)
370+
);
371+
assert_eq!(
372+
Staking::slashable_balance_of(&equivocation_validator_id),
373+
Perbill::from_percent(100).sub(slash_fraction).mul(initial_slashable_balance)
374+
);
375+
} else {
376+
assert_eq!(
377+
Balances::total_balance(&equivocation_validator_id),
378+
initial_balance - initial_slashable_balance
379+
);
380+
assert_eq!(Staking::slashable_balance_of(&equivocation_validator_id), 0);
381+
}
356382
assert_eq!(
357383
Staking::eras_stakers(2, &equivocation_validator_id),
358384
pallet_staking::Exposure { total: 0, own: 0, others: vec![] },
@@ -364,19 +390,28 @@ fn report_equivocation_current_set_works(mut f: impl ReportEquivocationFn) {
364390
continue
365391
}
366392

367-
assert_eq!(Balances::total_balance(validator), 10_000_000);
368-
assert_eq!(Staking::slashable_balance_of(validator), 10_000);
393+
assert_eq!(Balances::total_balance(validator), initial_balance);
394+
assert_eq!(Staking::slashable_balance_of(validator), initial_slashable_balance);
369395

370396
assert_eq!(
371397
Staking::eras_stakers(2, &validator),
372-
pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] },
398+
pallet_staking::Exposure {
399+
total: initial_slashable_balance,
400+
own: initial_slashable_balance,
401+
others: vec![]
402+
},
373403
);
374404
}
375405
});
376406
}
377407

378-
fn report_equivocation_old_set_works(mut f: impl ReportEquivocationFn) {
408+
fn report_equivocation_old_set_works(
409+
mut f: impl ReportEquivocationFn,
410+
hardcoded_slash_fraction: Option<Perbill>,
411+
) {
379412
let authorities = test_authorities();
413+
let initial_balance = 10_000_000;
414+
let initial_slashable_balance = 10_000;
380415

381416
ExtBuilder::default().add_authorities(authorities).build_and_execute(|| {
382417
start_era(1);
@@ -398,12 +433,16 @@ fn report_equivocation_old_set_works(mut f: impl ReportEquivocationFn) {
398433

399434
// make sure that all authorities have the same balance
400435
for validator in &validators {
401-
assert_eq!(Balances::total_balance(validator), 10_000_000);
402-
assert_eq!(Staking::slashable_balance_of(validator), 10_000);
436+
assert_eq!(Balances::total_balance(validator), initial_balance);
437+
assert_eq!(Staking::slashable_balance_of(validator), initial_slashable_balance);
403438

404439
assert_eq!(
405440
Staking::eras_stakers(2, &validator),
406-
pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] },
441+
pallet_staking::Exposure {
442+
total: initial_slashable_balance,
443+
own: initial_slashable_balance,
444+
others: vec![]
445+
},
407446
);
408447
}
409448

@@ -421,8 +460,22 @@ fn report_equivocation_old_set_works(mut f: impl ReportEquivocationFn) {
421460
// check that the balance of 0-th validator is slashed 100%.
422461
let equivocation_validator_id = validators[equivocation_authority_index];
423462

424-
assert_eq!(Balances::total_balance(&equivocation_validator_id), 10_000_000 - 10_000);
425-
assert_eq!(Staking::slashable_balance_of(&equivocation_validator_id), 0);
463+
if let Some(slash_fraction) = hardcoded_slash_fraction {
464+
assert_eq!(
465+
Balances::total_balance(&equivocation_validator_id),
466+
initial_balance - slash_fraction.mul(initial_slashable_balance)
467+
);
468+
assert_eq!(
469+
Staking::slashable_balance_of(&equivocation_validator_id),
470+
Perbill::from_percent(100).sub(slash_fraction).mul(initial_slashable_balance)
471+
);
472+
} else {
473+
assert_eq!(
474+
Balances::total_balance(&equivocation_validator_id),
475+
initial_balance - initial_slashable_balance
476+
);
477+
assert_eq!(Staking::slashable_balance_of(&equivocation_validator_id), 0);
478+
}
426479
assert_eq!(
427480
Staking::eras_stakers(3, &equivocation_validator_id),
428481
pallet_staking::Exposure { total: 0, own: 0, others: vec![] },
@@ -434,12 +487,16 @@ fn report_equivocation_old_set_works(mut f: impl ReportEquivocationFn) {
434487
continue
435488
}
436489

437-
assert_eq!(Balances::total_balance(validator), 10_000_000);
438-
assert_eq!(Staking::slashable_balance_of(validator), 10_000);
490+
assert_eq!(Balances::total_balance(validator), initial_balance);
491+
assert_eq!(Staking::slashable_balance_of(validator), initial_slashable_balance);
439492

440493
assert_eq!(
441494
Staking::eras_stakers(3, &validator),
442-
pallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] },
495+
pallet_staking::Exposure {
496+
total: initial_slashable_balance,
497+
own: initial_slashable_balance,
498+
others: vec![]
499+
},
443500
);
444501
}
445502
});
@@ -579,12 +636,12 @@ fn valid_equivocation_reports_dont_pay_fees(mut f: impl ReportEquivocationFn) {
579636

580637
#[test]
581638
fn report_double_voting_current_set_works() {
582-
report_equivocation_current_set_works(report_double_voting);
639+
report_equivocation_current_set_works(report_double_voting, None);
583640
}
584641

585642
#[test]
586643
fn report_double_voting_old_set_works() {
587-
report_equivocation_old_set_works(report_double_voting);
644+
report_equivocation_old_set_works(report_double_voting, None);
588645
}
589646

590647
#[test]
@@ -812,12 +869,12 @@ fn report_fork_voting(
812869

813870
#[test]
814871
fn report_fork_voting_current_set_works() {
815-
report_equivocation_current_set_works(report_fork_voting);
872+
report_equivocation_current_set_works(report_fork_voting, Some(Perbill::from_percent(50)));
816873
}
817874

818875
#[test]
819876
fn report_fork_voting_old_set_works() {
820-
report_equivocation_old_set_works(report_fork_voting);
877+
report_equivocation_old_set_works(report_fork_voting, Some(Perbill::from_percent(50)));
821878
}
822879

823880
#[test]
@@ -1007,12 +1064,15 @@ fn report_future_block_voting(
10071064

10081065
#[test]
10091066
fn report_future_block_voting_current_set_works() {
1010-
report_equivocation_current_set_works(report_future_block_voting);
1067+
report_equivocation_current_set_works(
1068+
report_future_block_voting,
1069+
Some(Perbill::from_percent(50)),
1070+
);
10111071
}
10121072

10131073
#[test]
10141074
fn report_future_block_voting_old_set_works() {
1015-
report_equivocation_old_set_works(report_future_block_voting);
1075+
report_equivocation_old_set_works(report_future_block_voting, Some(Perbill::from_percent(50)));
10161076
}
10171077

10181078
#[test]

0 commit comments

Comments
 (0)