diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 796782a102..34cb8eb749 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -82,8 +82,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 233, - impl_version: 1, + spec_version: 234, + impl_version: 0, apis: RUNTIME_API_VERSIONS, }; diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 627cd1c2a1..5226f5a3b3 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -928,6 +928,8 @@ decl_error! { InvalidEraToReward, /// Invalid number of nominations. InvalidNumberOfNominations, + /// Items are not sorted and unique. + NotSortedAndUnique, } } @@ -1327,21 +1329,15 @@ decl_module! { .map(|_| ()) .or_else(ensure_root)?; - let mut slash_indices = slash_indices; - slash_indices.sort_unstable(); + ensure!(!slash_indices.is_empty(), Error::::EmptyTargets); + ensure!(Self::is_sorted_and_unique(&slash_indices), Error::::NotSortedAndUnique); + let mut unapplied = ::UnappliedSlashes::get(&era); + let last_item = slash_indices[slash_indices.len() - 1]; + ensure!((last_item as usize) < unapplied.len(), Error::::InvalidSlashIndex); for (removed, index) in slash_indices.into_iter().enumerate() { - let index = index as usize; - - // if `index` is not duplicate, `removed` must be <= index. - ensure!(removed <= index, Error::::DuplicateIndex); - - // all prior removals were from before this index, since the - // list is sorted. - let index = index - removed; - ensure!(index < unapplied.len(), Error::::InvalidSlashIndex); - + let index = (index as usize) - removed; unapplied.remove(index); } @@ -1411,7 +1407,7 @@ decl_module! { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; ensure!( - ledger.unlocking.len() > 0, + !ledger.unlocking.is_empty(), Error::::NoUnlockChunk, ); @@ -1460,6 +1456,11 @@ impl Module { Self::bonded(stash).and_then(Self::ledger).map(|l| l.active).unwrap_or_default() } + /// Check that list is sorted and has no duplicates. + fn is_sorted_and_unique(list: &Vec) -> bool { + list.windows(2).all(|w| w[0] < w[1]) + } + // MUTABLES (DANGEROUS) fn do_payout_nominator(who: T::AccountId, era: EraIndex, validators: Vec<(T::AccountId, u32)>) diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs index 507b5591d5..00a8f854db 100644 --- a/substrate/frame/staking/src/tests.rs +++ b/substrate/frame/staking/src/tests.rs @@ -2616,7 +2616,13 @@ fn remove_deferred() { 1, ); - Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![0]).unwrap(); + // fails if empty + assert_noop!( + Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![]), + Error::::EmptyTargets + ); + + assert_ok!(Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![0])); assert_eq!(Balances::free_balance(11), 1000); assert_eq!(Balances::free_balance(101), 2000); @@ -2689,12 +2695,51 @@ fn remove_multi_deferred() { &[Perbill::from_percent(25)], ); - assert_eq!(::UnappliedSlashes::get(&1).len(), 3); - Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![0, 2]).unwrap(); + on_offence_now( + &[ + OffenceDetails { + offender: (42, exposure.clone()), + reporters: vec![], + }, + ], + &[Perbill::from_percent(25)], + ); + + on_offence_now( + &[ + OffenceDetails { + offender: (69, exposure.clone()), + reporters: vec![], + }, + ], + &[Perbill::from_percent(25)], + ); + + assert_eq!(::UnappliedSlashes::get(&1).len(), 5); + + // fails if list is not sorted + assert_noop!( + Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![2, 0, 4]), + Error::::NotSortedAndUnique + ); + // fails if list is not unique + assert_noop!( + Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![0, 2, 2]), + Error::::NotSortedAndUnique + ); + // fails if bad index + assert_noop!( + Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![1, 2, 3, 4, 5]), + Error::::InvalidSlashIndex + ); + + assert_ok!(Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![0, 2, 4])); let slashes = ::UnappliedSlashes::get(&1); - assert_eq!(slashes.len(), 1); + assert_eq!(slashes.len(), 2); + println!("Slashes: {:?}", slashes); assert_eq!(slashes[0].validator, 21); + assert_eq!(slashes[1].validator, 42); }) } @@ -2994,4 +3039,3 @@ fn set_history_depth_works() { assert!(!::ErasTotalStake::contains_key(10 - 5)); }); } -