From 32900294ba6bd73a6c3d882e2064665ae3200368 Mon Sep 17 00:00:00 2001
From: Alexander Popiak <alexander.popiak@parity.io>
Date: Mon, 11 Oct 2021 16:23:10 +0200
Subject: [PATCH] Add Deposit and Withdraw Events to Balances Pallet (#9425)

* add Deposit and Withdraw events to balances

+ add deposit_event() calls where appropriate to signal fund movement
+ adjust and extend tests

* line length

* move events to the end to avoid changing indices

* bump spec_version

* cargo fmt

* adjust block import bench to new event count

* fix node executor tests

* adjust import bench comment

* fix typo and formatting

* adjust event number

* fix copy pasta

* fix contracts pallets tests

* cargo fmt

* WIP fix events in tests

* fix offences tests

* fix tests

* cargo +nightly fmt

* fix contracts pallets tests

* cargo +nightly fmt

* fix offences tests

* formatting and compile fixes

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
---
 substrate/bin/node/bench/src/import.rs        |  18 ++-
 substrate/bin/node/executor/tests/basic.rs    |  39 +++++
 substrate/bin/node/runtime/src/lib.rs         |   4 +-
 substrate/frame/balances/src/lib.rs           |  38 ++++-
 substrate/frame/balances/src/tests.rs         |  20 ++-
 substrate/frame/balances/src/tests_local.rs   |   3 +-
 .../frame/balances/src/tests_reentrancy.rs    |  15 +-
 substrate/frame/contracts/src/tests.rs        |   5 +
 .../frame/offences/benchmarking/src/lib.rs    | 152 ++++++++++++------
 9 files changed, 218 insertions(+), 76 deletions(-)

diff --git a/substrate/bin/node/bench/src/import.rs b/substrate/bin/node/bench/src/import.rs
index 5bbf1ddf3b7..da9d0cdaf85 100644
--- a/substrate/bin/node/bench/src/import.rs
+++ b/substrate/bin/node/bench/src/import.rs
@@ -135,18 +135,20 @@ impl core::Benchmark for ImportBenchmark {
 			.inspect_state(|| {
 				match self.block_type {
 					BlockType::RandomTransfersKeepAlive => {
-						// should be 5 per signed extrinsic + 1 per unsigned
+						// should be 7 per signed extrinsic + 1 per unsigned
 						// we have 1 unsigned and the rest are signed in the block
-						// those 5 events per signed are:
-						//    - new account (RawEvent::NewAccount) as we always transfer fund to
-						//      non-existant account
-						//    - endowed (RawEvent::Endowed) for this new account
-						//    - successful transfer (RawEvent::Transfer) for this transfer operation
-						//    - deposit event for charging transaction fee
+						// those 7 events per signed are:
+						//    - withdraw (Balances::Withdraw) for charging the transaction fee
+						//    - new account (System::NewAccount) as we always transfer fund to
+						//      non-existent account
+						//    - endowed (Balances::Endowed) for this new account
+						//    - successful transfer (Event::Transfer) for this transfer operation
+						//    - 2x deposit (Balances::Deposit and Treasury::Deposit) for depositing
+						//      the transaction fee into the treasury
 						//    - extrinsic success
 						assert_eq!(
 							node_runtime::System::events().len(),
-							(self.block.extrinsics.len() - 1) * 5 + 1,
+							(self.block.extrinsics.len() - 1) * 7 + 1,
 						);
 					},
 					BlockType::Noop => {
diff --git a/substrate/bin/node/executor/tests/basic.rs b/substrate/bin/node/executor/tests/basic.rs
index c1ab5e5a0fe..bbb9339189b 100644
--- a/substrate/bin/node/executor/tests/basic.rs
+++ b/substrate/bin/node/executor/tests/basic.rs
@@ -385,6 +385,11 @@ fn full_native_block_import_works() {
 				})),
 				topics: vec![],
 			},
+			EventRecord {
+				phase: Phase::ApplyExtrinsic(1),
+				event: Event::Balances(pallet_balances::Event::Withdraw(alice().into(), fees)),
+				topics: vec![],
+			},
 			EventRecord {
 				phase: Phase::ApplyExtrinsic(1),
 				event: Event::Balances(pallet_balances::Event::Transfer(
@@ -394,6 +399,14 @@ fn full_native_block_import_works() {
 				)),
 				topics: vec![],
 			},
+			EventRecord {
+				phase: Phase::ApplyExtrinsic(1),
+				event: Event::Balances(pallet_balances::Event::Deposit(
+					pallet_treasury::Pallet::<Runtime>::account_id(),
+					fees * 8 / 10,
+				)),
+				topics: vec![],
+			},
 			EventRecord {
 				phase: Phase::ApplyExtrinsic(1),
 				event: Event::Treasury(pallet_treasury::Event::Deposit(fees * 8 / 10)),
@@ -439,6 +452,11 @@ fn full_native_block_import_works() {
 				})),
 				topics: vec![],
 			},
+			EventRecord {
+				phase: Phase::ApplyExtrinsic(1),
+				event: Event::Balances(pallet_balances::Event::Withdraw(bob().into(), fees)),
+				topics: vec![],
+			},
 			EventRecord {
 				phase: Phase::ApplyExtrinsic(1),
 				event: Event::Balances(pallet_balances::Event::Transfer(
@@ -448,6 +466,14 @@ fn full_native_block_import_works() {
 				)),
 				topics: vec![],
 			},
+			EventRecord {
+				phase: Phase::ApplyExtrinsic(1),
+				event: Event::Balances(pallet_balances::Event::Deposit(
+					pallet_treasury::Pallet::<Runtime>::account_id(),
+					fees * 8 / 10,
+				)),
+				topics: vec![],
+			},
 			EventRecord {
 				phase: Phase::ApplyExtrinsic(1),
 				event: Event::Treasury(pallet_treasury::Event::Deposit(fees * 8 / 10)),
@@ -461,6 +487,11 @@ fn full_native_block_import_works() {
 				})),
 				topics: vec![],
 			},
+			EventRecord {
+				phase: Phase::ApplyExtrinsic(2),
+				event: Event::Balances(pallet_balances::Event::Withdraw(alice().into(), fees)),
+				topics: vec![],
+			},
 			EventRecord {
 				phase: Phase::ApplyExtrinsic(2),
 				event: Event::Balances(pallet_balances::Event::Transfer(
@@ -470,6 +501,14 @@ fn full_native_block_import_works() {
 				)),
 				topics: vec![],
 			},
+			EventRecord {
+				phase: Phase::ApplyExtrinsic(2),
+				event: Event::Balances(pallet_balances::Event::Deposit(
+					pallet_treasury::Pallet::<Runtime>::account_id(),
+					fees * 8 / 10,
+				)),
+				topics: vec![],
+			},
 			EventRecord {
 				phase: Phase::ApplyExtrinsic(2),
 				event: Event::Treasury(pallet_treasury::Event::Deposit(fees * 8 / 10)),
diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index 4f620976c3a..c7920629bf3 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -121,8 +121,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: 267,
-	impl_version: 1,
+	spec_version: 268,
+	impl_version: 0,
 	apis: RUNTIME_API_VERSIONS,
 	transaction_version: 2,
 };
diff --git a/substrate/frame/balances/src/lib.rs b/substrate/frame/balances/src/lib.rs
index afd2331c8e3..da8019583c3 100644
--- a/substrate/frame/balances/src/lib.rs
+++ b/substrate/frame/balances/src/lib.rs
@@ -463,8 +463,6 @@ pub mod pallet {
 		Transfer(T::AccountId, T::AccountId, T::Balance),
 		/// A balance was set by root. \[who, free, reserved\]
 		BalanceSet(T::AccountId, T::Balance, T::Balance),
-		/// Some amount was deposited (e.g. for transaction fees). \[who, deposit\]
-		Deposit(T::AccountId, T::Balance),
 		/// Some balance was reserved (moved from free to reserved). \[who, value\]
 		Reserved(T::AccountId, T::Balance),
 		/// Some balance was unreserved (moved from reserved to free). \[who, value\]
@@ -473,6 +471,14 @@ pub mod pallet {
 		/// Final argument indicates the destination balance type.
 		/// \[from, to, balance, destination_status\]
 		ReserveRepatriated(T::AccountId, T::AccountId, T::Balance, Status),
+		/// Some amount was deposited into the account (e.g. for transaction fees). \[who,
+		/// deposit\]
+		Deposit(T::AccountId, T::Balance),
+		/// Some amount was withdrawn from the account (e.g. for transaction fees). \[who, value\]
+		Withdraw(T::AccountId, T::Balance),
+		/// Some amount was removed from the account (e.g. for misbehavior). \[who,
+		/// amount_slashed\]
+		Slashed(T::AccountId, T::Balance),
 	}
 
 	/// Old name generated by `decl_event`.
@@ -1103,6 +1109,7 @@ impl<T: Config<I>, I: 'static> fungible::Mutate<T::AccountId> for Pallet<T, I> {
 			Ok(())
 		})?;
 		TotalIssuance::<T, I>::mutate(|t| *t += amount);
+		Self::deposit_event(Event::Deposit(who.clone(), amount));
 		Ok(())
 	}
 
@@ -1123,6 +1130,7 @@ impl<T: Config<I>, I: 'static> fungible::Mutate<T::AccountId> for Pallet<T, I> {
 			},
 		)?;
 		TotalIssuance::<T, I>::mutate(|t| *t -= actual);
+		Self::deposit_event(Event::Withdraw(who.clone(), amount));
 		Ok(actual)
 	}
 }
@@ -1141,7 +1149,10 @@ impl<T: Config<I>, I: 'static> fungible::Transfer<T::AccountId> for Pallet<T, I>
 
 impl<T: Config<I>, I: 'static> fungible::Unbalanced<T::AccountId> for Pallet<T, I> {
 	fn set_balance(who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
-		Self::mutate_account(who, |account| account.free = amount)?;
+		Self::mutate_account(who, |account| {
+			account.free = amount;
+			Self::deposit_event(Event::BalanceSet(who.clone(), account.free, account.reserved));
+		})?;
 		Ok(())
 	}
 
@@ -1583,7 +1594,13 @@ where
 					}
 				},
 			) {
-				Ok(r) => return r,
+				Ok((imbalance, not_slashed)) => {
+					Self::deposit_event(Event::Slashed(
+						who.clone(),
+						value.saturating_sub(not_slashed),
+					));
+					return (imbalance, not_slashed)
+				},
 				Err(_) => (),
 			}
 		}
@@ -1608,6 +1625,7 @@ where
 			|account, is_new| -> Result<Self::PositiveImbalance, DispatchError> {
 				ensure!(!is_new, Error::<T, I>::DeadAccount);
 				account.free = account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?;
+				Self::deposit_event(Event::Deposit(who.clone(), value));
 				Ok(PositiveImbalance::new(value))
 			},
 		)
@@ -1640,6 +1658,7 @@ where
 					None => return Ok(Self::PositiveImbalance::zero()),
 				};
 
+				Self::deposit_event(Event::Deposit(who.clone(), value));
 				Ok(PositiveImbalance::new(value))
 			},
 		)
@@ -1677,6 +1696,7 @@ where
 
 				account.free = new_free_account;
 
+				Self::deposit_event(Event::Withdraw(who.clone(), value));
 				Ok(NegativeImbalance::new(value))
 			},
 		)
@@ -1709,6 +1729,7 @@ where
 					SignedImbalance::Negative(NegativeImbalance::new(account.free - value))
 				};
 				account.free = value;
+				Self::deposit_event(Event::BalanceSet(who.clone(), account.free, account.reserved));
 				Ok(imbalance)
 			},
 		)
@@ -1824,7 +1845,13 @@ where
 				// underflow should never happen, but it if does, there's nothing to be done here.
 				(NegativeImbalance::new(actual), value - actual)
 			}) {
-				Ok(r) => return r,
+				Ok((imbalance, not_slashed)) => {
+					Self::deposit_event(Event::Slashed(
+						who.clone(),
+						value.saturating_sub(not_slashed),
+					));
+					return (imbalance, not_slashed)
+				},
 				Err(_) => (),
 			}
 		}
@@ -1965,6 +1992,7 @@ where
 					// `actual <= to_change` and `to_change <= amount`; qed;
 					reserves[index].amount -= actual;
 
+					Self::deposit_event(Event::Slashed(who.clone(), actual));
 					(imb, value - actual)
 				},
 				Err(_) => (NegativeImbalance::zero(), value),
diff --git a/substrate/frame/balances/src/tests.rs b/substrate/frame/balances/src/tests.rs
index a08643821eb..6a6ebc692c3 100644
--- a/substrate/frame/balances/src/tests.rs
+++ b/substrate/frame/balances/src/tests.rs
@@ -314,6 +314,7 @@ macro_rules! decl_tests {
 			<$ext_builder>::default().monied(true).build().execute_with(|| {
 				assert_eq!(Balances::total_balance(&1), 10);
 				assert_ok!(Balances::deposit_into_existing(&1, 10).map(drop));
+				System::assert_last_event(Event::Balances(crate::Event::Deposit(1, 10)));
 				assert_eq!(Balances::total_balance(&1), 20);
 				assert_eq!(<TotalIssuance<$test>>::get(), 120);
 			});
@@ -341,6 +342,7 @@ macro_rules! decl_tests {
 		fn balance_works() {
 			<$ext_builder>::default().build().execute_with(|| {
 				let _ = Balances::deposit_creating(&1, 42);
+				System::assert_has_event(Event::Balances(crate::Event::Deposit(1, 42)));
 				assert_eq!(Balances::free_balance(1), 42);
 				assert_eq!(Balances::reserved_balance(1), 0);
 				assert_eq!(Balances::total_balance(&1), 42);
@@ -435,6 +437,19 @@ macro_rules! decl_tests {
 			});
 		}
 
+		#[test]
+		fn withdrawing_balance_should_work() {
+			<$ext_builder>::default().build().execute_with(|| {
+				let _ = Balances::deposit_creating(&2, 111);
+				let _ = Balances::withdraw(
+					&2, 11, WithdrawReasons::TRANSFER, ExistenceRequirement::KeepAlive
+				);
+				System::assert_last_event(Event::Balances(crate::Event::Withdraw(2, 11)));
+				assert_eq!(Balances::free_balance(2), 100);
+				assert_eq!(<TotalIssuance<$test>>::get(), 100);
+			});
+		}
+
 		#[test]
 		fn slashing_incomplete_balance_should_work() {
 			<$ext_builder>::default().build().execute_with(|| {
@@ -749,6 +764,7 @@ macro_rules! decl_tests {
 						[
 							Event::System(system::Event::KilledAccount(1)),
 							Event::Balances(crate::Event::DustLost(1, 99)),
+							Event::Balances(crate::Event::Slashed(1, 1)),
 						]
 					);
 				});
@@ -777,7 +793,8 @@ macro_rules! decl_tests {
 					assert_eq!(
 						events(),
 						[
-							Event::System(system::Event::KilledAccount(1))
+							Event::System(system::Event::KilledAccount(1)),
+							Event::Balances(crate::Event::Slashed(1, 100)),
 						]
 					);
 				});
@@ -797,6 +814,7 @@ macro_rules! decl_tests {
 					assert_eq!(Balances::slash(&1, 900), (NegativeImbalance::new(900), 0));
 					// Account is still alive
 					assert!(System::account_exists(&1));
+					System::assert_last_event(Event::Balances(crate::Event::Slashed(1, 900)));
 
 					// SCENARIO: Slash will kill account because not enough balance left.
 					assert_ok!(Balances::set_balance(Origin::root(), 1, 1_000, 0));
diff --git a/substrate/frame/balances/src/tests_local.rs b/substrate/frame/balances/src/tests_local.rs
index 1d758ce4e98..b2113a916ca 100644
--- a/substrate/frame/balances/src/tests_local.rs
+++ b/substrate/frame/balances/src/tests_local.rs
@@ -173,7 +173,7 @@ fn emit_events_with_no_existential_deposit_suicide_with_dust() {
 		assert_eq!(res, (NegativeImbalance::new(98), 0));
 
 		// no events
-		assert_eq!(events(), []);
+		assert_eq!(events(), [Event::Balances(crate::Event::Slashed(1, 98))]);
 
 		let res = Balances::slash(&1, 1);
 		assert_eq!(res, (NegativeImbalance::new(1), 0));
@@ -183,6 +183,7 @@ fn emit_events_with_no_existential_deposit_suicide_with_dust() {
 			[
 				Event::System(system::Event::KilledAccount(1)),
 				Event::Balances(crate::Event::DustLost(1, 1)),
+				Event::Balances(crate::Event::Slashed(1, 1)),
 			]
 		);
 	});
diff --git a/substrate/frame/balances/src/tests_reentrancy.rs b/substrate/frame/balances/src/tests_reentrancy.rs
index 25b8fb34f20..9a5ebb003af 100644
--- a/substrate/frame/balances/src/tests_reentrancy.rs
+++ b/substrate/frame/balances/src/tests_reentrancy.rs
@@ -167,11 +167,11 @@ fn transfer_dust_removal_tst1_should_work() {
 		assert_eq!(Balances::free_balance(&1), 1050);
 
 		// Verify the events
-		// Number of events expected is 8
-		assert_eq!(System::events().len(), 11);
+		assert_eq!(System::events().len(), 12);
 
 		System::assert_has_event(Event::Balances(crate::Event::Transfer(2, 3, 450)));
 		System::assert_has_event(Event::Balances(crate::Event::DustLost(2, 50)));
+		System::assert_has_event(Event::Balances(crate::Event::Deposit(1, 50)));
 	});
 }
 
@@ -195,11 +195,11 @@ fn transfer_dust_removal_tst2_should_work() {
 		assert_eq!(Balances::free_balance(&1), 1500);
 
 		// Verify the events
-		// Number of events expected is 8
-		assert_eq!(System::events().len(), 9);
+		assert_eq!(System::events().len(), 10);
 
 		System::assert_has_event(Event::Balances(crate::Event::Transfer(2, 1, 450)));
 		System::assert_has_event(Event::Balances(crate::Event::DustLost(2, 50)));
+		System::assert_has_event(Event::Balances(crate::Event::Deposit(1, 50)));
 	});
 }
 
@@ -232,8 +232,7 @@ fn repatriating_reserved_balance_dust_removal_should_work() {
 		assert_eq!(Balances::free_balance(1), 1500);
 
 		// Verify the events
-		// Number of events expected is 10
-		assert_eq!(System::events().len(), 10);
+		assert_eq!(System::events().len(), 11);
 
 		System::assert_has_event(Event::Balances(crate::Event::ReserveRepatriated(
 			2,
@@ -241,7 +240,7 @@ fn repatriating_reserved_balance_dust_removal_should_work() {
 			450,
 			Status::Free,
 		)));
-
-		System::assert_last_event(Event::Balances(crate::Event::DustLost(2, 50)));
+		System::assert_has_event(Event::Balances(crate::Event::DustLost(2, 50)));
+		System::assert_last_event(Event::Balances(crate::Event::Deposit(1, 50)));
 	});
 }
diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs
index b2141ca18b0..310c1d4cb2d 100644
--- a/substrate/frame/contracts/src/tests.rs
+++ b/substrate/frame/contracts/src/tests.rs
@@ -444,6 +444,11 @@ fn instantiate_and_call_and_deposit_event() {
 		assert_eq!(
 			System::events(),
 			vec![
+				EventRecord {
+					phase: Phase::Initialization,
+					event: Event::Balances(pallet_balances::Event::Deposit(ALICE, 1_000_000)),
+					topics: vec![],
+				},
 				EventRecord {
 					phase: Phase::Initialization,
 					event: Event::System(frame_system::Event::NewAccount(ALICE.clone())),
diff --git a/substrate/frame/offences/benchmarking/src/lib.rs b/substrate/frame/offences/benchmarking/src/lib.rs
index dde8aa92c24..c920b0b900d 100644
--- a/substrate/frame/offences/benchmarking/src/lib.rs
+++ b/substrate/frame/offences/benchmarking/src/lib.rs
@@ -224,27 +224,49 @@ fn check_events<T: Config, I: Iterator<Item = <T as SystemConfig>::Event>>(expec
 		.map(|frame_system::EventRecord { event, .. }| event)
 		.collect::<Vec<_>>();
 	let expected = expected.collect::<Vec<_>>();
-	let lengths = (events.len(), expected.len());
-	let length_mismatch = if lengths.0 != lengths.1 {
-		fn pretty<D: std::fmt::Debug>(header: &str, ev: &[D]) {
-			println!("{}", header);
-			for (idx, ev) in ev.iter().enumerate() {
-				println!("\t[{:04}] {:?}", idx, ev);
-			}
+
+	fn pretty<D: std::fmt::Debug>(header: &str, ev: &[D], offset: usize) {
+		println!("{}", header);
+		for (idx, ev) in ev.iter().enumerate() {
+			println!("\t[{:04}] {:?}", idx + offset, ev);
 		}
-		pretty("--Got:", &events);
-		pretty("--Expected:", &expected);
-		format!("Mismatching length. Got: {}, expected: {}", lengths.0, lengths.1)
-	} else {
-		Default::default()
-	};
+	}
+	fn print_events<D: std::fmt::Debug>(idx: usize, events: &[D], expected: &[D]) {
+		let window = 10;
+		let start = idx.saturating_sub(window / 2);
+		let end_got = (idx + window / 2).min(events.len());
+		pretty("Got(window):", &events[start..end_got], start);
+		let end_expected = (idx + window / 2).min(expected.len());
+		pretty("Expected(window):", &expected[start..end_expected], start);
+		println!("---------------");
+		let start_got = events.len().saturating_sub(window);
+		pretty("Got(end):", &events[start_got..], start_got);
+		let start_expected = expected.len().saturating_sub(window);
+		pretty("Expected(end):", &expected[start_expected..], start_expected);
+	}
+	let events_copy = events.clone();
+	let expected_copy = expected.clone();
 
 	for (idx, (a, b)) in events.into_iter().zip(expected).enumerate() {
-		assert_eq!(a, b, "Mismatch at: {}. {}", idx, length_mismatch);
+		if a != b {
+			print_events(idx, &events_copy, &expected_copy);
+			println!("Mismatch at: {}", idx);
+			println!("     Got: {:?}", b);
+			println!("Expected: {:?}", a);
+			if events_copy.len() != expected_copy.len() {
+				println!(
+					"Mismatching lengths. Got: {}, Expected: {}",
+					events_copy.len(),
+					expected_copy.len()
+				)
+			}
+			panic!("Mismatching events.");
+		}
 	}
 
-	if !length_mismatch.is_empty() {
-		panic!("{}", length_mismatch);
+	if events_copy.len() != expected_copy.len() {
+		print_events(0, &events_copy, &expected_copy);
+		panic!("Mismatching lengths. Got: {}, Expected: {}", events_copy.len(), expected_copy.len())
 	}
 }
 
@@ -288,46 +310,74 @@ benchmarks! {
 		let bond_amount: u32 = UniqueSaturatedInto::<u32>::unique_saturated_into(bond_amount::<T>());
 		let slash_amount = slash_fraction * bond_amount;
 		let reward_amount = slash_amount * (1 + n) / 2;
+		let reward = reward_amount / r;
 		let slash = |id| core::iter::once(
 			<T as StakingConfig>::Event::from(StakingEvent::<T>::Slashed(id, BalanceOf::<T>::from(slash_amount)))
 		);
+		let balance_slash = |id| core::iter::once(
+			<T as BalancesConfig>::Event::from(pallet_balances::Event::<T>::Slashed(id, slash_amount.into()))
+		);
 		let chill = |id| core::iter::once(
 			<T as StakingConfig>::Event::from(StakingEvent::<T>::Chilled(id))
 		);
-		let mut slash_events = raw_offenders.into_iter()
+		let balance_deposit = |id, amount: u32|
+			<T as BalancesConfig>::Event::from(pallet_balances::Event::<T>::Deposit(id, amount.into()));
+		let mut first = true;
+		let slash_events = raw_offenders.into_iter()
 			.flat_map(|offender| {
-				let nom_slashes = offender.nominator_stashes.into_iter().flat_map(|nom| slash(nom));
-				chill(offender.stash.clone())
-				.chain(slash(offender.stash))
-				.chain(nom_slashes)
+				let nom_slashes = offender.nominator_stashes.into_iter().flat_map(|nom| {
+					balance_slash(nom.clone()).map(Into::into)
+					.chain(slash(nom.clone()).map(Into::into))
+				}).collect::<Vec<_>>();
+
+				let mut events = chill(offender.stash.clone()).map(Into::into)
+					.chain(balance_slash(offender.stash.clone()).map(Into::into))
+					.chain(slash(offender.stash.clone()).map(Into::into))
+					.chain(nom_slashes.into_iter())
+					.collect::<Vec<_>>();
+
+				// the first deposit creates endowed events, see `endowed_reward_events`
+				if first {
+					first = false;
+					let mut reward_events = reporters.clone().into_iter()
+						.flat_map(|reporter| vec![
+							balance_deposit(reporter.clone(), reward.into()).into(),
+							frame_system::Event::<T>::NewAccount(reporter.clone()).into(),
+							<T as BalancesConfig>::Event::from(
+								pallet_balances::Event::<T>::Endowed(reporter.clone(), reward.into())
+							).into(),
+						])
+						.collect::<Vec<_>>();
+					events.append(&mut reward_events);
+					events.into_iter()
+				} else {
+					let mut reward_events = reporters.clone().into_iter()
+						.map(|reporter| balance_deposit(reporter, reward.into()).into())
+						.collect::<Vec<_>>();
+					events.append(&mut reward_events);
+					events.into_iter()
+				}
 			})
 			.collect::<Vec<_>>();
-		let reward_events = reporters.into_iter()
-			.flat_map(|reporter| vec![
-				frame_system::Event::<T>::NewAccount(reporter.clone()).into(),
-				<T as BalancesConfig>::Event::from(
-					pallet_balances::Event::<T>::Endowed(reporter, (reward_amount / r).into())
-				).into()
-			]);
-
-		// Rewards are applied after first offender and it's nominators.
-		// We split after: offender slash + offender chill + nominator slashes.
-		let slash_rest = slash_events.split_off(2 + n as usize);
 
-		// make sure that all slashes have been applied
+
+
 		#[cfg(test)]
-		check_events::<T, _>(
-			std::iter::empty()
-				.chain(slash_events.into_iter().map(Into::into))
-				.chain(reward_events)
-				.chain(slash_rest.into_iter().map(Into::into))
-				.chain(std::iter::once(<T as OffencesConfig>::Event::from(
-					pallet_offences::Event::Offence(
-						UnresponsivenessOffence::<T>::ID,
-						0_u32.to_le_bytes().to_vec(),
-					)
-				).into()))
-		);
+		{
+			// In case of error it's useful to see the inputs
+			println!("Inputs: r: {}, o: {}, n: {}", r, o, n);
+			// make sure that all slashes have been applied
+			check_events::<T, _>(
+				std::iter::empty()
+					.chain(slash_events.into_iter().map(Into::into))
+					.chain(std::iter::once(<T as OffencesConfig>::Event::from(
+						pallet_offences::Event::Offence(
+							UnresponsivenessOffence::<T>::ID,
+							0_u32.to_le_bytes().to_vec(),
+						)
+					).into()))
+			);
+		}
 	}
 
 	report_offence_grandpa {
@@ -358,10 +408,10 @@ benchmarks! {
 		assert_eq!(
 			System::<T>::event_count(), 0
 			+ 1 // offence
-			+ 2 // reporter (reward + endowment)
-			+ 1 // offenders slashed
+			+ 3 // reporter (reward + endowment)
+			+ 2 // offenders slashed
 			+ 1 // offenders chilled
-			+ n // nominators slashed
+			+ 2 * n // nominators slashed
 		);
 	}
 
@@ -393,10 +443,10 @@ benchmarks! {
 		assert_eq!(
 			System::<T>::event_count(), 0
 			+ 1 // offence
-			+ 2 // reporter (reward + endowment)
-			+ 1 // offenders slashed
+			+ 3 // reporter (reward + endowment)
+			+ 2 // offenders slashed
 			+ 1 // offenders chilled
-			+ n // nominators slashed
+			+ 2 * n // nominators slashed
 		);
 	}
 
-- 
GitLab