diff --git a/Cargo.lock b/Cargo.lock index 2e3ce8e0ec89b8b0e4c8cdf1c353c6e5fb494362..b66692b9c7ed568cfce67bc249cd46ebb591714a 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 4a2a53304ae75c44157c346aabec14e90e74a82c..ade9f7437bcf80a0e396625d44530c901841dd35 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 355eb1ff7f517e0567ef64aae7926b26d49336fe..6858083e14ce180092307546059eb318b0fd36d3 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 680b3da3491563def2a8dae615a094ff52fc3c3a..976999d23464736b73a55792f68b3feef4b686d4 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 0000000000000000000000000000000000000000..25f8f63aecab38a98b967f51ed963a0ae367d749 --- /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); + }) +}