From 8042ed11ba003150778bbf5013717b09f379e1b5 Mon Sep 17 00:00:00 2001
From: Benjamin Gallois <business@gallois.cc>
Date: Mon, 18 Dec 2023 10:13:23 +0100
Subject: [PATCH] Add end2end test for offences (nodes/rust/duniter-v2s!216)

* fix rebase errors and formatting

* separate offences end2end tests

* fix end2end tests offences

* add end2end test babe offences

* add end2end test grandpa offences

* add end2end test im_offline offences
---
 Cargo.lock                             |   1 +
 pallets/authority-members/src/impls.rs |   3 +
 pallets/authority-members/src/lib.rs   |   2 +
 runtime/gdev/Cargo.toml                |   1 +
 runtime/gdev/tests/offences_tests.rs   | 157 +++++++++++++++++++++++++
 5 files changed, 164 insertions(+)
 create mode 100644 runtime/gdev/tests/offences_tests.rs

diff --git a/Cargo.lock b/Cargo.lock
index 2e3ce8e0e..b66692b9c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3234,6 +3234,7 @@ dependencies = [
  "sp-offchain",
  "sp-runtime",
  "sp-session",
+ "sp-staking",
  "sp-std 5.0.0",
  "sp-transaction-pool",
  "sp-version",
diff --git a/pallets/authority-members/src/impls.rs b/pallets/authority-members/src/impls.rs
index 4a2a53304..ade9f7437 100644
--- a/pallets/authority-members/src/impls.rs
+++ b/pallets/authority-members/src/impls.rs
@@ -59,6 +59,9 @@ where
                         {
                             if !blacklist.contains(&member_id) {
                                 blacklist.push(member_id);
+                                Self::deposit_event(Event::MemberAddedToBlacklist {
+                                    member: member_id,
+                                });
                                 add_db_reads_writes(0, 1);
                             }
                             Self::insert_out(member_id);
diff --git a/pallets/authority-members/src/lib.rs b/pallets/authority-members/src/lib.rs
index 355eb1ff7..6858083e1 100644
--- a/pallets/authority-members/src/lib.rs
+++ b/pallets/authority-members/src/lib.rs
@@ -179,6 +179,8 @@ pub mod pallet {
         MemberRemoved { member: T::MemberId },
         /// A member has been removed from the blacklist.
         MemberRemovedFromBlacklist { member: T::MemberId },
+        /// A member has been blacklisted.
+        MemberAddedToBlacklist { member: T::MemberId },
     }
 
     // ERRORS //
diff --git a/runtime/gdev/Cargo.toml b/runtime/gdev/Cargo.toml
index 680b3da34..976999d23 100644
--- a/runtime/gdev/Cargo.toml
+++ b/runtime/gdev/Cargo.toml
@@ -135,6 +135,7 @@ try-runtime = [
 [dev-dependencies]
 sp-io = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.42', default-features = false }
 sp-keyring = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.42', default-features = false }
+sp-staking = { git = 'https://github.com/duniter/substrate', branch = 'duniter-substrate-v0.9.42', default-features = false }
 
 [dependencies]
 # local
diff --git a/runtime/gdev/tests/offences_tests.rs b/runtime/gdev/tests/offences_tests.rs
new file mode 100644
index 000000000..25f8f63ae
--- /dev/null
+++ b/runtime/gdev/tests/offences_tests.rs
@@ -0,0 +1,157 @@
+// Copyright 2021 Axiom-Team
+//
+// This file is part of Duniter-v2S.
+//
+// Duniter-v2S is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// Duniter-v2S is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
+
+mod common;
+
+use common::*;
+use frame_support::assert_ok;
+use frame_support::traits::ValidatorSet;
+use frame_support::traits::ValidatorSetWithIdentification;
+use gdev_runtime::*;
+use pallet_im_online as im_online;
+use pallet_im_online::UnresponsivenessOffence;
+use pallet_session::historical::IdentificationTuple;
+use sp_runtime::traits::Convert;
+use sp_staking::offence::ReportOffence;
+
+#[test]
+fn test_imonline_offence() {
+    ExtBuilder::new(1, 3, 4).build().execute_with(|| {
+        run_to_block(1);
+        let session_index = Session::current_index();
+        let current_validators = <Runtime as im_online::Config>::ValidatorSet::validators();
+        // Construct an offence where all validators (member: 1) are offenders
+        let offenders = current_validators
+            .into_iter()
+            .enumerate()
+            .filter_map(|(_, id)| {
+                <<Runtime as im_online::Config>::ValidatorSet as ValidatorSetWithIdentification<
+                    sp_runtime::AccountId32,
+                >>::IdentificationOf::convert(id.clone())
+                .map(|full_id| (id, full_id))
+            })
+            .collect::<Vec<IdentificationTuple<Runtime>>>();
+        let keys = ImOnline::keys();
+        let validator_set_count = keys.len() as u32;
+        let offence = UnresponsivenessOffence {
+            session_index,
+            validator_set_count,
+            offenders,
+        };
+        assert_ok!(
+            <Runtime as pallet_im_online::Config>::ReportUnresponsiveness::report_offence(
+                vec![],
+                offence
+            )
+        );
+        // An offence is deposited
+        System::assert_has_event(RuntimeEvent::Offences(pallet_offences::Event::Offence {
+            kind: *b"im-online:offlin",
+            timeslot: vec![0, 0, 0, 0],
+        }));
+        // Offenders are punished
+        System::assert_has_event(RuntimeEvent::AuthorityMembers(
+            pallet_authority_members::Event::MemberGoOffline { member: 1 },
+        ));
+        assert_eq!(AuthorityMembers::blacklist().len(), 0);
+    })
+}
+#[test]
+fn test_grandpa_offence() {
+    ExtBuilder::new(1, 3, 4).build().execute_with(|| {
+        run_to_block(1);
+        let session_index = Session::current_index();
+        let current_validators = <Runtime as pallet_im_online::Config>::ValidatorSet::validators();
+        // Construct an offence where all validators (member: 1) are offenders
+        let mut offenders = current_validators
+            .into_iter()
+            .enumerate()
+            .filter_map(|(_, id)| {
+                <Runtime as pallet_session::historical::Config>::FullIdentificationOf::convert(
+                    id.clone(),
+                )
+                .map(|full_id| (id, full_id))
+            })
+            .collect::<Vec<IdentificationTuple<Runtime>>>();
+        let keys = ImOnline::keys();
+        let validator_set_count = keys.len() as u32;
+        let time_slot = pallet_grandpa::TimeSlot {
+            set_id: 0,
+            round: 0,
+        };
+        let offence = pallet_grandpa::EquivocationOffence {
+            time_slot,
+            session_index,
+            validator_set_count,
+            offender: offenders.pop().unwrap(),
+        };
+        assert_ok!(Offences::report_offence(vec![], offence));
+        // An offence is deposited
+        System::assert_has_event(RuntimeEvent::Offences(pallet_offences::Event::Offence {
+            kind: *b"grandpa:equivoca",
+            timeslot: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+        }));
+        // Offenders are punished
+        System::assert_has_event(RuntimeEvent::AuthorityMembers(
+            pallet_authority_members::Event::MemberGoOffline { member: 1 },
+        ));
+        System::assert_has_event(RuntimeEvent::AuthorityMembers(
+            pallet_authority_members::Event::MemberAddedToBlacklist { member: 1 },
+        ));
+        assert_eq!(AuthorityMembers::blacklist().len(), 1);
+    })
+}
+#[test]
+fn test_babe_offence() {
+    ExtBuilder::new(1, 3, 4).build().execute_with(|| {
+        run_to_block(1);
+        let session_index = Session::current_index();
+        let current_validators = <Runtime as pallet_im_online::Config>::ValidatorSet::validators();
+        // Construct an offence where all validators (member: 1) are offenders
+        let mut offenders = current_validators
+            .into_iter()
+            .enumerate()
+            .filter_map(|(_, id)| {
+                <Runtime as pallet_session::historical::Config>::FullIdentificationOf::convert(
+                    id.clone(),
+                )
+                .map(|full_id| (id, full_id))
+            })
+            .collect::<Vec<IdentificationTuple<Runtime>>>();
+        let keys = ImOnline::keys();
+        let validator_set_count = keys.len() as u32;
+        let offence = pallet_babe::EquivocationOffence {
+            slot: 0u64.into(),
+            session_index,
+            validator_set_count,
+            offender: offenders.pop().unwrap(),
+        };
+        assert_ok!(Offences::report_offence(vec![], offence));
+        // An offence is deposited
+        System::assert_has_event(RuntimeEvent::Offences(pallet_offences::Event::Offence {
+            kind: *b"babe:equivocatio",
+            timeslot: vec![0, 0, 0, 0, 0, 0, 0, 0],
+        }));
+        // Offenders are punished
+        System::assert_has_event(RuntimeEvent::AuthorityMembers(
+            pallet_authority_members::Event::MemberGoOffline { member: 1 },
+        ));
+        System::assert_has_event(RuntimeEvent::AuthorityMembers(
+            pallet_authority_members::Event::MemberAddedToBlacklist { member: 1 },
+        ));
+        assert_eq!(AuthorityMembers::blacklist().len(), 1);
+    })
+}
-- 
GitLab