From 80020b35ea751454c73e1c2a80dbd985a3b43ef6 Mon Sep 17 00:00:00 2001 From: Andronik Date: Mon, 24 Oct 2022 13:29:15 +0200 Subject: [PATCH 01/34] disputes/slashing: slash only backers for ForInvalid --- runtime/parachains/src/disputes.rs | 47 ++++++++++- runtime/parachains/src/disputes/slashing.rs | 82 ++++++++++++------- .../src/disputes/slashing/benchmarking.rs | 7 +- runtime/parachains/src/mock.rs | 2 + 4 files changed, 105 insertions(+), 33 deletions(-) diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index 9f458421e2ed..9474d21d0d85 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -81,6 +81,7 @@ pub trait SlashingHandler { session: SessionIndex, candidate_hash: CandidateHash, losers: impl IntoIterator, + backers: impl IntoIterator, ); /// Punish a series of validators who were against a valid parablock. This @@ -89,6 +90,7 @@ pub trait SlashingHandler { session: SessionIndex, candidate_hash: CandidateHash, losers: impl IntoIterator, + backers: impl IntoIterator, ); /// Called by the initializer to initialize the slashing pallet. @@ -106,6 +108,7 @@ impl SlashingHandler for () { _: SessionIndex, _: CandidateHash, _: impl IntoIterator, + _: impl IntoIterator, ) { } @@ -113,6 +116,7 @@ impl SlashingHandler for () { _: SessionIndex, _: CandidateHash, _: impl IntoIterator, + _: impl IntoIterator, ) { } @@ -459,6 +463,18 @@ pub mod pallet { DisputeState, >; + /// Backing votes stored for each dispute. + /// This storage is used for slashing. + #[pallet::storage] + pub(super) type BackersOnDisputes = StorageDoubleMap< + _, + Twox64Concat, + SessionIndex, + Blake2_128Concat, + CandidateHash, + Vec, + >; + /// All included blocks on the chain, as well as the block number in this chain that /// should be reverted back to if the candidate is disputed and determined to be invalid. #[pallet::storage] @@ -521,6 +537,8 @@ pub mod pallet { PotentialSpam, /// A dispute where there are only votes on one side. SingleSidedDispute, + /// No backing votes were provides along dispute statements. + MissingBackingVotes, } #[pallet::call] @@ -888,6 +906,8 @@ impl Pallet { // This should be small, as disputes are rare, so `None` is fine. #[allow(deprecated)] >::remove_prefix(to_prune, None); + #[allow(deprecated)] + >::remove_prefix(to_prune, None); // This is larger, and will be extracted to the `shared` pallet for more proper pruning. // TODO: https://github.com/paritytech/polkadot/issues/3469 @@ -984,7 +1004,8 @@ impl Pallet { let mut summary = { let mut importer = DisputeStateImporter::new(dispute_state, now); for (i, (statement, validator_index, signature)) in set.statements.iter().enumerate() { - // assure the validator index and is present in the session info + // ensure the validator index is present in the session info + // and the signature is valid let validator_public = match session_info.validators.get(*validator_index) { None => { filter.remove_index(i); @@ -1181,6 +1202,9 @@ impl Pallet { } }; + let mut backers = + >::get(&set.session, &set.candidate_hash).unwrap_or_default(); + // Import all votes. They were pre-checked. let summary = { let mut importer = DisputeStateImporter::new(dispute_state, now); @@ -1188,6 +1212,14 @@ impl Pallet { let valid = statement.indicates_validity(); importer.import(*validator_index, valid).map_err(Error::::from)?; + + match statement { + DisputeStatement::Valid(ValidDisputeStatementKind::BackingSeconded(_)) | + DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(_)) => { + backers.push(validator_index.clone()); + }, + _ => {}, + } } importer.finish() @@ -1200,6 +1232,11 @@ impl Pallet { Error::::SingleSidedDispute, ); + // Reject statements with no accompanying backing votes. + ensure!(!backers.is_empty(), Error::::MissingBackingVotes,); + + >::insert(&set.session, &set.candidate_hash, backers.clone()); + let DisputeStatementSet { ref session, ref candidate_hash, .. } = set; let session = *session; let candidate_hash = *candidate_hash; @@ -1246,10 +1283,16 @@ impl Pallet { session, candidate_hash, summary.slash_against, + backers.clone(), ); // an invalid candidate, according to 2/3. Punish those on the 'for' side. - T::SlashingHandler::punish_for_invalid(session, candidate_hash, summary.slash_for); + T::SlashingHandler::punish_for_invalid( + session, + candidate_hash, + summary.slash_for, + backers, + ); } >::insert(&session, &candidate_hash, &summary.state); diff --git a/runtime/parachains/src/disputes/slashing.rs b/runtime/parachains/src/disputes/slashing.rs index 2dfdc87c4b4e..b378bf2b4e23 100644 --- a/runtime/parachains/src/disputes/slashing.rs +++ b/runtime/parachains/src/disputes/slashing.rs @@ -16,32 +16,31 @@ //! Dispute slashing pallet. //! -//! Once a dispute is concluded, we want to slash validators -//! who were on the wrong side of the dispute. The slashing amount -//! depends on whether the candidate was valid (small) or invalid (big). -//! In addition to that, we might want to kick out the validators from the -//! active set. +//! Once a dispute is concluded, we want to slash validators who were on the +//! wrong side of the dispute. The slashing amount depends on whether the +//! candidate was valid (none at the moment) or invalid (big). In addition to +//! that, we might want to kick out the validators from the active set. +//! Currently, we limit slashing to the backing group for invalid disputes. //! //! The `offences` pallet from Substrate provides us with a way to do both. -//! Currently, the interface expects us to provide staking information -//! including nominator exposure in order to submit an offence. +//! Currently, the interface expects us to provide staking information including +//! nominator exposure in order to submit an offence. //! //! Normally, we'd able to fetch this information from the runtime as soon as //! the dispute is concluded. This is also what `im-online` pallet does. //! However, since a dispute can conclude several sessions after the candidate //! was backed (see `dispute_period` in `HostConfiguration`), we can't rely on //! this information be available in the context of the current block. The -//! `babe` and `grandpa` equivocation handlers also have to deal -//! with this problem. +//! `babe` and `grandpa` equivocation handlers also have to deal with this +//! problem. //! //! Our implementation looks like a hybrid of `im-online` and `grandpa` //! equivocation handlers. Meaning, we submit an `offence` for the concluded -//! disputes about the current session candidate directly from the runtime. -//! If, however, the dispute is about a past session, we record unapplied -//! slashes on chain, without `FullIdentification` of the offenders. -//! Later on, a block producer can submit an unsigned transaction with -//! `KeyOwnershipProof` of an offender and submit it to the runtime -//! to produce an offence. +//! disputes about the current session candidate directly from the runtime. If, +//! however, the dispute is about a past session, we record unapplied slashes on +//! chain, without `FullIdentification` of the offenders. Later on, a block +//! producer can submit an unsigned transaction with `KeyOwnershipProof` of an +//! offender and submit it to the runtime to produce an offence. use crate::{disputes, initializer::ValidatorSetCount, session_info::IdentificationTuple}; use frame_support::{ @@ -64,7 +63,10 @@ use sp_runtime::{ use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::offence::{DisableStrategy, Kind, Offence, OffenceError, ReportOffence}; use sp_std::{ - collections::btree_map::{BTreeMap, Entry}, + collections::{ + btree_map::{BTreeMap, Entry}, + btree_set::BTreeSet, + }, prelude::*, }; @@ -73,7 +75,7 @@ const LOG_TARGET: &str = "runtime::parachains::slashing"; // These are constants, but we want to make them configurable // via `HostConfiguration` in the future. const SLASH_FOR_INVALID: Perbill = Perbill::from_percent(100); -const SLASH_AGAINST_VALID: Perbill = Perbill::from_perthousand(1); +const SLASH_AGAINST_VALID: Perbill = Perbill::zero(); const DEFENSIVE_PROOF: &'static str = "disputes module should bail on old session"; #[cfg(feature = "runtime-benchmarks")] @@ -228,10 +230,31 @@ where candidate_hash: CandidateHash, kind: SlashingOffenceKind, losers: impl IntoIterator, + backers: impl IntoIterator, ) { - let losers: Vec = losers.into_iter().collect(); - if losers.is_empty() { - // Nothing to do + // sanity check for the current implementation + if kind == SlashingOffenceKind::AgainstValid { + debug_assert!(false, "should only slash ForInvalid disputes"); + return + } + let backers: Vec = backers.into_iter().collect(); + debug_assert!( + { + let losers_index: BTreeSet<_> = losers.into_iter().collect(); + backers.iter().all(|i| losers_index.contains(i)) + }, + "backers should be a subset of losing ForInvalid validators", + ); + debug_assert_eq!( + { + let backers_dedup: BTreeSet<_> = backers.iter().collect(); + backers_dedup.len() + }, + backers.len(), + "backers should be deduplicated, this is ensured by disputes pallet", + ); + debug_assert!(!backers.is_empty(), "backers should not be empty",); + if backers.is_empty() { return } let session_info = crate::session_info::Pallet::::session_info(session_index); @@ -239,7 +262,8 @@ where Some(info) => info, None => return, }; - let maybe = Self::maybe_identify_validators(session_index, losers.iter().cloned()); + + let maybe = Self::maybe_identify_validators(session_index, backers.iter().cloned()); if let Some(offenders) = maybe { let validator_set_count = session_info.discovery_keys.len() as ValidatorSetCount; let offence = SlashingOffence::new( @@ -255,7 +279,7 @@ where return } - let keys = losers + let keys = backers .into_iter() .filter_map(|i| session_info.validators.get(i).cloned().map(|id| (i, id))) .collect(); @@ -272,18 +296,20 @@ where session_index: SessionIndex, candidate_hash: CandidateHash, losers: impl IntoIterator, + backers: impl IntoIterator, ) { let kind = SlashingOffenceKind::ForInvalid; - Self::do_punish(session_index, candidate_hash, kind, losers); + Self::do_punish(session_index, candidate_hash, kind, losers, backers); } fn punish_against_valid( - session_index: SessionIndex, - candidate_hash: CandidateHash, - losers: impl IntoIterator, + _session_index: SessionIndex, + _candidate_hash: CandidateHash, + _losers: impl IntoIterator, + _backers: impl IntoIterator, ) { - let kind = SlashingOffenceKind::AgainstValid; - Self::do_punish(session_index, candidate_hash, kind, losers); + // do nothing for now + // NOTE: changing that requires modifying `do_punish` implementation } fn initializer_initialize(now: T::BlockNumber) -> Weight { diff --git a/runtime/parachains/src/disputes/slashing/benchmarking.rs b/runtime/parachains/src/disputes/slashing/benchmarking.rs index 552172dc1901..9e199b75d9bb 100644 --- a/runtime/parachains/src/disputes/slashing/benchmarking.rs +++ b/runtime/parachains/src/disputes/slashing/benchmarking.rs @@ -109,8 +109,9 @@ where let validator_index = ValidatorIndex(0); let losers = [validator_index].into_iter(); + let backers = losers.clone(); - T::SlashingHandler::punish_against_valid(session_index, CANDIDATE_HASH, losers); + T::SlashingHandler::punish_for_invalid(session_index, CANDIDATE_HASH, losers, backers); let unapplied = >::get(session_index, CANDIDATE_HASH); assert_eq!(unapplied.unwrap().keys.len(), 1); @@ -123,7 +124,7 @@ fn dispute_proof( validator_id: ValidatorId, validator_index: ValidatorIndex, ) -> DisputeProof { - let kind = SlashingOffenceKind::AgainstValid; + let kind = SlashingOffenceKind::ForInvalid; let time_slot = DisputesTimeSlot::new(session_index, CANDIDATE_HASH); DisputeProof { time_slot, kind, validator_index, validator_id } @@ -134,7 +135,7 @@ benchmarks! { where T: Config, } - // in this setup we have a single `AgainstValid` dispute + // in this setup we have a single `ForInvalid` dispute // submitted for a past session report_dispute_lost { let n in 4..<::BenchmarkingConfig as BenchmarkingConfiguration>::MAX_VALIDATORS; diff --git a/runtime/parachains/src/mock.rs b/runtime/parachains/src/mock.rs index 3e6ed79a77c2..5212521e069e 100644 --- a/runtime/parachains/src/mock.rs +++ b/runtime/parachains/src/mock.rs @@ -269,6 +269,7 @@ impl crate::disputes::SlashingHandler for Test { session: SessionIndex, _: CandidateHash, losers: impl IntoIterator, + _backers: impl IntoIterator, ) { PUNISH_VALIDATORS_FOR.with(|r| r.borrow_mut().push((session, losers.into_iter().collect()))) } @@ -277,6 +278,7 @@ impl crate::disputes::SlashingHandler for Test { session: SessionIndex, _: CandidateHash, losers: impl IntoIterator, + _backers: impl IntoIterator, ) { PUNISH_VALIDATORS_AGAINST .with(|r| r.borrow_mut().push((session, losers.into_iter().collect()))) From ed428b1cf55fdb5a84dc7c15d6af5823236a9597 Mon Sep 17 00:00:00 2001 From: Andronik Date: Mon, 7 Nov 2022 11:22:58 +0100 Subject: [PATCH 02/34] add an assertion in mock impl --- runtime/parachains/src/disputes/tests.rs | 21 ++++++++++----------- runtime/parachains/src/mock.rs | 16 +++++++++++++--- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/runtime/parachains/src/disputes/tests.rs b/runtime/parachains/src/disputes/tests.rs index 4d8ac714cb7b..626abe3964c3 100644 --- a/runtime/parachains/src/disputes/tests.rs +++ b/runtime/parachains/src/disputes/tests.rs @@ -697,21 +697,20 @@ fn test_provide_multi_dispute_is_providing() { }); let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1)); + let inclusion_parent = sp_core::H256::repeat_byte(0xff); + let session = 1; let stmts = vec![DisputeStatementSet { candidate_hash: candidate_hash.clone(), - session: 1, + session, statements: vec![ ( - DisputeStatement::Valid(ValidDisputeStatementKind::Explicit), + DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid( + inclusion_parent, + )), ValidatorIndex(0), - v0.sign( - &ExplicitDisputeStatement { - valid: true, - candidate_hash: candidate_hash.clone(), - session: 1, - } - .signing_payload(), - ), + v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload( + &SigningContext { session_index: session, parent_hash: inclusion_parent }, + )), ), ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), @@ -720,7 +719,7 @@ fn test_provide_multi_dispute_is_providing() { &ExplicitDisputeStatement { valid: false, candidate_hash: candidate_hash.clone(), - session: 1, + session, } .signing_payload(), ), diff --git a/runtime/parachains/src/mock.rs b/runtime/parachains/src/mock.rs index 5212521e069e..4abcbcbd55d4 100644 --- a/runtime/parachains/src/mock.rs +++ b/runtime/parachains/src/mock.rs @@ -41,7 +41,10 @@ use sp_runtime::{ transaction_validity::TransactionPriority, KeyTypeId, Permill, }; -use std::{cell::RefCell, collections::HashMap}; +use std::{ + cell::RefCell, + collections::{BTreeSet, HashMap}, +}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -269,9 +272,16 @@ impl crate::disputes::SlashingHandler for Test { session: SessionIndex, _: CandidateHash, losers: impl IntoIterator, - _backers: impl IntoIterator, + backers: impl IntoIterator, ) { - PUNISH_VALIDATORS_FOR.with(|r| r.borrow_mut().push((session, losers.into_iter().collect()))) + let backers_dedup: BTreeSet<_> = backers.into_iter().collect(); + let losers_dedup: BTreeSet<_> = losers.into_iter().collect(); + if !losers_dedup.is_empty() { + assert!(backers_dedup.is_subset(&losers_dedup)); + } + + PUNISH_VALIDATORS_FOR + .with(|r| r.borrow_mut().push((session, losers_dedup.into_iter().collect()))) } fn punish_against_valid( From 82287285239f34e4d8bf08f9883cf218472967ba Mon Sep 17 00:00:00 2001 From: Andronik Date: Mon, 7 Nov 2022 13:07:56 +0100 Subject: [PATCH 03/34] fix tests --- runtime/parachains/src/disputes/tests.rs | 171 +++++++++++++++-------- 1 file changed, 112 insertions(+), 59 deletions(-) diff --git a/runtime/parachains/src/disputes/tests.rs b/runtime/parachains/src/disputes/tests.rs index 626abe3964c3..7c9aafe66137 100644 --- a/runtime/parachains/src/disputes/tests.rs +++ b/runtime/parachains/src/disputes/tests.rs @@ -553,6 +553,7 @@ fn test_dispute_timeout() { }); let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1)); + let inclusion_parent = sp_core::H256::repeat_byte(0xff); // v0 votes for 3, v6 against. let stmts = vec![DisputeStatementSet { @@ -560,16 +561,13 @@ fn test_dispute_timeout() { session: start - 1, statements: vec![ ( - DisputeStatement::Valid(ValidDisputeStatementKind::Explicit), + DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid( + inclusion_parent, + )), ValidatorIndex(0), - v0.sign( - &ExplicitDisputeStatement { - valid: true, - candidate_hash: candidate_hash.clone(), - session: start - 1, - } - .signing_payload(), - ), + v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload( + &SigningContext { session_index: start - 1, parent_hash: inclusion_parent }, + )), ), ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), @@ -739,6 +737,70 @@ fn test_provide_multi_dispute_is_providing() { }) } +#[test] +fn test_disputes_with_missing_backing_votes_are_rejected() { + new_test_ext(Default::default()).execute_with(|| { + let v0 = ::Pair::generate().0; + let v1 = ::Pair::generate().0; + + run_to_block(3, |b| { + // a new session at each block + if b == 1 { + Some(( + true, + b, + vec![(&0, v0.public()), (&1, v1.public())], + Some(vec![(&0, v0.public()), (&1, v1.public())]), + )) + } else { + Some((true, b, vec![(&1, v1.public())], Some(vec![(&1, v1.public())]))) + } + }); + + let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1)); + let session = 1; + + let stmts = vec![DisputeStatementSet { + candidate_hash: candidate_hash.clone(), + session, + statements: vec![ + ( + DisputeStatement::Valid(ValidDisputeStatementKind::Explicit), + ValidatorIndex(0), + v0.sign( + &ExplicitDisputeStatement { + valid: true, + candidate_hash: candidate_hash.clone(), + session, + } + .signing_payload(), + ), + ), + ( + DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), + ValidatorIndex(1), + v1.sign( + &ExplicitDisputeStatement { + valid: false, + candidate_hash: candidate_hash.clone(), + session, + } + .signing_payload(), + ), + ), + ], + }]; + + assert!(Pallet::::process_checked_multi_dispute_data( + stmts + .into_iter() + .map(CheckedDisputeStatementSet::unchecked_from_unchecked) + .collect() + ) + .is_err(),); + }) +} + #[test] fn test_freeze_on_note_included() { new_test_ext(Default::default()).execute_with(|| { @@ -756,6 +818,8 @@ fn test_freeze_on_note_included() { }); let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1)); + let inclusion_parent = sp_core::H256::repeat_byte(0xff); + let session = 3; // v0 votes for 3 let stmts = vec![DisputeStatementSet { @@ -787,16 +851,13 @@ fn test_freeze_on_note_included() { ), ), ( - DisputeStatement::Valid(ValidDisputeStatementKind::Explicit), + DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid( + inclusion_parent, + )), ValidatorIndex(1), - v1.sign( - &ExplicitDisputeStatement { - valid: true, - candidate_hash: candidate_hash.clone(), - session: 3, - } - .signing_payload(), - ), + v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload( + &SigningContext { session_index: session, parent_hash: inclusion_parent }, + )), ), ], }]; @@ -830,11 +891,13 @@ fn test_freeze_provided_against_supermajority_for_included() { }); let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1)); + let inclusion_parent = sp_core::H256::repeat_byte(0xff); + let session = 3; // v0 votes for 3 let stmts = vec![DisputeStatementSet { candidate_hash: candidate_hash.clone(), - session: 3, + session, statements: vec![ ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), @@ -843,7 +906,7 @@ fn test_freeze_provided_against_supermajority_for_included() { &ExplicitDisputeStatement { valid: false, candidate_hash: candidate_hash.clone(), - session: 3, + session, } .signing_payload(), ), @@ -855,22 +918,19 @@ fn test_freeze_provided_against_supermajority_for_included() { &ExplicitDisputeStatement { valid: false, candidate_hash: candidate_hash.clone(), - session: 3, + session, } .signing_payload(), ), ), ( - DisputeStatement::Valid(ValidDisputeStatementKind::Explicit), + DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid( + inclusion_parent, + )), ValidatorIndex(1), - v1.sign( - &ExplicitDisputeStatement { - valid: true, - candidate_hash: candidate_hash.clone(), - session: 3, - } - .signing_payload(), - ), + v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload( + &SigningContext { session_index: session, parent_hash: inclusion_parent }, + )), ), ], }]; @@ -941,23 +1001,22 @@ fn test_provide_multi_dispute_success_and_other() { }); let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1)); + let inclusion_parent = sp_core::H256::repeat_byte(0xff); + let session = 3; // v0 votes for 3, v6 votes against let stmts = vec![DisputeStatementSet { candidate_hash: candidate_hash.clone(), - session: 3, + session, statements: vec![ ( - DisputeStatement::Valid(ValidDisputeStatementKind::Explicit), + DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid( + inclusion_parent, + )), ValidatorIndex(0), - v0.sign( - &ExplicitDisputeStatement { - valid: true, - candidate_hash: candidate_hash.clone(), - session: 3, - } - .signing_payload(), - ), + v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload( + &SigningContext { session_index: session, parent_hash: inclusion_parent }, + )), ), ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), @@ -966,7 +1025,7 @@ fn test_provide_multi_dispute_success_and_other() { &ExplicitDisputeStatement { valid: false, candidate_hash: candidate_hash.clone(), - session: 3, + session, } .signing_payload(), ), @@ -989,16 +1048,13 @@ fn test_provide_multi_dispute_success_and_other() { session: 4, statements: vec![ ( - DisputeStatement::Valid(ValidDisputeStatementKind::Explicit), + DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid( + inclusion_parent, + )), ValidatorIndex(3), - v1.sign( - &ExplicitDisputeStatement { - valid: true, - candidate_hash: candidate_hash.clone(), - session: 4, - } - .signing_payload(), - ), + v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload( + &SigningContext { session_index: 4, parent_hash: inclusion_parent }, + )), ), ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), @@ -1064,16 +1120,13 @@ fn test_provide_multi_dispute_success_and_other() { session: 5, statements: vec![ ( - DisputeStatement::Valid(ValidDisputeStatementKind::Explicit), + DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid( + inclusion_parent, + )), ValidatorIndex(5), - v3.sign( - &ExplicitDisputeStatement { - valid: true, - candidate_hash: candidate_hash.clone(), - session: 5, - } - .signing_payload(), - ), + v3.sign(&CompactStatement::Valid(candidate_hash).signing_payload( + &SigningContext { session_index: 5, parent_hash: inclusion_parent }, + )), ), ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), From 656ab84f26dc0432235e38fa179234be4b509f0e Mon Sep 17 00:00:00 2001 From: Andronik Date: Mon, 7 Nov 2022 17:08:27 +0100 Subject: [PATCH 04/34] do not slash backers on onconcluded disputes --- runtime/parachains/src/disputes/slashing.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/runtime/parachains/src/disputes/slashing.rs b/runtime/parachains/src/disputes/slashing.rs index b378bf2b4e23..8feb24304ae3 100644 --- a/runtime/parachains/src/disputes/slashing.rs +++ b/runtime/parachains/src/disputes/slashing.rs @@ -238,11 +238,12 @@ where return } let backers: Vec = backers.into_iter().collect(); + let losers: BTreeSet<_> = losers.into_iter().collect(); + if losers.is_empty() { + return + } debug_assert!( - { - let losers_index: BTreeSet<_> = losers.into_iter().collect(); - backers.iter().all(|i| losers_index.contains(i)) - }, + backers.iter().all(|i| losers.contains(i)), "backers should be a subset of losing ForInvalid validators", ); debug_assert_eq!( From e3d7ba67e5cdf81e538c9bc9248027b3099639a2 Mon Sep 17 00:00:00 2001 From: Andronik Date: Wed, 9 Nov 2022 17:07:46 +0100 Subject: [PATCH 05/34] slash an intersection of backers and losers --- runtime/parachains/src/disputes/slashing.rs | 23 ++++++--------------- runtime/parachains/src/mock.rs | 16 +++----------- 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/runtime/parachains/src/disputes/slashing.rs b/runtime/parachains/src/disputes/slashing.rs index 8feb24304ae3..462249a9d4e3 100644 --- a/runtime/parachains/src/disputes/slashing.rs +++ b/runtime/parachains/src/disputes/slashing.rs @@ -237,34 +237,23 @@ where debug_assert!(false, "should only slash ForInvalid disputes"); return } - let backers: Vec = backers.into_iter().collect(); let losers: BTreeSet<_> = losers.into_iter().collect(); if losers.is_empty() { return } - debug_assert!( - backers.iter().all(|i| losers.contains(i)), - "backers should be a subset of losing ForInvalid validators", - ); - debug_assert_eq!( - { - let backers_dedup: BTreeSet<_> = backers.iter().collect(); - backers_dedup.len() - }, - backers.len(), - "backers should be deduplicated, this is ensured by disputes pallet", - ); - debug_assert!(!backers.is_empty(), "backers should not be empty",); - if backers.is_empty() { + let backers: BTreeSet<_> = backers.into_iter().collect(); + let to_punish: Vec = losers.intersection(&backers).cloned().collect(); + if to_punish.is_empty() { return } + let session_info = crate::session_info::Pallet::::session_info(session_index); let session_info = match session_info.defensive_proof(DEFENSIVE_PROOF) { Some(info) => info, None => return, }; - let maybe = Self::maybe_identify_validators(session_index, backers.iter().cloned()); + let maybe = Self::maybe_identify_validators(session_index, to_punish.iter().cloned()); if let Some(offenders) = maybe { let validator_set_count = session_info.discovery_keys.len() as ValidatorSetCount; let offence = SlashingOffence::new( @@ -280,7 +269,7 @@ where return } - let keys = backers + let keys = to_punish .into_iter() .filter_map(|i| session_info.validators.get(i).cloned().map(|id| (i, id))) .collect(); diff --git a/runtime/parachains/src/mock.rs b/runtime/parachains/src/mock.rs index 4abcbcbd55d4..5212521e069e 100644 --- a/runtime/parachains/src/mock.rs +++ b/runtime/parachains/src/mock.rs @@ -41,10 +41,7 @@ use sp_runtime::{ transaction_validity::TransactionPriority, KeyTypeId, Permill, }; -use std::{ - cell::RefCell, - collections::{BTreeSet, HashMap}, -}; +use std::{cell::RefCell, collections::HashMap}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -272,16 +269,9 @@ impl crate::disputes::SlashingHandler for Test { session: SessionIndex, _: CandidateHash, losers: impl IntoIterator, - backers: impl IntoIterator, + _backers: impl IntoIterator, ) { - let backers_dedup: BTreeSet<_> = backers.into_iter().collect(); - let losers_dedup: BTreeSet<_> = losers.into_iter().collect(); - if !losers_dedup.is_empty() { - assert!(backers_dedup.is_subset(&losers_dedup)); - } - - PUNISH_VALIDATORS_FOR - .with(|r| r.borrow_mut().push((session, losers_dedup.into_iter().collect()))) + PUNISH_VALIDATORS_FOR.with(|r| r.borrow_mut().push((session, losers.into_iter().collect()))) } fn punish_against_valid( From 59b9c0dd673acdacf682cffe1a5d546b8760118f Mon Sep 17 00:00:00 2001 From: Andronik Date: Thu, 10 Nov 2022 14:22:03 +0100 Subject: [PATCH 06/34] zombienet/disputes: check for offence only for invalid disputes --- zombienet_tests/functional/0002-parachains-disputes.zndsl | 3 --- .../functional/0003-parachains-garbage-candidate.zndsl | 5 +++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/zombienet_tests/functional/0002-parachains-disputes.zndsl b/zombienet_tests/functional/0002-parachains-disputes.zndsl index 9386e07e209a..b56cd9b06c89 100644 --- a/zombienet_tests/functional/0002-parachains-disputes.zndsl +++ b/zombienet_tests/functional/0002-parachains-disputes.zndsl @@ -48,9 +48,6 @@ eve: reports parachain_candidate_disputes_total is at least 10 within 15 seconds eve: reports parachain_candidate_dispute_concluded{validity="valid"} is at least 10 within 15 seconds eve: reports parachain_candidate_dispute_concluded{validity="invalid"} is 0 within 15 seconds -# Check there is an offence report -alice: system event contains "There is an offence reported" within 60 seconds - # Check lag - approval alice: reports polkadot_parachain_approval_checking_finality_lag is 0 bob: reports polkadot_parachain_approval_checking_finality_lag is 0 diff --git a/zombienet_tests/functional/0003-parachains-garbage-candidate.zndsl b/zombienet_tests/functional/0003-parachains-garbage-candidate.zndsl index 24be749f13e6..71e93fcf359e 100644 --- a/zombienet_tests/functional/0003-parachains-garbage-candidate.zndsl +++ b/zombienet_tests/functional/0003-parachains-garbage-candidate.zndsl @@ -18,6 +18,11 @@ honest-validator-0: parachain 2000 block height is at least 2 within 180 seconds honest-validator-1: parachain 2001 block height is at least 2 within 180 seconds honest-validator-2: parachain 2002 block height is at least 2 within 180 seconds +# Check there is an offence report after dispute conclusion +honest-validator-0: system event contains "There is an offence reported" within 180 seconds +honest-validator-1: system event contains "There is an offence reported" within 180 seconds +honest-validator-2: system event contains "There is an offence reported" within 180 seconds + # Check for chain reversion after dispute conclusion. honest-validator-0: log line contains "reverted due to a bad parachain block" within 180 seconds honest-validator-1: log line contains "reverted due to a bad parachain block" within 180 seconds From 9ee1e3b9bc9d9048df7743b5eff3877dd48975cd Mon Sep 17 00:00:00 2001 From: Andronik Date: Fri, 11 Nov 2022 17:29:46 +0100 Subject: [PATCH 07/34] add backing votes to disputes bench builder --- runtime/parachains/src/builder.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/runtime/parachains/src/builder.rs b/runtime/parachains/src/builder.rs index 9a1c16e6aa1d..6aab6d939166 100644 --- a/runtime/parachains/src/builder.rs +++ b/runtime/parachains/src/builder.rs @@ -595,6 +595,7 @@ impl BenchBuilder { let (para_id, core_idx, group_idx) = self.create_indexes(seed); let candidate_hash = CandidateHash(H256::from(byte32_slice_from(seed))); + let relay_parent = H256::from(byte32_slice_from(seed)); Self::add_availability( para_id, @@ -614,9 +615,12 @@ impl BenchBuilder { // so we make sure that we have a super majority with valid statements. let dispute_statement = if validator_index % 4 == 0 { DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit) + } else if validator_index < 3 { + // Set the last two votes as backing for the dispute set to be accepted + DisputeStatement::Valid( + ValidDisputeStatementKind::BackingValid(relay_parent) + ) } else { - // Note that in the future we could use some availability votes as an - // implicit valid kind. DisputeStatement::Valid(ValidDisputeStatementKind::Explicit) }; let data = dispute_statement.payload_data(candidate_hash.clone(), session); From cabbee258b9d51ad6c2fcf2392de1a3a54b1ace2 Mon Sep 17 00:00:00 2001 From: Andronik Date: Fri, 11 Nov 2022 17:55:15 +0100 Subject: [PATCH 08/34] Update runtime/parachains/src/builder.rs --- runtime/parachains/src/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/parachains/src/builder.rs b/runtime/parachains/src/builder.rs index 6aab6d939166..d4cc217cbace 100644 --- a/runtime/parachains/src/builder.rs +++ b/runtime/parachains/src/builder.rs @@ -616,7 +616,7 @@ impl BenchBuilder { let dispute_statement = if validator_index % 4 == 0 { DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit) } else if validator_index < 3 { - // Set the last two votes as backing for the dispute set to be accepted + // Set two votes as backing for the dispute set to be accepted DisputeStatement::Valid( ValidDisputeStatementKind::BackingValid(relay_parent) ) From d7bd141cb64f27c9c6ac6502e54779613410c329 Mon Sep 17 00:00:00 2001 From: Bradley Olson <34992650+BradleyOlson64@users.noreply.github.com> Date: Fri, 11 Nov 2022 11:24:40 -0800 Subject: [PATCH 09/34] Brad implementers guide revisions 2 (#6239) * Add disputes subsystems fix * Updated dispute approval vote import reasoning * Improved wording of my changes * Resolving issues brought up in comments --- .../src/node/disputes/README.md | 2 +- .../src/node/disputes/dispute-coordinator.md | 60 ++++++++++--------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/roadmap/implementers-guide/src/node/disputes/README.md b/roadmap/implementers-guide/src/node/disputes/README.md index 7b28b908fbbe..2db891aa216f 100644 --- a/roadmap/implementers-guide/src/node/disputes/README.md +++ b/roadmap/implementers-guide/src/node/disputes/README.md @@ -5,7 +5,7 @@ This section is for the node-side subsystems that lead to participation in dispu * Participation. Participate in active disputes. When a node becomes aware of a dispute, it should recover the data for the disputed block, check the validity of the parablock, and issue a statement regarding the validity of the parablock. * Distribution. Validators should notify each other of active disputes and relevant statements over the network. * Submission. When authoring a block, a validator should inspect the state of the parent block and provide any information about disputes that the chain needs as part of the `ParaInherent`. This should initialize new disputes on-chain as necessary. - * Fork-choice and Finality. When observing a block issuing a `DisputeRollback` digest in the header, that branch of the relay chain should be abandoned all the way back to the indicated block. When voting on chains in GRANDPA, no chains that contain blocks that are or have been disputed should be voted on. + * Fork-choice and Finality. When observing a block issuing a `DisputeRollback` digest in the header, that branch of the relay chain should be abandoned all the way back to the indicated block. When voting on chains in GRANDPA, no chains that contain blocks with active disputes or disputes that concluded invalid should be voted on. ## Components diff --git a/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md b/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md index 29a3c5ffa082..c04e15b1a0e9 100644 --- a/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md +++ b/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md @@ -9,8 +9,8 @@ In particular the dispute-coordinator is responsible for: - Ensuring that the node is able to raise a dispute in case an invalid candidate is found during approval checking. -- Ensuring malicious approval votes will be recorded, so nodes can get slashed - properly. +- Ensuring lazy approval votes (votes given without running the parachain + validation function) will be recorded, so lazy nodes can get slashed properly. - Coordinating actual participation in a dispute, ensuring that the node participates in any justified dispute in a way that ensures resolution of disputes on the network even in the case of many disputes raised (flood/DoS @@ -76,24 +76,27 @@ inefficient process. (Quadratic complexity adds up, with 35 votes in total per c Approval votes are very relevant nonetheless as we are going to see in the next section. -## Ensuring Malicious Approval Votes Will Be Recorded +## Ensuring Lazy Approval Votes Will Be Recorded ### Ensuring Recording While there is no need to record approval votes in the dispute coordinator preemptively, we do need to make sure they are recorded when a dispute actually happens. This is because only votes recorded by the dispute -coordinator will be considered for slashing. While the backing group always gets -slashed, a serious attack attempt will likely also consist of malicious approval -checkers which will cast approval votes, although the candidate is invalid. If -we did not import those votes, those nodes would likely cast an `invalid` explicit -vote as part of the dispute in addition to their approval vote and thus avoid a -slash. With the 2/3rd honest assumption it seems unrealistic that malicious -actors will keep sending approval votes once they became aware of a raised -dispute. Hence the most crucial approval votes to import are the early ones -(tranche 0), to take into account network latencies and such we still want to -import approval votes at a later point in time as well (in particular we need to -make sure the dispute can conclude, but more on that later). +coordinator will be considered for slashing. It is sufficient for our +threat model that malicious backers are slashed as opposed to both backers and +approval checkers. However, we still must import approval votes from the approvals +process into the disputes process to ensure that lazy approval checkers +actually run the parachain validation function. Slashing lazy approval checkers is necessary, else we risk a useless approvals process where every approval +checker blindly votes valid for every candidate. If we did not import approval +votes, lazy nodes would likely cast a properly checked explicit vote as part +of the dispute in addition to their blind approval vote and thus avoid a slash. +With the 2/3rd honest assumption it seems unrealistic that lazy approval voters +will keep sending unchecked approval votes once they became aware of a raised +dispute. Hence the most crucial approval votes to import are the early ones +(tranche 0), to take into account network latencies and such we still want to +import approval votes at a later point in time as well (in particular we need +to make sure the dispute can conclude, but more on that later). As mentioned already previously, importing votes is most efficient when batched. At the same time approval voting and disputes are running concurrently so @@ -173,7 +176,7 @@ There are two potential caveats with this though: voting. By distributing our own approval vote we make sure the dispute can conclude regardless how the race ended (we either participate explicitly anyway or we sent our already present approval vote). By importing all - approval votes we make it possible to slash malicious approval voters, even + approval votes we make it possible to slash lazy approval voters, even if they also cast an invalid explicit vote. Conclusion: As long as we make sure, if our own approval vote gets imported @@ -199,11 +202,12 @@ time participation is faster than approval, a node would do double work. ### Ensuring Chain Import While in the previous section we discussed means for nodes to ensure relevant -votes are recorded so attackers get slashed properly, it is crucial to also -discuss the actual chain import. Only if we guarantee that recorded votes will -also get imported on chain (on all potential chains really) we will succeed in -executing slashes. Again approval votes prove to be our weak spot here, but also -backing votes might get missed. +votes are recorded so lazy approval checkers get slashed properly, it is crucial +to also discuss the actual chain import. Only if we guarantee that recorded votes +will also get imported on chain (on all potential chains really) we will succeed +in executing slashes. Particularly we need to make sure backing votes end up on +chain consistantly. In contrast recording and slashing lazy approval voters only +needs to be likely, not certain. Dispute distribution will make sure all explicit dispute votes get distributed among nodes which includes current block producers (current authority set) which @@ -223,14 +227,14 @@ production in the current set - they might only exist on an already abandoned fork. This means a block producer that just joined the set, might not have seen any of them. -For approvals it is even more tricky: Approval voting together with finalization -is a completely off-chain process therefore those protocols don't care about -block production at all. Approval votes only have a guarantee of being -propagated between the nodes that are responsible for finalizing the concerned -blocks. This implies that on an era change the current authority set, will not -necessarily get informed about any approval votes for the previous era. Hence -even if all validators of the previous era successfully recorded all approval -votes in the dispute coordinator, they won't get a chance to put them on chain, +For approvals it is even more tricky and less necessary: Approval voting together +with finalization is a completely off-chain process therefore those protocols +don't care about block production at all. Approval votes only have a guarantee of +being propagated between the nodes that are responsible for finalizing the +concerned blocks. This implies that on an era change the current authority set, +will not necessarily get informed about any approval votes for the previous era. +Hence even if all validators of the previous era successfully recorded all approval +votes in the dispute coordinator, they won't get a chance to put them on chain, hence they won't be considered for slashing. It is important to note, that the essential properties of the system still hold: From adcd81e7eb35b18096f9e2d5246ac5b58e810964 Mon Sep 17 00:00:00 2001 From: Tsvetomir Dimitrov Date: Fri, 11 Nov 2022 23:47:29 +0200 Subject: [PATCH 10/34] Update disputes prioritisation in `dispute-coordinator` (#6130) * Scraper processes CandidateBacked events * Change definition of best-effort * Fix `dispute-coordinator` tests * Unit test for dispute filtering * Clarification comment * Add tests * Fix logic If a dispute is not backed, not included and not confirmed we don't participate but we do import votes. * Add metrics for refrained participations * Revert "Add tests" This reverts commit 7b8391a087922ced942cde9cd2b50ff3f633efc0. * Revert "Unit test for dispute filtering" This reverts commit 92ba5fe678214ab360306313a33c781338e600a0. * fix dispute-coordinator tests * Fix scraping * new tests * Small fixes in guide * Apply suggestions from code review Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> * Fix some comments and remove a pointless test * Code review feedback * Clarification comment in tests * Some tests * Reference counted `CandidateHash` in scraper * Proper handling for Backed and Included candidates in scraper Backed candidates which are not included should be kept for a predetermined window of finalized blocks. E.g. if a candidate is backed but not included in block 2, and the window size is 2, the same candidate should be cleaned after block 4 is finalized. Add reference counting for candidates in scraper. A candidate can be added on multiple block heights so we have to make sure we don't clean it prematurely from the scraper. Add tests. * Update comments in tests * Guide update * Fix cleanup logic for `backed_candidates_by_block_number` * Simplify cleanup * Make spellcheck happy * Update tests * Extract candidate backing logic in separate struct * Code review feedback * Treat backed and included candidates in the same fashion * Update some comments * Small improvements in test * spell check * Fix some more comments * clean -> prune * Code review feedback * Reword comment * spelling Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> --- .../dispute-coordinator/src/initialized.rs | 36 +- node/core/dispute-coordinator/src/lib.rs | 2 +- node/core/dispute-coordinator/src/metrics.rs | 16 + .../src/scraping/candidates.rs | 148 +++++++ .../dispute-coordinator/src/scraping/mod.rs | 120 +++--- .../dispute-coordinator/src/scraping/tests.rs | 250 ++++++++++- node/core/dispute-coordinator/src/tests.rs | 391 ++++++++++++++++-- .../src/node/disputes/dispute-coordinator.md | 9 +- 8 files changed, 873 insertions(+), 99 deletions(-) create mode 100644 node/core/dispute-coordinator/src/scraping/candidates.rs diff --git a/node/core/dispute-coordinator/src/initialized.rs b/node/core/dispute-coordinator/src/initialized.rs index 9229f61f3a78..901a6d863ed2 100644 --- a/node/core/dispute-coordinator/src/initialized.rs +++ b/node/core/dispute-coordinator/src/initialized.rs @@ -869,12 +869,21 @@ impl Initialized { } } + let has_own_vote = new_state.has_own_vote(); + let is_disputed = new_state.is_disputed(); + let has_controlled_indices = !env.controlled_indices().is_empty(); + let is_backed = self.scraper.is_candidate_backed(&candidate_hash); + let is_confirmed = new_state.is_confirmed(); + // We participate only in disputes which are included, backed or confirmed + let allow_participation = is_included || is_backed || is_confirmed; + // Participate in dispute if we did not cast a vote before and actually have keys to cast a - // local vote: - if !new_state.has_own_vote() && - new_state.is_disputed() && - !env.controlled_indices().is_empty() - { + // local vote. Disputes should fall in one of the categories below, otherwise we will refrain + // from participation: + // - `is_included` lands in prioritised queue + // - `is_confirmed` | `is_backed` lands in best effort queue + // We don't participate in disputes on finalized candidates. + if !has_own_vote && is_disputed && has_controlled_indices && allow_participation { let priority = ParticipationPriority::with_priority_if(is_included); gum::trace!( target: LOG_TARGET, @@ -896,6 +905,23 @@ impl Initialized { ) .await; log_error(r)?; + } else { + gum::trace!( + target: LOG_TARGET, + ?candidate_hash, + ?is_confirmed, + ?has_own_vote, + ?is_disputed, + ?has_controlled_indices, + ?allow_participation, + ?is_included, + ?is_backed, + "Will not queue participation for candidate" + ); + + if !allow_participation { + self.metrics.on_refrained_participation(); + } } // Also send any already existing approval vote on new disputes: diff --git a/node/core/dispute-coordinator/src/lib.rs b/node/core/dispute-coordinator/src/lib.rs index 03abd8f59d60..09d6c621b999 100644 --- a/node/core/dispute-coordinator/src/lib.rs +++ b/node/core/dispute-coordinator/src/lib.rs @@ -286,7 +286,7 @@ impl DisputeCoordinatorSubsystem { let mut participation_requests = Vec::new(); let mut unconfirmed_disputes: UnconfirmedDisputes = UnconfirmedDisputes::new(); - let (mut scraper, votes) = ChainScraper::new(ctx.sender(), initial_head).await?; + let (scraper, votes) = ChainScraper::new(ctx.sender(), initial_head).await?; for ((session, ref candidate_hash), status) in active_disputes { let votes: CandidateVotes = match overlay_db.load_candidate_votes(session, candidate_hash) { diff --git a/node/core/dispute-coordinator/src/metrics.rs b/node/core/dispute-coordinator/src/metrics.rs index 1fbe7e49e8b8..70cd49ac49d1 100644 --- a/node/core/dispute-coordinator/src/metrics.rs +++ b/node/core/dispute-coordinator/src/metrics.rs @@ -30,6 +30,8 @@ struct MetricsInner { queued_participations: prometheus::CounterVec, /// How long vote cleanup batches take. vote_cleanup_time: prometheus::Histogram, + /// Number of refrained participations. + refrained_participations: prometheus::Counter, } /// Candidate validation metrics. @@ -88,6 +90,12 @@ impl Metrics { pub(crate) fn time_vote_cleanup(&self) -> Option { self.0.as_ref().map(|metrics| metrics.vote_cleanup_time.start_timer()) } + + pub(crate) fn on_refrained_participation(&self) { + if let Some(metrics) = &self.0 { + metrics.refrained_participations.inc(); + } + } } impl metrics::Metrics for Metrics { @@ -147,6 +155,14 @@ impl metrics::Metrics for Metrics { )?, registry, )?, + refrained_participations: prometheus::register( + prometheus::Counter::with_opts( + prometheus::Opts::new( + "polkadot_parachain_dispute_refrained_participations", + "Number of refrained participations. We refrain from participation if all of the following conditions are met: disputed candidate is not included, not backed and not confirmed.", + ))?, + registry, + )?, }; Ok(Metrics(Some(metrics))) } diff --git a/node/core/dispute-coordinator/src/scraping/candidates.rs b/node/core/dispute-coordinator/src/scraping/candidates.rs new file mode 100644 index 000000000000..2fe797768cc2 --- /dev/null +++ b/node/core/dispute-coordinator/src/scraping/candidates.rs @@ -0,0 +1,148 @@ +use polkadot_primitives::v2::{BlockNumber, CandidateHash}; +use std::collections::{BTreeMap, HashMap, HashSet}; + +/// Keeps `CandidateHash` in reference counted way. +/// Each `insert` saves a value with `reference count == 1` or increases the reference +/// count if the value already exists. +/// Each `remove` decreases the reference count for the corresponding `CandidateHash`. +/// If the reference count reaches 0 - the value is removed. +struct RefCountedCandidates { + candidates: HashMap, +} + +impl RefCountedCandidates { + pub fn new() -> Self { + Self { candidates: HashMap::new() } + } + // If `CandidateHash` doesn't exist in the `HashMap` it is created and its reference + // count is set to 1. + // If `CandidateHash` already exists in the `HashMap` its reference count is increased. + pub fn insert(&mut self, candidate: CandidateHash) { + *self.candidates.entry(candidate).or_default() += 1; + } + + // If a `CandidateHash` with reference count equals to 1 is about to be removed - the + // candidate is dropped from the container too. + // If a `CandidateHash` with reference count biger than 1 is about to be removed - the + // reference count is decreased and the candidate remains in the container. + pub fn remove(&mut self, candidate: &CandidateHash) { + match self.candidates.get_mut(candidate) { + Some(v) if *v > 1 => *v -= 1, + Some(v) => { + assert!(*v == 1); + self.candidates.remove(candidate); + }, + None => {}, + } + } + + pub fn contains(&self, candidate: &CandidateHash) -> bool { + self.candidates.contains_key(&candidate) + } +} + +#[cfg(test)] +mod ref_counted_candidates_tests { + use super::*; + use polkadot_primitives::v2::{BlakeTwo256, HashT}; + + #[test] + fn element_is_removed_when_refcount_reaches_zero() { + let mut container = RefCountedCandidates::new(); + + let zero = CandidateHash(BlakeTwo256::hash(&vec![0])); + let one = CandidateHash(BlakeTwo256::hash(&vec![1])); + // add two separate candidates + container.insert(zero); // refcount == 1 + container.insert(one); + + // and increase the reference count for the first + container.insert(zero); // refcount == 2 + + assert!(container.contains(&zero)); + assert!(container.contains(&one)); + + // remove once -> refcount == 1 + container.remove(&zero); + assert!(container.contains(&zero)); + assert!(container.contains(&one)); + + // remove once -> refcount == 0 + container.remove(&zero); + assert!(!container.contains(&zero)); + assert!(container.contains(&one)); + + // remove the other element + container.remove(&one); + assert!(!container.contains(&zero)); + assert!(!container.contains(&one)); + } +} + +/// Keeps track of scraped candidates. Supports `insert`, `remove_up_to_height` and `contains` +/// operations. +pub struct ScrapedCandidates { + /// Main data structure which keeps the candidates we know about. `contains` does lookups only here. + candidates: RefCountedCandidates, + /// Keeps track at which block number a candidate was inserted. Used in `remove_up_to_height`. + /// Without this tracking we won't be able to remove all candidates before block X. + candidates_by_block_number: BTreeMap>, +} + +impl ScrapedCandidates { + pub fn new() -> Self { + Self { + candidates: RefCountedCandidates::new(), + candidates_by_block_number: BTreeMap::new(), + } + } + + pub fn contains(&self, candidate_hash: &CandidateHash) -> bool { + self.candidates.contains(candidate_hash) + } + + // Removes all candidates up to a given height. The candidates at the block height are NOT removed. + pub fn remove_up_to_height(&mut self, height: &BlockNumber) { + let not_stale = self.candidates_by_block_number.split_off(&height); + let stale = std::mem::take(&mut self.candidates_by_block_number); + self.candidates_by_block_number = not_stale; + for candidates in stale.values() { + for c in candidates { + self.candidates.remove(c); + } + } + } + + pub fn insert(&mut self, block_number: BlockNumber, candidate_hash: CandidateHash) { + self.candidates.insert(candidate_hash); + self.candidates_by_block_number + .entry(block_number) + .or_default() + .insert(candidate_hash); + } + + // Used only for tests to verify the pruning doesn't leak data. + #[cfg(test)] + pub fn candidates_by_block_number_is_empty(&self) -> bool { + self.candidates_by_block_number.is_empty() + } +} + +#[cfg(test)] +mod scraped_candidates_tests { + use super::*; + use polkadot_primitives::v2::{BlakeTwo256, HashT}; + + #[test] + fn stale_candidates_are_removed() { + let mut candidates = ScrapedCandidates::new(); + let target = CandidateHash(BlakeTwo256::hash(&vec![1, 2, 3])); + candidates.insert(1, target); + + assert!(candidates.contains(&target)); + + candidates.remove_up_to_height(&2); + assert!(!candidates.contains(&target)); + assert!(candidates.candidates_by_block_number_is_empty()); + } +} diff --git a/node/core/dispute-coordinator/src/scraping/mod.rs b/node/core/dispute-coordinator/src/scraping/mod.rs index 7d5d33e1ff4b..99a6e68cdfb5 100644 --- a/node/core/dispute-coordinator/src/scraping/mod.rs +++ b/node/core/dispute-coordinator/src/scraping/mod.rs @@ -14,10 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use std::{ - collections::{BTreeMap, HashSet}, - num::NonZeroUsize, -}; +use std::num::NonZeroUsize; use futures::channel::oneshot; use lru::LruCache; @@ -40,6 +37,8 @@ use crate::{ #[cfg(test)] mod tests; +mod candidates; + /// Number of hashes to keep in the LRU. /// /// @@ -58,18 +57,21 @@ const LRU_OBSERVED_BLOCKS_CAPACITY: NonZeroUsize = match NonZeroUsize::new(20) { /// /// Concretely: /// -/// - Monitors for inclusion events to keep track of candidates that have been included on chains. +/// - Monitors for `CandidateIncluded` events to keep track of candidates that have been +/// included on chains. +/// - Monitors for `CandidateBacked` events to keep track of all backed candidates. /// - Calls `FetchOnChainVotes` for each block to gather potentially missed votes from chain. /// /// With this information it provides a `CandidateComparator` and as a return value of /// `process_active_leaves_update` any scraped votes. +/// +/// Scraped candidates are available `CANDIDATE_LIFETIME_AFTER_FINALIZATION` more blocks +/// after finalization as a precaution not to prune them prematurely. pub struct ChainScraper { /// All candidates we have seen included, which not yet have been finalized. - included_candidates: HashSet, - /// including block -> `CandidateHash` - /// - /// We need this to clean up `included_candidates` on finalization. - candidates_by_block_number: BTreeMap>, + included_candidates: candidates::ScrapedCandidates, + /// All candidates we have seen backed + backed_candidates: candidates::ScrapedCandidates, /// Latest relay blocks observed by the provider. /// /// We assume that ancestors of cached blocks are already processed, i.e. we have saved @@ -85,6 +87,16 @@ impl ChainScraper { /// As long as we have `MAX_FINALITY_LAG` this makes sense as a value. pub(crate) const ANCESTRY_SIZE_LIMIT: u32 = MAX_FINALITY_LAG; + /// How many blocks after finalization a backed/included candidate should be kept. + /// We don't want to remove scraped candidates on finalization because we want to + /// be sure that disputes will conclude on abandoned forks. + /// Removing the candidate on finalization creates a possibility for an attacker to + /// avoid slashing. If a bad fork is abandoned too quickly because in the same another + /// better one gets finalized the entries for the bad fork will be pruned and we + /// will never participate in a dispute for it. We want such disputes to conclude + /// in a timely manner so that the offenders are slashed. + pub(crate) const CANDIDATE_LIFETIME_AFTER_FINALIZATION: BlockNumber = 2; + /// Create a properly initialized `OrderingProvider`. /// /// Returns: `Self` and any scraped votes. @@ -96,8 +108,8 @@ impl ChainScraper { Sender: overseer::DisputeCoordinatorSenderTrait, { let mut s = Self { - included_candidates: HashSet::new(), - candidates_by_block_number: BTreeMap::new(), + included_candidates: candidates::ScrapedCandidates::new(), + backed_candidates: candidates::ScrapedCandidates::new(), last_observed_blocks: LruCache::new(LRU_OBSERVED_BLOCKS_CAPACITY), }; let update = @@ -107,10 +119,15 @@ impl ChainScraper { } /// Check whether we have seen a candidate included on any chain. - pub fn is_candidate_included(&mut self, candidate_hash: &CandidateHash) -> bool { + pub fn is_candidate_included(&self, candidate_hash: &CandidateHash) -> bool { self.included_candidates.contains(candidate_hash) } + /// Check whether the candidate is backed + pub fn is_candidate_backed(&self, candidate_hash: &CandidateHash) -> bool { + self.backed_candidates.contains(candidate_hash) + } + /// Query active leaves for any candidate `CandidateEvent::CandidateIncluded` events. /// /// and updates current heads, so we can query candidates for all non finalized blocks. @@ -120,7 +137,7 @@ impl ChainScraper { &mut self, sender: &mut Sender, update: &ActiveLeavesUpdate, - ) -> crate::error::Result> + ) -> Result> where Sender: overseer::DisputeCoordinatorSenderTrait, { @@ -158,21 +175,27 @@ impl ChainScraper { /// Prune finalized candidates. /// - /// Once a candidate lives in a relay chain block that's behind the finalized chain/got - /// finalized, we can treat it as low priority. - pub fn process_finalized_block(&mut self, finalized: &BlockNumber) { - let not_finalized = self.candidates_by_block_number.split_off(finalized); - let finalized = std::mem::take(&mut self.candidates_by_block_number); - self.candidates_by_block_number = not_finalized; - // Clean up finalized: - for finalized_candidate in finalized.into_values().flatten() { - self.included_candidates.remove(&finalized_candidate); + /// We keep each candidate for `CANDIDATE_LIFETIME_AFTER_FINALIZATION` blocks after finalization. + /// After that we treat it as low priority. + pub fn process_finalized_block(&mut self, finalized_block_number: &BlockNumber) { + // `CANDIDATE_LIFETIME_AFTER_FINALIZATION - 1` because `finalized_block_number`counts to the + // candidate lifetime. + match finalized_block_number.checked_sub(Self::CANDIDATE_LIFETIME_AFTER_FINALIZATION - 1) { + Some(key_to_prune) => { + self.backed_candidates.remove_up_to_height(&key_to_prune); + self.included_candidates.remove_up_to_height(&key_to_prune); + }, + None => { + // Nothing to prune. We are still in the beginning of the chain and there are not + // enough finalized blocks yet. + }, } + {} } /// Process candidate events of a block. /// - /// Keep track of all included candidates. + /// Keep track of all included and backed candidates. async fn process_candidate_events( &mut self, sender: &mut Sender, @@ -182,28 +205,33 @@ impl ChainScraper { where Sender: overseer::DisputeCoordinatorSenderTrait, { - // Get included events: - let included = - get_candidate_events(sender, block_hash) - .await? - .into_iter() - .filter_map(|ev| match ev { - CandidateEvent::CandidateIncluded(receipt, _, _, _) => Some(receipt), - _ => None, - }); - for receipt in included { - let candidate_hash = receipt.hash(); - gum::trace!( - target: LOG_TARGET, - ?candidate_hash, - ?block_number, - "Processing included event" - ); - self.included_candidates.insert(candidate_hash); - self.candidates_by_block_number - .entry(block_number) - .or_default() - .insert(candidate_hash); + // Get included and backed events: + for ev in get_candidate_events(sender, block_hash).await? { + match ev { + CandidateEvent::CandidateIncluded(receipt, _, _, _) => { + let candidate_hash = receipt.hash(); + gum::trace!( + target: LOG_TARGET, + ?candidate_hash, + ?block_number, + "Processing included event" + ); + self.included_candidates.insert(block_number, candidate_hash); + }, + CandidateEvent::CandidateBacked(receipt, _, _, _) => { + let candidate_hash = receipt.hash(); + gum::trace!( + target: LOG_TARGET, + ?candidate_hash, + ?block_number, + "Processing backed event" + ); + self.backed_candidates.insert(block_number, candidate_hash); + }, + _ => { + // skip the rest + }, + } } Ok(()) } diff --git a/node/core/dispute-coordinator/src/scraping/tests.rs b/node/core/dispute-coordinator/src/scraping/tests.rs index b6b5a1f633bf..6251891af5a3 100644 --- a/node/core/dispute-coordinator/src/scraping/tests.rs +++ b/node/core/dispute-coordinator/src/scraping/tests.rs @@ -73,7 +73,12 @@ impl TestState { assert_finalized_block_number_request(&mut ctx_handle, finalized_block_number).await; gum::trace!(target: LOG_TARGET, "After assert_finalized_block_number"); // No ancestors requests, as list would be empty. - assert_candidate_events_request(&mut ctx_handle, &chain).await; + assert_candidate_events_request( + &mut ctx_handle, + &chain, + get_backed_and_included_candidate_events, + ) + .await; assert_chain_vote_request(&mut ctx_handle, &chain).await; }; @@ -112,6 +117,10 @@ async fn process_active_leaves_update( .unwrap(); } +fn process_finalized_block(scraper: &mut ChainScraper, finalized: &BlockNumber) { + scraper.process_finalized_block(&finalized) +} + fn make_candidate_receipt(relay_parent: Hash) -> CandidateReceipt { let zeros = dummy_hash(); let descriptor = CandidateDescriptor { @@ -145,16 +154,66 @@ fn get_block_number_hash(n: BlockNumber) -> Hash { } /// Get a dummy event that corresponds to candidate inclusion for the given block number. -fn get_candidate_included_events(block_number: BlockNumber) -> Vec { - vec![CandidateEvent::CandidateIncluded( - make_candidate_receipt(get_block_number_hash(block_number)), +fn get_backed_and_included_candidate_events(block_number: BlockNumber) -> Vec { + let candidate_receipt = make_candidate_receipt(get_block_number_hash(block_number)); + vec![ + CandidateEvent::CandidateIncluded( + candidate_receipt.clone(), + HeadData::default(), + CoreIndex::from(0), + GroupIndex::from(0), + ), + CandidateEvent::CandidateBacked( + candidate_receipt, + HeadData::default(), + CoreIndex::from(0), + GroupIndex::from(0), + ), + ] +} + +fn get_backed_candidate_event(block_number: BlockNumber) -> Vec { + let candidate_receipt = make_candidate_receipt(get_block_number_hash(block_number)); + vec![CandidateEvent::CandidateBacked( + candidate_receipt, HeadData::default(), CoreIndex::from(0), GroupIndex::from(0), )] } +/// Hash for a 'magic' candidate. This is meant to be a special candidate used to verify special cases. +fn get_magic_candidate_hash() -> Hash { + BlakeTwo256::hash(&"abc".encode()) +} +/// Get a dummy event that corresponds to candidate inclusion for a hardcoded block number. +/// Used to simulate candidates included multiple times at different block heights. +fn get_backed_and_included_magic_candidate_events( + _block_number: BlockNumber, +) -> Vec { + let candidate_receipt = make_candidate_receipt(get_magic_candidate_hash()); + vec![ + CandidateEvent::CandidateIncluded( + candidate_receipt.clone(), + HeadData::default(), + CoreIndex::from(0), + GroupIndex::from(0), + ), + CandidateEvent::CandidateBacked( + candidate_receipt, + HeadData::default(), + CoreIndex::from(0), + GroupIndex::from(0), + ), + ] +} -async fn assert_candidate_events_request(virtual_overseer: &mut VirtualOverseer, chain: &[Hash]) { +async fn assert_candidate_events_request( + virtual_overseer: &mut VirtualOverseer, + chain: &[Hash], + event_generator: F, +) where + F: Fn(u32) -> Vec, +{ assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::RuntimeApi(RuntimeApiMessage::Request( @@ -163,7 +222,7 @@ async fn assert_candidate_events_request(virtual_overseer: &mut VirtualOverseer, )) => { let maybe_block_number = chain.iter().position(|h| *h == hash); let response = maybe_block_number - .map(|num| get_candidate_included_events(num as u32)) + .map(|num| event_generator(num as u32)) .unwrap_or_default(); tx.send(Ok(response)).unwrap(); } @@ -207,12 +266,15 @@ async fn assert_block_ancestors_request(virtual_overseer: &mut VirtualOverseer, ); } -async fn overseer_process_active_leaves_update( +async fn overseer_process_active_leaves_update( virtual_overseer: &mut VirtualOverseer, chain: &[Hash], finalized_block: BlockNumber, expected_ancestry_len: usize, -) { + event_generator: F, +) where + F: Fn(u32) -> Vec + Clone, +{ // Before walking through ancestors provider requests latest finalized block number. assert_finalized_block_number_request(virtual_overseer, finalized_block).await; // Expect block ancestors requests with respect to the ancestry step. @@ -221,7 +283,7 @@ async fn overseer_process_active_leaves_update( } // For each ancestry and the head return corresponding candidates inclusions. for _ in 0..expected_ancestry_len { - assert_candidate_events_request(virtual_overseer, chain).await; + assert_candidate_events_request(virtual_overseer, chain, event_generator.clone()).await; assert_chain_vote_request(virtual_overseer, chain).await; } } @@ -236,7 +298,9 @@ fn scraper_provides_included_state_when_initialized() { let TestState { mut chain, mut scraper, mut ctx } = state; assert!(!scraper.is_candidate_included(&candidate_2.hash())); + assert!(!scraper.is_candidate_backed(&candidate_2.hash())); assert!(scraper.is_candidate_included(&candidate_1.hash())); + assert!(scraper.is_candidate_backed(&candidate_1.hash())); // After next active leaves update we should see the candidate included. let next_update = next_leaf(&mut chain); @@ -248,11 +312,13 @@ fn scraper_provides_included_state_when_initialized() { &chain, finalized_block_number, expected_ancestry_len, + get_backed_and_included_candidate_events, ); join(process_active_leaves_update(ctx.sender(), &mut scraper, next_update), overseer_fut) .await; assert!(scraper.is_candidate_included(&candidate_2.hash())); + assert!(scraper.is_candidate_backed(&candidate_2.hash())); }); } @@ -274,6 +340,7 @@ fn scraper_requests_candidates_of_leaf_ancestors() { &chain, finalized_block_number, BLOCKS_TO_SKIP, + get_backed_and_included_candidate_events, ); join(process_active_leaves_update(ctx.sender(), &mut scraper, next_update), overseer_fut) .await; @@ -282,6 +349,7 @@ fn scraper_requests_candidates_of_leaf_ancestors() { for block_number in 1..next_block_number { let candidate = make_candidate_receipt(get_block_number_hash(block_number)); assert!(scraper.is_candidate_included(&candidate.hash())); + assert!(scraper.is_candidate_backed(&candidate.hash())); } }); } @@ -304,6 +372,7 @@ fn scraper_requests_candidates_of_non_cached_ancestors() { &chain, finalized_block_number, BLOCKS_TO_SKIP[0], + get_backed_and_included_candidate_events, ); join(process_active_leaves_update(ctx.sender(), &mut ordering, next_update), overseer_fut) .await; @@ -315,6 +384,7 @@ fn scraper_requests_candidates_of_non_cached_ancestors() { &chain, finalized_block_number, BLOCKS_TO_SKIP[1], + get_backed_and_included_candidate_events, ); join(process_active_leaves_update(ctx.sender(), &mut ordering, next_update), overseer_fut) .await; @@ -340,8 +410,170 @@ fn scraper_requests_candidates_of_non_finalized_ancestors() { &chain, finalized_block_number, BLOCKS_TO_SKIP - finalized_block_number as usize, // Expect the provider not to go past finalized block. + get_backed_and_included_candidate_events, ); join(process_active_leaves_update(ctx.sender(), &mut ordering, next_update), overseer_fut) .await; }); } + +#[test] +fn scraper_prunes_finalized_candidates() { + const TEST_TARGET_BLOCK_NUMBER: BlockNumber = 2; + + // How many blocks should we skip before sending a leaf update. + const BLOCKS_TO_SKIP: usize = 3; + + futures::executor::block_on(async { + let (state, mut virtual_overseer) = TestState::new().await; + + let TestState { mut chain, mut scraper, mut ctx } = state; + + // 1 because `TestState` starts at leaf 1. + let next_update = (1..BLOCKS_TO_SKIP).map(|_| next_leaf(&mut chain)).last().unwrap(); + + let mut finalized_block_number = 1; + let expected_ancestry_len = BLOCKS_TO_SKIP - finalized_block_number as usize; + let overseer_fut = overseer_process_active_leaves_update( + &mut virtual_overseer, + &chain, + finalized_block_number, + expected_ancestry_len, + |block_num| { + if block_num == TEST_TARGET_BLOCK_NUMBER { + get_backed_and_included_candidate_events(block_num) + } else { + vec![] + } + }, + ); + join(process_active_leaves_update(ctx.sender(), &mut scraper, next_update), overseer_fut) + .await; + + let candidate = make_candidate_receipt(get_block_number_hash(TEST_TARGET_BLOCK_NUMBER)); + + // After `CANDIDATE_LIFETIME_AFTER_FINALIZATION` blocks the candidate should be removed + finalized_block_number = + TEST_TARGET_BLOCK_NUMBER + ChainScraper::CANDIDATE_LIFETIME_AFTER_FINALIZATION; + process_finalized_block(&mut scraper, &finalized_block_number); + + assert!(!scraper.is_candidate_backed(&candidate.hash())); + assert!(!scraper.is_candidate_included(&candidate.hash())); + }); +} + +#[test] +fn scraper_handles_backed_but_not_included_candidate() { + const TEST_TARGET_BLOCK_NUMBER: BlockNumber = 2; + + // How many blocks should we skip before sending a leaf update. + const BLOCKS_TO_SKIP: usize = 3; + + futures::executor::block_on(async { + let (state, mut virtual_overseer) = TestState::new().await; + + let TestState { mut chain, mut scraper, mut ctx } = state; + + let next_update = (1..BLOCKS_TO_SKIP as BlockNumber) + .map(|_| next_leaf(&mut chain)) + .last() + .unwrap(); + + // Add `ActiveLeavesUpdate` containing `CandidateBacked` event for block `BLOCK_WITH_EVENTS` + let mut finalized_block_number = 1; + let expected_ancestry_len = BLOCKS_TO_SKIP - finalized_block_number as usize; + let overseer_fut = overseer_process_active_leaves_update( + &mut virtual_overseer, + &chain, + finalized_block_number, + expected_ancestry_len, + |block_num| { + if block_num == TEST_TARGET_BLOCK_NUMBER { + get_backed_candidate_event(block_num) + } else { + vec![] + } + }, + ); + join(process_active_leaves_update(ctx.sender(), &mut scraper, next_update), overseer_fut) + .await; + + // Finalize blocks to enforce pruning of scraped events + finalized_block_number += 1; + process_finalized_block(&mut scraper, &finalized_block_number); + + // `FIRST_TEST_BLOCK` is finalized, which is within `BACKED_CANDIDATE_LIFETIME_AFTER_FINALIZATION` window. + // The candidate should still be backed. + let candidate = make_candidate_receipt(get_block_number_hash(TEST_TARGET_BLOCK_NUMBER)); + assert!(!scraper.is_candidate_included(&candidate.hash())); + assert!(scraper.is_candidate_backed(&candidate.hash())); + + // Bump the finalized block outside `BACKED_CANDIDATE_LIFETIME_AFTER_FINALIZATION`. + // The candidate should be removed. + assert!( + finalized_block_number < + TEST_TARGET_BLOCK_NUMBER + ChainScraper::CANDIDATE_LIFETIME_AFTER_FINALIZATION + ); + finalized_block_number += + TEST_TARGET_BLOCK_NUMBER + ChainScraper::CANDIDATE_LIFETIME_AFTER_FINALIZATION; + process_finalized_block(&mut scraper, &finalized_block_number); + + assert!(!scraper.is_candidate_included(&candidate.hash())); + assert!(!scraper.is_candidate_backed(&candidate.hash())); + }); +} + +#[test] +fn scraper_handles_the_same_candidate_incuded_in_two_different_block_heights() { + // Same candidate will be inclued in these two leaves + let test_targets = vec![2, 3]; + + // How many blocks should we skip before sending a leaf update. + const BLOCKS_TO_SKIP: usize = 3; + + futures::executor::block_on(async { + let (state, mut virtual_overseer) = TestState::new().await; + + let TestState { mut chain, mut scraper, mut ctx } = state; + + // 1 because `TestState` starts at leaf 1. + let next_update = (1..BLOCKS_TO_SKIP).map(|_| next_leaf(&mut chain)).last().unwrap(); + + // Now we will add the same magic candidate at two different block heights. + // Check `get_backed_and_included_magic_candidate_event` implementation + let mut finalized_block_number = 1; + let expected_ancestry_len = BLOCKS_TO_SKIP - finalized_block_number as usize; + let overseer_fut = overseer_process_active_leaves_update( + &mut virtual_overseer, + &chain, + finalized_block_number, + expected_ancestry_len, + |block_num| { + if test_targets.contains(&block_num) { + get_backed_and_included_magic_candidate_events(block_num) + } else { + vec![] + } + }, + ); + join(process_active_leaves_update(ctx.sender(), &mut scraper, next_update), overseer_fut) + .await; + + // Finalize blocks to enforce pruning of scraped events. + // The magic candidate was added twice, so it shouldn't be removed if we finalize two more blocks. + finalized_block_number = test_targets.first().expect("there are two block nums") + + ChainScraper::CANDIDATE_LIFETIME_AFTER_FINALIZATION; + process_finalized_block(&mut scraper, &finalized_block_number); + + let magic_candidate = make_candidate_receipt(get_magic_candidate_hash()); + assert!(scraper.is_candidate_backed(&magic_candidate.hash())); + assert!(scraper.is_candidate_included(&magic_candidate.hash())); + + // On the next finalization the magic candidate should be removed + finalized_block_number += 1; + process_finalized_block(&mut scraper, &finalized_block_number); + + assert!(!scraper.is_candidate_backed(&magic_candidate.hash())); + assert!(!scraper.is_candidate_included(&magic_candidate.hash())); + }); +} diff --git a/node/core/dispute-coordinator/src/tests.rs b/node/core/dispute-coordinator/src/tests.rs index a1ad315d2ea0..d7208c1a59eb 100644 --- a/node/core/dispute-coordinator/src/tests.rs +++ b/node/core/dispute-coordinator/src/tests.rs @@ -57,10 +57,10 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_test_helpers::{make_subsystem_context, TestSubsystemContextHandle}; use polkadot_primitives::v2::{ - ApprovalVote, BlockNumber, CandidateCommitments, CandidateHash, CandidateReceipt, - DisputeStatement, GroupIndex, Hash, Header, IndexedVec, MultiDisputeStatementSet, - ScrapedOnChainVotes, SessionIndex, SessionInfo, SigningContext, ValidDisputeStatementKind, - ValidatorId, ValidatorIndex, ValidatorSignature, + ApprovalVote, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, + CandidateReceipt, CoreIndex, DisputeStatement, GroupIndex, Hash, HeadData, Header, IndexedVec, + MultiDisputeStatementSet, ScrapedOnChainVotes, SessionIndex, SessionInfo, SigningContext, + ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorSignature, }; use crate::{ @@ -212,6 +212,7 @@ impl TestState { virtual_overseer: &mut VirtualOverseer, session: SessionIndex, block_number: BlockNumber, + candidate_events: Vec, ) { assert!(block_number > 0); @@ -239,8 +240,14 @@ impl TestState { ))) .await; - self.handle_sync_queries(virtual_overseer, block_hash, block_number, session) - .await; + self.handle_sync_queries( + virtual_overseer, + block_hash, + block_number, + session, + candidate_events, + ) + .await; } async fn handle_sync_queries( @@ -249,6 +256,7 @@ impl TestState { block_hash: Hash, block_number: BlockNumber, session: SessionIndex, + candidate_events: Vec, ) { // Order of messages is not fixed (different on initializing): #[derive(Debug)] @@ -352,7 +360,7 @@ impl TestState { _new_leaf, RuntimeApiRequest::CandidateEvents(tx), )) => { - tx.send(Ok(Vec::new())).unwrap(); + tx.send(Ok(candidate_events.clone())).unwrap(); } ); gum::trace!("After answering runtime api request"); @@ -401,8 +409,14 @@ impl TestState { ))) .await; - self.handle_sync_queries(virtual_overseer, *leaf, n as BlockNumber, session) - .await; + self.handle_sync_queries( + virtual_overseer, + *leaf, + n as BlockNumber, + session, + Vec::new(), + ) + .await; } } @@ -556,6 +570,26 @@ fn make_invalid_candidate_receipt() -> CandidateReceipt { dummy_candidate_receipt_bad_sig(Default::default(), Some(Default::default())) } +// Generate a `CandidateBacked` event from a `CandidateReceipt`. The rest is dummy data. +fn make_candidate_backed_event(candidate_receipt: CandidateReceipt) -> CandidateEvent { + CandidateEvent::CandidateBacked( + candidate_receipt, + HeadData(Vec::new()), + CoreIndex(0), + GroupIndex(0), + ) +} + +// Generate a `CandidateIncluded` event from a `CandidateReceipt`. The rest is dummy data. +fn make_candidate_included_event(candidate_receipt: CandidateReceipt) -> CandidateEvent { + CandidateEvent::CandidateIncluded( + candidate_receipt, + HeadData(Vec::new()), + CoreIndex(0), + GroupIndex(0), + ) +} + /// Handle request for approval votes: pub async fn handle_approval_vote_request( ctx_handle: &mut VirtualOverseer, @@ -587,7 +621,9 @@ fn too_many_unconfirmed_statements_are_considered_spam() { let candidate_receipt2 = make_invalid_candidate_receipt(); let candidate_hash2 = candidate_receipt2.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; let valid_vote1 = test_state .issue_backing_statement_with_index(ValidatorIndex(3), candidate_hash1, session) @@ -634,8 +670,9 @@ fn too_many_unconfirmed_statements_are_considered_spam() { handle_approval_vote_request(&mut virtual_overseer, &candidate_hash1, HashMap::new()) .await; - // Participation has to fail, otherwise the dispute will be confirmed. - participation_missing_availability(&mut virtual_overseer).await; + // Participation has to fail here, otherwise the dispute will be confirmed. However + // participation won't happen at all because the dispute is neither backed, not confirmed + // nor the candidate is included. Or in other words - we'll refrain from participation. { let (tx, rx) = oneshot::channel(); @@ -718,7 +755,9 @@ fn approval_vote_import_works() { let candidate_receipt1 = make_valid_candidate_receipt(); let candidate_hash1 = candidate_receipt1.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; let valid_vote1 = test_state .issue_backing_statement_with_index(ValidatorIndex(3), candidate_hash1, session) @@ -762,8 +801,8 @@ fn approval_vote_import_works() { handle_approval_vote_request(&mut virtual_overseer, &candidate_hash1, approval_votes) .await; - // Participation has to fail, otherwise the dispute will be confirmed. - participation_missing_availability(&mut virtual_overseer).await; + // Participation won't happen here because the dispute is neither backed, not confirmed + // nor the candidate is included. Or in other words - we'll refrain from participation. { let (tx, rx) = oneshot::channel(); @@ -814,7 +853,17 @@ fn dispute_gets_confirmed_via_participation() { let candidate_receipt2 = make_invalid_candidate_receipt(); let candidate_hash2 = candidate_receipt2.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session( + &mut virtual_overseer, + session, + 1, + vec![ + make_candidate_backed_event(candidate_receipt1.clone()), + make_candidate_backed_event(candidate_receipt2.clone()), + ], + ) + .await; let valid_vote1 = test_state .issue_explicit_statement_with_index( @@ -963,7 +1012,9 @@ fn dispute_gets_confirmed_at_byzantine_threshold() { let candidate_receipt2 = make_invalid_candidate_receipt(); let candidate_hash2 = candidate_receipt2.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; let valid_vote1 = test_state .issue_explicit_statement_with_index( @@ -1037,7 +1088,8 @@ fn dispute_gets_confirmed_at_byzantine_threshold() { handle_approval_vote_request(&mut virtual_overseer, &candidate_hash1, HashMap::new()) .await; - participation_missing_availability(&mut virtual_overseer).await; + // Participation won't happen here because the dispute is neither backed, not confirmed + // nor the candidate is included. Or in other words - we'll refrain from participation. { let (tx, rx) = oneshot::channel(); @@ -1124,7 +1176,9 @@ fn backing_statements_import_works_and_no_spam() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; let valid_vote1 = test_state .issue_backing_statement_with_index(ValidatorIndex(3), candidate_hash, session) @@ -1187,6 +1241,15 @@ fn backing_statements_import_works_and_no_spam() { .issue_backing_statement_with_index(ValidatorIndex(4), candidate_hash, session) .await; + test_state + .activate_leaf_at_session( + &mut virtual_overseer, + session, + 1, + vec![make_candidate_backed_event(candidate_receipt.clone())], + ) + .await; + let (pending_confirmation, confirmation_rx) = oneshot::channel(); // Backing vote import should not have accounted to spam slots, so this should succeed // as well: @@ -1228,7 +1291,14 @@ fn conflicting_votes_lead_to_dispute_participation() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session( + &mut virtual_overseer, + session, + 1, + vec![make_candidate_backed_event(candidate_receipt.clone())], + ) + .await; let valid_vote = test_state .issue_explicit_statement_with_index( @@ -1353,7 +1423,14 @@ fn positive_votes_dont_trigger_participation() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session( + &mut virtual_overseer, + session, + 1, + vec![make_candidate_backed_event(candidate_receipt.clone())], + ) + .await; let valid_vote = test_state .issue_explicit_statement_with_index( @@ -1466,7 +1543,9 @@ fn wrong_validator_index_is_ignored() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; let valid_vote = test_state .issue_explicit_statement_with_index( @@ -1544,7 +1623,14 @@ fn finality_votes_ignore_disputed_candidates() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session( + &mut virtual_overseer, + session, + 1, + vec![make_candidate_backed_event(candidate_receipt.clone())], + ) + .await; let valid_vote = test_state .issue_explicit_statement_with_index( @@ -1654,7 +1740,14 @@ fn supermajority_valid_dispute_may_be_finalized() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session( + &mut virtual_overseer, + session, + 1, + vec![make_candidate_backed_event(candidate_receipt.clone())], + ) + .await; let supermajority_threshold = polkadot_primitives::v2::supermajority_threshold(test_state.validators.len()); @@ -1794,7 +1887,14 @@ fn concluded_supermajority_for_non_active_after_time() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session( + &mut virtual_overseer, + session, + 1, + vec![make_candidate_backed_event(candidate_receipt.clone())], + ) + .await; let supermajority_threshold = polkadot_primitives::v2::supermajority_threshold(test_state.validators.len()); @@ -1912,7 +2012,14 @@ fn concluded_supermajority_against_non_active_after_time() { let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session( + &mut virtual_overseer, + session, + 1, + vec![make_candidate_backed_event(candidate_receipt.clone())], + ) + .await; let supermajority_threshold = polkadot_primitives::v2::supermajority_threshold(test_state.validators.len()); @@ -2036,7 +2143,9 @@ fn resume_dispute_without_local_statement() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; let valid_vote = test_state .issue_explicit_statement_with_index( @@ -2074,7 +2183,8 @@ fn resume_dispute_without_local_statement() { .await; // Missing availability -> No local vote. - participation_missing_availability(&mut virtual_overseer).await; + // Participation won't happen here because the dispute is neither backed, not confirmed + // nor the candidate is included. Or in other words - we'll refrain from participation. assert_eq!(confirmation_rx.await, Ok(ImportStatementsResult::ValidImport)); @@ -2216,7 +2326,14 @@ fn resume_dispute_with_local_statement() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session( + &mut virtual_overseer, + session, + 1, + vec![make_candidate_backed_event(candidate_receipt.clone())], + ) + .await; let local_valid_vote = test_state .issue_explicit_statement_with_index( @@ -2315,7 +2432,9 @@ fn resume_dispute_without_local_statement_or_local_key() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; let valid_vote = test_state .issue_explicit_statement_with_index( @@ -2408,7 +2527,9 @@ fn resume_dispute_with_local_statement_without_local_key() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; let local_valid_vote = test_state .issue_explicit_statement_with_index( @@ -2517,7 +2638,9 @@ fn issue_local_statement_does_cause_distribution_but_not_duplicate_participation let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; let other_vote = test_state .issue_explicit_statement_with_index( @@ -2590,7 +2713,9 @@ fn own_approval_vote_gets_distributed_on_dispute() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; let statement = test_state.issue_approval_vote_with_index( ValidatorIndex(0), @@ -2681,7 +2806,9 @@ fn negative_issue_local_statement_only_triggers_import() { let candidate_receipt = make_invalid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; virtual_overseer .send(FromOrchestra::Communication { @@ -2729,7 +2856,9 @@ fn redundant_votes_ignored() { let candidate_receipt = make_valid_candidate_receipt(); let candidate_hash = candidate_receipt.hash(); - test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await; + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; let valid_vote = test_state .issue_backing_statement_with_index(ValidatorIndex(1), candidate_hash, session) @@ -2787,3 +2916,195 @@ fn redundant_votes_ignored() { }) }); } + +#[test] +fn refrain_from_participation() { + test_harness(|mut test_state, mut virtual_overseer| { + Box::pin(async move { + let session = 1; + + test_state.handle_resume_sync(&mut virtual_overseer, session).await; + + let candidate_receipt = make_valid_candidate_receipt(); + let candidate_hash = candidate_receipt.hash(); + + // activate leaf - no backing/included event + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; + + // generate two votes + let valid_vote = test_state + .issue_explicit_statement_with_index( + ValidatorIndex(1), + candidate_hash, + session, + true, + ) + .await; + + let invalid_vote = test_state + .issue_explicit_statement_with_index( + ValidatorIndex(2), + candidate_hash, + session, + false, + ) + .await; + + virtual_overseer + .send(FromOrchestra::Communication { + msg: DisputeCoordinatorMessage::ImportStatements { + candidate_receipt: candidate_receipt.clone(), + session, + statements: vec![ + (valid_vote, ValidatorIndex(1)), + (invalid_vote, ValidatorIndex(2)), + ], + pending_confirmation: None, + }, + }) + .await; + + handle_approval_vote_request(&mut virtual_overseer, &candidate_hash, HashMap::new()) + .await; + + { + let (tx, rx) = oneshot::channel(); + virtual_overseer + .send(FromOrchestra::Communication { + msg: DisputeCoordinatorMessage::ActiveDisputes(tx), + }) + .await; + + assert_eq!(rx.await.unwrap().len(), 1); + + let (tx, rx) = oneshot::channel(); + virtual_overseer + .send(FromOrchestra::Communication { + msg: DisputeCoordinatorMessage::QueryCandidateVotes( + vec![(session, candidate_hash)], + tx, + ), + }) + .await; + + let (_, _, votes) = rx.await.unwrap().get(0).unwrap().clone(); + assert_eq!(votes.valid.len(), 1); + assert_eq!(votes.invalid.len(), 1); + } + + // activate leaf - no backing event + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; + + virtual_overseer.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; + + // confirm that no participation request is made. + assert!(virtual_overseer.try_recv().await.is_none()); + + test_state + }) + }); +} + +/// We have got no `participation_for_backed_candidates` test because most of the other tests (e.g. +/// `dispute_gets_confirmed_via_participation`, `backing_statements_import_works_and_no_spam`) use +/// candidate backing event to trigger participation. If they pass - that case works. +#[test] +fn participation_for_included_candidates() { + test_harness(|mut test_state, mut virtual_overseer| { + Box::pin(async move { + let session = 1; + + test_state.handle_resume_sync(&mut virtual_overseer, session).await; + + let candidate_receipt = make_valid_candidate_receipt(); + let candidate_hash = candidate_receipt.hash(); + + // activate leaf - with candidate included event + test_state + .activate_leaf_at_session( + &mut virtual_overseer, + session, + 1, + vec![make_candidate_included_event(candidate_receipt.clone())], + ) + .await; + + // generate two votes + let valid_vote = test_state + .issue_explicit_statement_with_index( + ValidatorIndex(1), + candidate_hash, + session, + true, + ) + .await; + + let invalid_vote = test_state + .issue_explicit_statement_with_index( + ValidatorIndex(2), + candidate_hash, + session, + false, + ) + .await; + + virtual_overseer + .send(FromOrchestra::Communication { + msg: DisputeCoordinatorMessage::ImportStatements { + candidate_receipt: candidate_receipt.clone(), + session, + statements: vec![ + (valid_vote, ValidatorIndex(1)), + (invalid_vote, ValidatorIndex(2)), + ], + pending_confirmation: None, + }, + }) + .await; + + handle_approval_vote_request(&mut virtual_overseer, &candidate_hash, HashMap::new()) + .await; + + participation_with_distribution( + &mut virtual_overseer, + &candidate_hash, + candidate_receipt.commitments_hash, + ) + .await; + + { + let (tx, rx) = oneshot::channel(); + virtual_overseer + .send(FromOrchestra::Communication { + msg: DisputeCoordinatorMessage::ActiveDisputes(tx), + }) + .await; + + assert_eq!(rx.await.unwrap().len(), 1); + + // check if we have participated (casted a vote) + let (tx, rx) = oneshot::channel(); + virtual_overseer + .send(FromOrchestra::Communication { + msg: DisputeCoordinatorMessage::QueryCandidateVotes( + vec![(session, candidate_hash)], + tx, + ), + }) + .await; + + let (_, _, votes) = rx.await.unwrap().get(0).unwrap().clone(); + assert_eq!(votes.valid.len(), 2); // 2 => we have participated + assert_eq!(votes.invalid.len(), 1); + } + + virtual_overseer.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; + + test_state + }) + }); +} diff --git a/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md b/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md index c04e15b1a0e9..3d44e210db0e 100644 --- a/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md +++ b/roadmap/implementers-guide/src/node/disputes/dispute-coordinator.md @@ -142,7 +142,7 @@ There are two potential caveats with this though: dispute concludes in all cases? The answer is nuanced, but in general we cannot rely on it. The problem is first, that finalization and approval-voting is an off-chain process so there is no global consensus: As - soon as at least f+1 honest (f= n/3, where n is the number of + soon as at least f+1 honest (f=n/3, where n is the number of validators/nodes) nodes have seen the dispute conclude, finalization will take place and approval votes will be cleared. This would still be fine, if we had some guarantees that those honest nodes will be able to include those @@ -214,7 +214,7 @@ among nodes which includes current block producers (current authority set) which is an important property: If the dispute carries on across an era change, we need to ensure that the new validator set will learn about any disputes and their votes, so they can put that information on chain. Dispute-distribution -luckily has this property and sends votes to the current authority set always. +luckily has this property and always sends votes to the current authority set. The issue is, for dispute-distribution, nodes send only their own explicit (or in some cases their approval vote) in addition to some opposing vote. This guarantees that at least some backing or approval vote will be present at the @@ -327,7 +327,10 @@ participation at all on any _vote import_ if any of the following holds true: - We have seen the disputed candidate backed in some not yet finalized block on at least one fork of the chain. This ensures the candidate is at least not completely made up and there has been some effort already flown into that - candidate. + candidate. Generally speaking a dispute shouldn't be raised for a candidate + which is backed but is not yet included. Disputes are raised during approval + checking. We participate on such disputes as a precaution - maybe we haven't + seen the `CandidateIncluded` event yet? - The dispute is already confirmed: Meaning that 1/3+1 nodes already participated, as this suggests in our threat model that there was at least one honest node that already voted, so the dispute must be genuine. From 254fbc4c2666a0be58aa10e031eabd0148839f5a Mon Sep 17 00:00:00 2001 From: Andronik Date: Sat, 12 Nov 2022 12:52:26 +0100 Subject: [PATCH 11/34] approval-voting: remove redundant validation check (#6266) * approval-voting: remove a redundant check * candidate-validation: remove unreachable check --- node/core/approval-voting/src/lib.rs | 20 +++----------------- node/core/candidate-validation/src/lib.rs | 5 ----- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index b96992df2c88..cfebd1065edc 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -2404,28 +2404,14 @@ async fn launch_approval( match val_rx.await { Err(_) => return ApprovalState::failed(validator_index, candidate_hash), - Ok(Ok(ValidationResult::Valid(commitments, _))) => { + Ok(Ok(ValidationResult::Valid(_, _))) => { // Validation checked out. Issue an approval command. If the underlying service is unreachable, // then there isn't anything we can do. gum::trace!(target: LOG_TARGET, ?candidate_hash, ?para_id, "Candidate Valid"); - let expected_commitments_hash = candidate.commitments_hash; - if commitments.hash() == expected_commitments_hash { - let _ = metrics_guard.take(); - return ApprovalState::approved(validator_index, candidate_hash) - } else { - // Commitments mismatch - issue a dispute. - issue_local_invalid_statement( - &mut sender, - session_index, - candidate_hash, - candidate.clone(), - ); - - metrics_guard.take().on_approval_invalid(); - return ApprovalState::failed(validator_index, candidate_hash) - } + let _ = metrics_guard.take(); + return ApprovalState::approved(validator_index, candidate_hash) }, Ok(Ok(ValidationResult::Invalid(reason))) => { gum::warn!( diff --git a/node/core/candidate-validation/src/lib.rs b/node/core/candidate-validation/src/lib.rs index a82a0feb78a0..7d9db4f3d794 100644 --- a/node/core/candidate-validation/src/lib.rs +++ b/node/core/candidate-validation/src/lib.rs @@ -467,11 +467,6 @@ where .await; if let Ok(ValidationResult::Valid(ref outputs, _)) = validation_result { - // If validation produces new commitments we consider the candidate invalid. - if candidate_receipt.commitments_hash != outputs.hash() { - return Ok(ValidationResult::Invalid(InvalidCandidate::CommitmentsHashMismatch)) - } - let (tx, rx) = oneshot::channel(); match runtime_api_request( sender, From b900d2a27a8d51015da18ea0f1e3f9d23a0498cb Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Sat, 12 Nov 2022 15:14:35 +0100 Subject: [PATCH 12/34] remove fill_block (#6200) Co-authored-by: parity-processbot <> --- runtime/kusama/src/lib.rs | 17 ++++------------- runtime/polkadot/src/lib.rs | 17 ++++------------- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index e399d7a0b35e..7ef451e52ea9 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -2046,7 +2046,7 @@ mod tests_fess { #[cfg(test)] mod multiplier_tests { use super::*; - use frame_support::{dispatch::GetDispatchInfo, traits::OnFinalize}; + use frame_support::{dispatch::DispatchInfo, traits::OnFinalize}; use runtime_common::{MinimumMultiplier, TargetBlockFullness}; use separator::Separatable; use sp_runtime::traits::Convert; @@ -2088,17 +2088,8 @@ mod multiplier_tests { let mut blocks = 0; let mut fees_paid = 0; - let call = frame_system::Call::::fill_block { - ratio: Perbill::from_rational( - block_weight.ref_time(), - BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap().ref_time(), - ), - }; - println!("calling {:?}", call); - let info = call.get_dispatch_info(); - // convert to outer call. - let call = RuntimeCall::System(call); - let len = call.using_encoded(|e| e.len()) as u32; + frame_system::Pallet::::set_block_consumed_resources(Weight::MAX, 0); + let info = DispatchInfo { weight: Weight::MAX, ..Default::default() }; let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::default() .build_storage::() @@ -2112,7 +2103,7 @@ mod multiplier_tests { while multiplier <= Multiplier::from_u32(1) { t.execute_with(|| { // imagine this tx was called. - let fee = TransactionPayment::compute_fee(len, &info, 0); + let fee = TransactionPayment::compute_fee(0, &info, 0); fees_paid += fee; // this will update the multiplier. diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index 8beb551db376..25b142a13be1 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -2299,7 +2299,7 @@ mod test { #[cfg(test)] mod multiplier_tests { use super::*; - use frame_support::{dispatch::GetDispatchInfo, traits::OnFinalize}; + use frame_support::{dispatch::DispatchInfo, traits::OnFinalize}; use runtime_common::{MinimumMultiplier, TargetBlockFullness}; use separator::Separatable; use sp_runtime::traits::Convert; @@ -2341,17 +2341,8 @@ mod multiplier_tests { let mut blocks = 0; let mut fees_paid = 0; - let call = frame_system::Call::::fill_block { - ratio: Perbill::from_rational( - block_weight.ref_time(), - BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap().ref_time(), - ), - }; - println!("calling {:?}", call); - let info = call.get_dispatch_info(); - // convert to outer call. - let call = RuntimeCall::System(call); - let len = call.using_encoded(|e| e.len()) as u32; + frame_system::Pallet::::set_block_consumed_resources(Weight::MAX, 0); + let info = DispatchInfo { weight: Weight::MAX, ..Default::default() }; let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::default() .build_storage::() @@ -2365,7 +2356,7 @@ mod multiplier_tests { while multiplier <= Multiplier::from_u32(1) { t.execute_with(|| { // imagine this tx was called. - let fee = TransactionPayment::compute_fee(len, &info, 0); + let fee = TransactionPayment::compute_fee(0, &info, 0); fees_paid += fee; // this will update the multiplier. From 5b60a5bb0a6cec981460c4c5498743f05e7c8e7b Mon Sep 17 00:00:00 2001 From: Andronik Date: Sun, 13 Nov 2022 20:14:24 +0100 Subject: [PATCH 13/34] fix a compilation warning (#6279) Fixes #6277. --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d6ff78d801e2..4b0e2047bf64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -126,7 +126,6 @@ maintenance = { status = "actively-developed" } # This list is ordered alphabetically. [profile.dev.package] blake2 = { opt-level = 3 } -blake2-rfc = { opt-level = 3 } blake2b_simd = { opt-level = 3 } chacha20poly1305 = { opt-level = 3 } cranelift-codegen = { opt-level = 3 } From 8d3ff430f656ee9fbcf599c3e6985315939e71ce Mon Sep 17 00:00:00 2001 From: eskimor Date: Mon, 14 Nov 2022 12:41:45 +0100 Subject: [PATCH 14/34] Only report concluded if there is an actual dispute. (#6270) * Only report concluded if there is an actual dispute. Hence no "non"-disputes will be added to disputes anymore. * Fix redundant check. * Test for no onesided disputes. Co-authored-by: eskimor --- node/core/dispute-coordinator/src/import.rs | 116 ++++++++---------- .../dispute-coordinator/src/initialized.rs | 65 +++++----- node/core/dispute-coordinator/src/tests.rs | 62 ++++++++++ node/primitives/src/disputes/message.rs | 3 + node/primitives/src/disputes/status.rs | 27 +++- 5 files changed, 166 insertions(+), 107 deletions(-) diff --git a/node/core/dispute-coordinator/src/import.rs b/node/core/dispute-coordinator/src/import.rs index 8eb3d173dcf7..c0f0d3d9e009 100644 --- a/node/core/dispute-coordinator/src/import.rs +++ b/node/core/dispute-coordinator/src/import.rs @@ -28,7 +28,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; -use polkadot_node_primitives::{CandidateVotes, SignedDisputeStatement}; +use polkadot_node_primitives::{CandidateVotes, DisputeStatus, SignedDisputeStatement, Timestamp}; use polkadot_node_subsystem_util::rolling_session_window::RollingSessionWindow; use polkadot_primitives::v2::{ CandidateReceipt, DisputeStatement, IndexedVec, SessionIndex, SessionInfo, @@ -154,22 +154,8 @@ pub struct CandidateVoteState { /// Information about own votes: own_vote: OwnVoteState, - /// Whether or not the dispute concluded invalid. - concluded_invalid: bool, - - /// Whether or not the dispute concluded valid. - /// - /// Note: Due to equivocations it is technically possible for a dispute to conclude both valid - /// and invalid. In that case the invalid result takes precedence. - concluded_valid: bool, - - /// There is an ongoing dispute and we reached f+1 votes -> the dispute is confirmed - /// - /// as at least one honest validator cast a vote for the candidate. - is_confirmed: bool, - - /// Whether or not we have an ongoing dispute. - is_disputed: bool, + /// Current dispute status, if there is any. + dispute_status: Option, } impl CandidateVoteState { @@ -179,18 +165,11 @@ impl CandidateVoteState { pub fn new_from_receipt(candidate_receipt: CandidateReceipt) -> Self { let votes = CandidateVotes { candidate_receipt, valid: BTreeMap::new(), invalid: BTreeMap::new() }; - Self { - votes, - own_vote: OwnVoteState::NoVote, - concluded_invalid: false, - concluded_valid: false, - is_confirmed: false, - is_disputed: false, - } + Self { votes, own_vote: OwnVoteState::NoVote, dispute_status: None } } /// Create a new `CandidateVoteState` from already existing votes. - pub fn new<'a>(votes: CandidateVotes, env: &CandidateEnvironment<'a>) -> Self { + pub fn new<'a>(votes: CandidateVotes, env: &CandidateEnvironment<'a>, now: Timestamp) -> Self { let own_vote = OwnVoteState::new(&votes, env); let n_validators = env.validators().len(); @@ -198,16 +177,31 @@ impl CandidateVoteState { let supermajority_threshold = polkadot_primitives::v2::supermajority_threshold(n_validators); - let concluded_invalid = votes.invalid.len() >= supermajority_threshold; - let concluded_valid = votes.valid.len() >= supermajority_threshold; - // We have a dispute, if we have votes on both sides: let is_disputed = !votes.invalid.is_empty() && !votes.valid.is_empty(); - let byzantine_threshold = polkadot_primitives::v2::byzantine_threshold(n_validators); - let is_confirmed = votes.voted_indices().len() > byzantine_threshold && is_disputed; + let dispute_status = if is_disputed { + let mut status = DisputeStatus::active(); + let byzantine_threshold = polkadot_primitives::v2::byzantine_threshold(n_validators); + let is_confirmed = votes.voted_indices().len() > byzantine_threshold; + if is_confirmed { + status = status.confirm(); + }; + let concluded_for = votes.valid.len() >= supermajority_threshold; + if concluded_for { + status = status.conclude_for(now); + }; + + let concluded_against = votes.invalid.len() >= supermajority_threshold; + if concluded_against { + status = status.conclude_against(now); + }; + Some(status) + } else { + None + }; - Self { votes, own_vote, concluded_invalid, concluded_valid, is_confirmed, is_disputed } + Self { votes, own_vote, dispute_status } } /// Import fresh statements. @@ -217,6 +211,7 @@ impl CandidateVoteState { self, env: &CandidateEnvironment, statements: Vec<(SignedDisputeStatement, ValidatorIndex)>, + now: Timestamp, ) -> ImportResult { let (mut votes, old_state) = self.into_old_state(); @@ -294,7 +289,7 @@ impl CandidateVoteState { } } - let new_state = Self::new(votes, env); + let new_state = Self::new(votes, env, now); ImportResult { old_state, @@ -313,32 +308,15 @@ impl CandidateVoteState { /// Extract `CandidateVotes` for handling import of new statements. fn into_old_state(self) -> (CandidateVotes, CandidateVoteState<()>) { - let CandidateVoteState { - votes, - own_vote, - concluded_invalid, - concluded_valid, - is_confirmed, - is_disputed, - } = self; - ( - votes, - CandidateVoteState { - votes: (), - own_vote, - concluded_invalid, - concluded_valid, - is_confirmed, - is_disputed, - }, - ) + let CandidateVoteState { votes, own_vote, dispute_status } = self; + (votes, CandidateVoteState { votes: (), own_vote, dispute_status }) } } impl CandidateVoteState { /// Whether or not we have an ongoing dispute. pub fn is_disputed(&self) -> bool { - self.is_disputed + self.dispute_status.is_some() } /// Whether there is an ongoing confirmed dispute. @@ -346,7 +324,7 @@ impl CandidateVoteState { /// This checks whether there is a dispute ongoing and we have more than byzantine threshold /// votes. pub fn is_confirmed(&self) -> bool { - self.is_confirmed + self.dispute_status.map_or(false, |s| s.is_confirmed_concluded()) } /// This machine already cast some vote in that dispute/for that candidate. @@ -359,14 +337,19 @@ impl CandidateVoteState { self.own_vote.approval_votes() } - /// Whether or not this dispute has already enough valid votes to conclude. - pub fn is_concluded_valid(&self) -> bool { - self.concluded_valid + /// Whether or not there is a dispute and it has already enough valid votes to conclude. + pub fn has_concluded_for(&self) -> bool { + self.dispute_status.map_or(false, |s| s.has_concluded_for()) + } + + /// Whether or not there is a dispute and it has already enough invalid votes to conclude. + pub fn has_concluded_against(&self) -> bool { + self.dispute_status.map_or(false, |s| s.has_concluded_against()) } - /// Whether or not this dispute has already enough invalid votes to conclude. - pub fn is_concluded_invalid(&self) -> bool { - self.concluded_invalid + /// Get access to the dispute status, in case there is one. + pub fn dispute_status(&self) -> &Option { + &self.dispute_status } /// Access to underlying votes. @@ -451,18 +434,18 @@ impl ImportResult { } /// Whether or not any dispute just concluded valid due to the import. - pub fn is_freshly_concluded_valid(&self) -> bool { - !self.old_state().is_concluded_valid() && self.new_state().is_concluded_valid() + pub fn is_freshly_concluded_for(&self) -> bool { + !self.old_state().has_concluded_for() && self.new_state().has_concluded_for() } /// Whether or not any dispute just concluded invalid due to the import. - pub fn is_freshly_concluded_invalid(&self) -> bool { - !self.old_state().is_concluded_invalid() && self.new_state().is_concluded_invalid() + pub fn is_freshly_concluded_against(&self) -> bool { + !self.old_state().has_concluded_against() && self.new_state().has_concluded_against() } /// Whether or not any dispute just concluded either invalid or valid due to the import. pub fn is_freshly_concluded(&self) -> bool { - self.is_freshly_concluded_invalid() || self.is_freshly_concluded_valid() + self.is_freshly_concluded_against() || self.is_freshly_concluded_for() } /// Modify this `ImportResult`s, by importing additional approval votes. @@ -473,6 +456,7 @@ impl ImportResult { self, env: &CandidateEnvironment, approval_votes: HashMap, + now: Timestamp, ) -> Self { let Self { old_state, @@ -508,7 +492,7 @@ impl ImportResult { } } - let new_state = CandidateVoteState::new(votes, env); + let new_state = CandidateVoteState::new(votes, env, now); Self { old_state, diff --git a/node/core/dispute-coordinator/src/initialized.rs b/node/core/dispute-coordinator/src/initialized.rs index 901a6d863ed2..0df1a620826c 100644 --- a/node/core/dispute-coordinator/src/initialized.rs +++ b/node/core/dispute-coordinator/src/initialized.rs @@ -748,7 +748,7 @@ impl Initialized { .load_candidate_votes(session, &candidate_hash)? .map(CandidateVotes::from) { - Some(votes) => CandidateVoteState::new(votes, &env), + Some(votes) => CandidateVoteState::new(votes, &env, now), None => if let MaybeCandidateReceipt::Provides(candidate_receipt) = candidate_receipt { CandidateVoteState::new_from_receipt(candidate_receipt) @@ -766,7 +766,7 @@ impl Initialized { gum::trace!(target: LOG_TARGET, ?candidate_hash, ?session, "Loaded votes"); let import_result = { - let intermediate_result = old_state.import_statements(&env, statements); + let intermediate_result = old_state.import_statements(&env, statements, now); // Handle approval vote import: // @@ -803,7 +803,7 @@ impl Initialized { ); intermediate_result }, - Ok(votes) => intermediate_result.import_approval_votes(&env, votes), + Ok(votes) => intermediate_result.import_approval_votes(&env, votes, now), } } else { gum::trace!( @@ -977,43 +977,34 @@ impl Initialized { } // All good, update recent disputes if state has changed: - if import_result.dispute_state_changed() { - let mut recent_disputes = overlay_db.load_recent_disputes()?.unwrap_or_default(); + if let Some(new_status) = new_state.dispute_status() { + // Only bother with db access, if there was an actual change. + if import_result.dispute_state_changed() { + let mut recent_disputes = overlay_db.load_recent_disputes()?.unwrap_or_default(); + + let status = + recent_disputes.entry((session, candidate_hash)).or_insert_with(|| { + gum::info!( + target: LOG_TARGET, + ?candidate_hash, + session, + "New dispute initiated for candidate.", + ); + DisputeStatus::active() + }); + + *status = *new_status; - let status = recent_disputes.entry((session, candidate_hash)).or_insert_with(|| { - gum::info!( + gum::trace!( target: LOG_TARGET, ?candidate_hash, - session, - "New dispute initiated for candidate.", + ?status, + has_concluded_for = ?new_state.has_concluded_for(), + has_concluded_against = ?new_state.has_concluded_against(), + "Writing recent disputes with updates for candidate" ); - DisputeStatus::active() - }); - - if new_state.is_confirmed() { - *status = status.confirm(); + overlay_db.write_recent_disputes(recent_disputes); } - - // Note: concluded-invalid overwrites concluded-valid, - // so we do this check first. Dispute state machine is - // non-commutative. - if new_state.is_concluded_valid() { - *status = status.concluded_for(now); - } - - if new_state.is_concluded_invalid() { - *status = status.concluded_against(now); - } - - gum::trace!( - target: LOG_TARGET, - ?candidate_hash, - ?status, - is_concluded_valid = ?new_state.is_concluded_valid(), - is_concluded_invalid = ?new_state.is_concluded_invalid(), - "Writing recent disputes with updates for candidate" - ); - overlay_db.write_recent_disputes(recent_disputes); } // Update metrics: @@ -1036,7 +1027,7 @@ impl Initialized { ); self.metrics.on_approval_votes(import_result.imported_approval_votes()); - if import_result.is_freshly_concluded_valid() { + if import_result.is_freshly_concluded_for() { gum::info!( target: LOG_TARGET, ?candidate_hash, @@ -1045,7 +1036,7 @@ impl Initialized { ); self.metrics.on_concluded_valid(); } - if import_result.is_freshly_concluded_invalid() { + if import_result.is_freshly_concluded_against() { gum::info!( target: LOG_TARGET, ?candidate_hash, diff --git a/node/core/dispute-coordinator/src/tests.rs b/node/core/dispute-coordinator/src/tests.rs index d7208c1a59eb..b2f779041a4c 100644 --- a/node/core/dispute-coordinator/src/tests.rs +++ b/node/core/dispute-coordinator/src/tests.rs @@ -2917,6 +2917,68 @@ fn redundant_votes_ignored() { }); } +#[test] +/// Make sure no disputes are recorded when there are no opposing votes, even if we reached supermajority. +fn no_onesided_disputes() { + test_harness(|mut test_state, mut virtual_overseer| { + Box::pin(async move { + let session = 1; + + test_state.handle_resume_sync(&mut virtual_overseer, session).await; + + let candidate_receipt = make_valid_candidate_receipt(); + let candidate_hash = candidate_receipt.hash(); + test_state + .activate_leaf_at_session(&mut virtual_overseer, session, 1, Vec::new()) + .await; + + let mut statements = Vec::new(); + for index in 1..10 { + statements.push(( + test_state + .issue_backing_statement_with_index( + ValidatorIndex(index), + candidate_hash, + session, + ) + .await, + ValidatorIndex(index), + )); + } + + let (pending_confirmation, confirmation_rx) = oneshot::channel(); + virtual_overseer + .send(FromOrchestra::Communication { + msg: DisputeCoordinatorMessage::ImportStatements { + candidate_receipt: candidate_receipt.clone(), + session, + statements, + pending_confirmation: Some(pending_confirmation), + }, + }) + .await; + assert_matches!(confirmation_rx.await, Ok(ImportStatementsResult::ValidImport)); + + // We should not have any active disputes now. + let (tx, rx) = oneshot::channel(); + virtual_overseer + .send(FromOrchestra::Communication { + msg: DisputeCoordinatorMessage::ActiveDisputes(tx), + }) + .await; + + assert!(rx.await.unwrap().is_empty()); + + virtual_overseer.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; + + // No more messages expected: + assert!(virtual_overseer.try_recv().await.is_none()); + + test_state + }) + }); +} + #[test] fn refrain_from_participation() { test_harness(|mut test_state, mut virtual_overseer| { diff --git a/node/primitives/src/disputes/message.rs b/node/primitives/src/disputes/message.rs index 93d4e804f906..1a943f8dcee6 100644 --- a/node/primitives/src/disputes/message.rs +++ b/node/primitives/src/disputes/message.rs @@ -33,6 +33,9 @@ use polkadot_primitives::v2::{ /// /// And most likely has been constructed correctly. This is used with /// `DisputeDistributionMessage::SendDispute` for sending out votes. +/// +/// NOTE: This is sent over the wire, any changes are a change in protocol and need to be +/// versioned. #[derive(Debug, Clone)] pub struct DisputeMessage(UncheckedDisputeMessage); diff --git a/node/primitives/src/disputes/status.rs b/node/primitives/src/disputes/status.rs index c0ffb907423b..7cc26c1a1f7a 100644 --- a/node/primitives/src/disputes/status.rs +++ b/node/primitives/src/disputes/status.rs @@ -19,8 +19,12 @@ use parity_scale_codec::{Decode, Encode}; /// Timestamp based on the 1 Jan 1970 UNIX base, which is persistent across node restarts and OS reboots. pub type Timestamp = u64; -/// The status of dispute. This is a state machine which can be altered by the -/// helper methods. +/// The status of dispute. +/// +/// As managed by the dispute coordinator. +/// +/// NOTE: This status is persisted to the database, any changes have to be versioned and a db +/// migration will be needed. #[derive(Debug, Clone, Copy, Encode, Decode, PartialEq)] pub enum DisputeStatus { /// The dispute is active and unconcluded. @@ -69,9 +73,24 @@ impl DisputeStatus { } } + /// Concluded valid? + pub fn has_concluded_for(&self) -> bool { + match self { + &DisputeStatus::ConcludedFor(_) => true, + _ => false, + } + } + /// Concluded invalid? + pub fn has_concluded_against(&self) -> bool { + match self { + &DisputeStatus::ConcludedAgainst(_) => true, + _ => false, + } + } + /// Transition the status to a new status after observing the dispute has concluded for the candidate. /// This may be a no-op if the status was already concluded. - pub fn concluded_for(self, now: Timestamp) -> DisputeStatus { + pub fn conclude_for(self, now: Timestamp) -> DisputeStatus { match self { DisputeStatus::Active | DisputeStatus::Confirmed => DisputeStatus::ConcludedFor(now), DisputeStatus::ConcludedFor(at) => DisputeStatus::ConcludedFor(std::cmp::min(at, now)), @@ -81,7 +100,7 @@ impl DisputeStatus { /// Transition the status to a new status after observing the dispute has concluded against the candidate. /// This may be a no-op if the status was already concluded. - pub fn concluded_against(self, now: Timestamp) -> DisputeStatus { + pub fn conclude_against(self, now: Timestamp) -> DisputeStatus { match self { DisputeStatus::Active | DisputeStatus::Confirmed => DisputeStatus::ConcludedAgainst(now), From 71b5aac03a488c004abd4a7b95621362c4aca39b Mon Sep 17 00:00:00 2001 From: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Date: Mon, 14 Nov 2022 12:59:56 +0100 Subject: [PATCH 15/34] [ci] fix buildah image (#6281) --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0dd640186bb8..877004694d79 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -152,7 +152,7 @@ default: .build-push-image: extends: - .kubernetes-env - image: quay.io/buildah/stable + image: quay.io/buildah/stable:v1.27 before_script: - test -s ./artifacts/VERSION || exit 1 - test -s ./artifacts/EXTRATAG || exit 1 From ed143f2767c6dc0535ad7af177bda56164204708 Mon Sep 17 00:00:00 2001 From: eskimor Date: Tue, 15 Nov 2022 10:04:24 +0100 Subject: [PATCH 16/34] Revert special casing of Kusama for grandpa rounds. (#6217) Co-authored-by: eskimor --- node/service/src/lib.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/node/service/src/lib.rs b/node/service/src/lib.rs index 18218a8aba8e..3e535f3bd1f5 100644 --- a/node/service/src/lib.rs +++ b/node/service/src/lib.rs @@ -1221,22 +1221,11 @@ where } } - // Reduce grandpa load on Kusama and test networks. This will slow down finality by - // approximately one slot duration, but will reduce load. We would like to see the impact on - // Kusama, see: https://github.com/paritytech/polkadot/issues/5464 - let gossip_duration = if chain_spec.is_versi() || - chain_spec.is_wococo() || - chain_spec.is_rococo() || - chain_spec.is_kusama() - { - Duration::from_millis(2000) - } else { - Duration::from_millis(1000) - }; - let config = grandpa::Config { // FIXME substrate#1578 make this available through chainspec - gossip_duration, + // Grandpa performance can be improved a bit by tuning this parameter, see: + // https://github.com/paritytech/polkadot/issues/5464 + gossip_duration: Duration::from_millis(1000), justification_period: 512, name: Some(name), observer_enabled: false, From 17b75670bf775f01de894d55d82c3b12802d0c05 Mon Sep 17 00:00:00 2001 From: Marcin S Date: Tue, 15 Nov 2022 09:58:26 -0500 Subject: [PATCH 17/34] Fixes "for loop over an `Option`" warnings (#6291) Was seeing these warnings when running `cargo check --all`: ``` warning: for loop over an `Option`. This is more readably written as an `if let` statement --> node/core/approval-voting/src/lib.rs:1147:21 | 1147 | for activated in update.activated { | ^^^^^^^^^^^^^^^^ | = note: `#[warn(for_loops_over_fallibles)]` on by default help: to check pattern in a loop use `while let` | 1147 | while let Some(activated) = update.activated { | ~~~~~~~~~~~~~~~ ~~~ help: consider using `if let` to clear intent | 1147 | if let Some(activated) = update.activated { | ~~~~~~~~~~~~ ~~~ ``` My guess is that `activated` used to be a SmallVec or similar, as is `deactivated`. It was changed to an `Option`, the `for` still compiled (it's technically correct, just weird), and the compiler didn't catch it until now. --- node/core/approval-voting/src/lib.rs | 2 +- node/core/bitfield-signing/src/lib.rs | 2 +- node/core/chain-selection/src/lib.rs | 2 +- node/core/provisioner/src/lib.rs | 2 +- node/network/availability-recovery/src/lib.rs | 2 +- node/network/bitfield-distribution/src/lib.rs | 2 +- node/network/bridge/src/rx/mod.rs | 2 +- node/network/statement-distribution/src/lib.rs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index cfebd1065edc..bc63549795c2 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -1144,7 +1144,7 @@ async fn handle_from_overseer( FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)) => { let mut actions = Vec::new(); - for activated in update.activated { + if let Some(activated) = update.activated { let head = activated.hash; match import::handle_new_head(ctx, state, db, head, &*last_finalized_height).await { Err(e) => return Err(SubsystemError::with_origin("db", e)), diff --git a/node/core/bitfield-signing/src/lib.rs b/node/core/bitfield-signing/src/lib.rs index 13a3dd28705f..64cbb18cc44c 100644 --- a/node/core/bitfield-signing/src/lib.rs +++ b/node/core/bitfield-signing/src/lib.rs @@ -225,7 +225,7 @@ async fn run( } } - for leaf in update.activated { + if let Some(leaf) = update.activated { let sender = ctx.sender().clone(); let leaf_hash = leaf.hash; diff --git a/node/core/chain-selection/src/lib.rs b/node/core/chain-selection/src/lib.rs index eb5ceac9b768..e5ffe6811d6e 100644 --- a/node/core/chain-selection/src/lib.rs +++ b/node/core/chain-selection/src/lib.rs @@ -430,7 +430,7 @@ where return Ok(()) } FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)) => { - for leaf in update.activated { + if let Some(leaf) = update.activated { let write_ops = handle_active_leaf( ctx.sender(), &*backend, diff --git a/node/core/provisioner/src/lib.rs b/node/core/provisioner/src/lib.rs index f669f9ddae2c..0530d48aabda 100644 --- a/node/core/provisioner/src/lib.rs +++ b/node/core/provisioner/src/lib.rs @@ -183,7 +183,7 @@ fn handle_active_leaves_update( per_relay_parent.remove(deactivated); } - for leaf in update.activated { + if let Some(leaf) = update.activated { let delay_fut = Delay::new(PRE_PROPOSE_TIMEOUT).map(move |_| leaf.hash).boxed(); per_relay_parent.insert(leaf.hash, PerRelayParent::new(leaf)); inherent_delays.push(delay_fut); diff --git a/node/network/availability-recovery/src/lib.rs b/node/network/availability-recovery/src/lib.rs index 4ca28c955f9e..5a2905082379 100644 --- a/node/network/availability-recovery/src/lib.rs +++ b/node/network/availability-recovery/src/lib.rs @@ -836,7 +836,7 @@ async fn handle_signal(state: &mut State, signal: OverseerSignal) -> SubsystemRe OverseerSignal::Conclude => Ok(true), OverseerSignal::ActiveLeaves(ActiveLeavesUpdate { activated, .. }) => { // if activated is non-empty, set state.live_block to the highest block in `activated` - for activated in activated { + if let Some(activated) = activated { if activated.number > state.live_block.0 { state.live_block = (activated.number, activated.hash) } diff --git a/node/network/bitfield-distribution/src/lib.rs b/node/network/bitfield-distribution/src/lib.rs index 1b2167484b49..2a1e3b8d9ef3 100644 --- a/node/network/bitfield-distribution/src/lib.rs +++ b/node/network/bitfield-distribution/src/lib.rs @@ -233,7 +233,7 @@ impl BitfieldDistribution { })) => { let _timer = self.metrics.time_active_leaves_update(); - for activated in activated { + if let Some(activated) = activated { let relay_parent = activated.hash; gum::trace!(target: LOG_TARGET, ?relay_parent, "activated"); diff --git a/node/network/bridge/src/rx/mod.rs b/node/network/bridge/src/rx/mod.rs index a08596cd15ac..0cb89b19b0ee 100644 --- a/node/network/bridge/src/rx/mod.rs +++ b/node/network/bridge/src/rx/mod.rs @@ -566,7 +566,7 @@ where num_deactivated = %deactivated.len(), ); - for activated in activated { + if let Some(activated) = activated { let pos = live_heads .binary_search_by(|probe| probe.number.cmp(&activated.number).reverse()) .unwrap_or_else(|i| i); diff --git a/node/network/statement-distribution/src/lib.rs b/node/network/statement-distribution/src/lib.rs index c0ac09dbe3f8..055fd4123f9a 100644 --- a/node/network/statement-distribution/src/lib.rs +++ b/node/network/statement-distribution/src/lib.rs @@ -2017,7 +2017,7 @@ impl StatementDistributionSubsystem { } } - for activated in activated { + if let Some(activated) = activated { let relay_parent = activated.hash; let span = PerLeafSpan::new(activated.span, "statement-distribution"); gum::trace!( From 5d607f620373db6c481915a0fe6f1b334e7f847b Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Tue, 15 Nov 2022 17:30:50 +0100 Subject: [PATCH 18/34] companion for #12599 (#6290) * companion for #12599 * update Cargo.lock * use cargo path instead of diener * update lockfile for {"substrate"} Co-authored-by: parity-processbot <> --- Cargo.lock | 415 ++++++++++++++++++++++++++--------------------------- 1 file changed, 200 insertions(+), 215 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 765c025ff027..e244c01e3c03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -426,7 +426,7 @@ dependencies = [ [[package]] name = "beefy-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "array-bytes", "async-trait", @@ -463,7 +463,7 @@ dependencies = [ [[package]] name = "beefy-gadget-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "beefy-gadget", "beefy-primitives", @@ -483,7 +483,7 @@ dependencies = [ [[package]] name = "beefy-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "beefy-primitives", "sp-api", @@ -493,7 +493,7 @@ dependencies = [ [[package]] name = "beefy-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "scale-info", @@ -2013,7 +2013,7 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", ] @@ -2037,7 +2037,7 @@ checksum = "85dcb89d2b10c5f6133de2efd8c11959ce9dbb46a2f7a4cab208c4eeda6ce1ab" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "frame-system", @@ -2060,7 +2060,7 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "Inflector", "array-bytes", @@ -2112,7 +2112,7 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2123,7 +2123,7 @@ dependencies = [ [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-election-provider-solution-type", "frame-support", @@ -2139,7 +2139,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "frame-system", @@ -2168,7 +2168,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "bitflags", "frame-metadata", @@ -2200,7 +2200,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "Inflector", "cfg-expr", @@ -2214,7 +2214,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", @@ -2226,7 +2226,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "proc-macro2", "quote", @@ -2236,7 +2236,7 @@ dependencies = [ [[package]] name = "frame-support-test" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "frame-support-test-pallet", @@ -2259,7 +2259,7 @@ dependencies = [ [[package]] name = "frame-support-test-pallet" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "frame-system", @@ -2270,7 +2270,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "log", @@ -2288,7 +2288,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -2303,7 +2303,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "sp-api", @@ -2312,7 +2312,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "parity-scale-codec", @@ -2483,7 +2483,7 @@ dependencies = [ [[package]] name = "generate-bags" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "chrono", "frame-election-provider-support", @@ -4611,7 +4611,7 @@ checksum = "20448fd678ec04e6ea15bbe0476874af65e98a01515d667aa49f1434dc44ebf4" [[package]] name = "pallet-assets" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -4625,7 +4625,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "frame-system", @@ -4641,7 +4641,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "frame-system", @@ -4656,7 +4656,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -4700,7 +4700,7 @@ dependencies = [ [[package]] name = "pallet-bags-list-remote-tests" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-election-provider-support", "frame-support", @@ -4719,7 +4719,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -4734,7 +4734,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "beefy-primitives", "frame-support", @@ -4750,7 +4750,7 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "array-bytes", "beefy-merkle-tree", @@ -4773,7 +4773,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -4791,7 +4791,7 @@ dependencies = [ [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -4810,7 +4810,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -4827,7 +4827,7 @@ dependencies = [ [[package]] name = "pallet-conviction-voting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "assert_matches", "frame-benchmarking", @@ -4844,7 +4844,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -4862,7 +4862,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -4886,7 +4886,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -4899,7 +4899,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -4917,7 +4917,7 @@ dependencies = [ [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -4935,7 +4935,7 @@ dependencies = [ [[package]] name = "pallet-gilt" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -4950,7 +4950,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -4973,7 +4973,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "enumflags2", "frame-benchmarking", @@ -4989,7 +4989,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5009,7 +5009,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5026,7 +5026,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5043,7 +5043,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "ckb-merkle-mountain-range", "frame-benchmarking", @@ -5061,7 +5061,7 @@ dependencies = [ [[package]] name = "pallet-mmr-rpc" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "anyhow", "jsonrpsee", @@ -5077,7 +5077,7 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5093,7 +5093,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "frame-system", @@ -5110,7 +5110,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-benchmarking" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5130,7 +5130,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "sp-api", @@ -5140,7 +5140,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "frame-system", @@ -5157,7 +5157,7 @@ dependencies = [ [[package]] name = "pallet-offences-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5180,7 +5180,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5197,7 +5197,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5212,7 +5212,7 @@ dependencies = [ [[package]] name = "pallet-ranked-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5230,7 +5230,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5245,7 +5245,7 @@ dependencies = [ [[package]] name = "pallet-referenda" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "assert_matches", "frame-benchmarking", @@ -5263,7 +5263,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5279,7 +5279,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "frame-system", @@ -5300,7 +5300,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5316,7 +5316,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "frame-system", @@ -5330,7 +5330,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5353,7 +5353,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -5364,7 +5364,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "log", "sp-arithmetic", @@ -5373,7 +5373,7 @@ dependencies = [ [[package]] name = "pallet-state-trie-migration" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4a5a9dea00c9b4e4d34ff56368451aa4dac09d77" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5390,7 +5390,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "frame-system", @@ -5404,7 +5404,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5422,7 +5422,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5441,7 +5441,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-support", "frame-system", @@ -5457,7 +5457,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", @@ -5473,7 +5473,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -5485,7 +5485,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5502,7 +5502,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5518,7 +5518,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -5533,7 +5533,7 @@ dependencies = [ [[package]] name = "pallet-whitelist" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-benchmarking", "frame-support", @@ -8007,7 +8007,7 @@ dependencies = [ [[package]] name = "remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "env_logger 0.9.0", "log", @@ -8347,7 +8347,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "log", "sp-core", @@ -8358,7 +8358,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "futures", @@ -8385,7 +8385,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "futures", "futures-timer", @@ -8408,7 +8408,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -8424,7 +8424,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "impl-trait-for-tuples", "memmap2", @@ -8441,7 +8441,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -8452,7 +8452,7 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "array-bytes", "chrono", @@ -8492,7 +8492,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "fnv", "futures", @@ -8520,7 +8520,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "hash-db", "kvdb", @@ -8545,7 +8545,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "futures", @@ -8569,7 +8569,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "fork-tree", @@ -8610,7 +8610,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "futures", "jsonrpsee", @@ -8632,7 +8632,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "fork-tree", "parity-scale-codec", @@ -8645,7 +8645,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "futures", @@ -8669,7 +8669,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "lazy_static", "lru", @@ -8685,7 +8685,6 @@ dependencies = [ "sp-io", "sp-panic-handler", "sp-runtime-interface", - "sp-tasks", "sp-trie", "sp-version", "sp-wasm-interface", @@ -8696,7 +8695,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "environmental", "parity-scale-codec", @@ -8712,7 +8711,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "log", "parity-scale-codec", @@ -8727,7 +8726,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "cfg-if", "libc", @@ -8747,7 +8746,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "ahash", "array-bytes", @@ -8788,7 +8787,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "finality-grandpa", "futures", @@ -8809,7 +8808,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "ansi_term", "futures", @@ -8826,7 +8825,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "array-bytes", "async-trait", @@ -8841,7 +8840,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "array-bytes", "async-trait", @@ -8888,7 +8887,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "cid", "futures", @@ -8908,7 +8907,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "bitflags", @@ -8934,7 +8933,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "ahash", "futures", @@ -8952,7 +8951,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "array-bytes", "futures", @@ -8973,7 +8972,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "array-bytes", "fork-tree", @@ -9003,7 +9002,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "array-bytes", "futures", @@ -9022,7 +9021,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "array-bytes", "bytes", @@ -9052,7 +9051,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "futures", "libp2p", @@ -9065,7 +9064,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -9074,7 +9073,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "futures", "hash-db", @@ -9104,7 +9103,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "futures", "jsonrpsee", @@ -9127,7 +9126,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "futures", "jsonrpsee", @@ -9140,7 +9139,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "futures", "hex", @@ -9159,7 +9158,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "directories", @@ -9230,7 +9229,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "log", "parity-scale-codec", @@ -9244,7 +9243,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -9263,7 +9262,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "futures", "libc", @@ -9282,7 +9281,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "chrono", "futures", @@ -9300,7 +9299,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "ansi_term", "atty", @@ -9331,7 +9330,7 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -9342,7 +9341,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "futures", @@ -9369,7 +9368,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "futures", @@ -9383,7 +9382,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "futures", "futures-timer", @@ -9864,7 +9863,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "hash-db", "log", @@ -9882,7 +9881,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "blake2", "proc-macro-crate", @@ -9893,8 +9892,8 @@ dependencies = [ [[package]] name = "sp-application-crypto" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "scale-info", @@ -9906,8 +9905,8 @@ dependencies = [ [[package]] name = "sp-arithmetic" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "integer-sqrt", "num-traits", @@ -9922,7 +9921,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "scale-info", @@ -9935,7 +9934,7 @@ dependencies = [ [[package]] name = "sp-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "parity-scale-codec", @@ -9947,7 +9946,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "sp-api", @@ -9959,7 +9958,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "futures", "log", @@ -9977,7 +9976,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "futures", @@ -9996,7 +9995,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "merlin", @@ -10019,7 +10018,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "scale-info", @@ -10033,7 +10032,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "scale-info", @@ -10045,8 +10044,8 @@ dependencies = [ [[package]] name = "sp-core" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "array-bytes", "base58", @@ -10065,7 +10064,6 @@ dependencies = [ "merlin", "num-traits", "parity-scale-codec", - "parity-util-mem", "parking_lot 0.12.1", "primitive-types", "rand 0.7.3", @@ -10091,8 +10089,8 @@ dependencies = [ [[package]] name = "sp-core-hashing" -version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "blake2", "byteorder", @@ -10106,7 +10104,7 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "proc-macro2", "quote", @@ -10117,7 +10115,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -10125,8 +10123,8 @@ dependencies = [ [[package]] name = "sp-debug-derive" -version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "proc-macro2", "quote", @@ -10135,8 +10133,8 @@ dependencies = [ [[package]] name = "sp-externalities" -version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "environmental", "parity-scale-codec", @@ -10147,7 +10145,7 @@ dependencies = [ [[package]] name = "sp-finality-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "finality-grandpa", "log", @@ -10165,7 +10163,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -10178,8 +10176,8 @@ dependencies = [ [[package]] name = "sp-io" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "bytes", "futures", @@ -10205,7 +10203,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "lazy_static", "sp-core", @@ -10215,8 +10213,8 @@ dependencies = [ [[package]] name = "sp-keystore" -version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "futures", @@ -10233,7 +10231,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "thiserror", "zstd", @@ -10242,7 +10240,7 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "log", "parity-scale-codec", @@ -10259,7 +10257,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "scale-info", @@ -10273,7 +10271,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "sp-api", "sp-core", @@ -10282,8 +10280,8 @@ dependencies = [ [[package]] name = "sp-panic-handler" -version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "backtrace", "lazy_static", @@ -10293,7 +10291,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "rustc-hash", "serde", @@ -10302,8 +10300,8 @@ dependencies = [ [[package]] name = "sp-runtime" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "either", "hash256-std-hasher", @@ -10325,8 +10323,8 @@ dependencies = [ [[package]] name = "sp-runtime-interface" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -10343,8 +10341,8 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "Inflector", "proc-macro-crate", @@ -10356,7 +10354,7 @@ dependencies = [ [[package]] name = "sp-sandbox" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "log", "parity-scale-codec", @@ -10370,7 +10368,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "scale-info", @@ -10384,7 +10382,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "scale-info", @@ -10394,8 +10392,8 @@ dependencies = [ [[package]] name = "sp-state-machine" -version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "hash-db", "log", @@ -10416,13 +10414,13 @@ dependencies = [ [[package]] name = "sp-std" -version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" [[package]] name = "sp-storage" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "impl-serde", "parity-scale-codec", @@ -10432,23 +10430,10 @@ dependencies = [ "sp-std", ] -[[package]] -name = "sp-tasks" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" -dependencies = [ - "log", - "sp-core", - "sp-externalities", - "sp-io", - "sp-runtime-interface", - "sp-std", -] - [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "futures-timer", @@ -10463,8 +10448,8 @@ dependencies = [ [[package]] name = "sp-tracing" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "sp-std", @@ -10476,7 +10461,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "sp-api", "sp-runtime", @@ -10485,7 +10470,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "log", @@ -10500,8 +10485,8 @@ dependencies = [ [[package]] name = "sp-trie" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "ahash", "hash-db", @@ -10524,7 +10509,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "impl-serde", "parity-scale-codec", @@ -10541,7 +10526,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -10551,8 +10536,8 @@ dependencies = [ [[package]] name = "sp-wasm-interface" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "impl-trait-for-tuples", "log", @@ -10565,7 +10550,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -10596,9 +10581,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.29.0" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0837b5d62f42082c9d56cd946495ae273a3c68083b637b9153341d5e465146d" +checksum = "37a9821878e1f13aba383aa40a86fb1b33c7265774ec91e32563cb1dd1577496" dependencies = [ "Inflector", "num-format", @@ -10780,7 +10765,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "platforms", ] @@ -10788,7 +10773,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -10809,7 +10794,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "futures-util", "hyper", @@ -10822,7 +10807,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "async-trait", "jsonrpsee", @@ -10835,7 +10820,7 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "jsonrpsee", "log", @@ -10856,7 +10841,7 @@ dependencies = [ [[package]] name = "substrate-test-client" version = "2.0.1" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "array-bytes", "async-trait", @@ -10882,7 +10867,7 @@ dependencies = [ [[package]] name = "substrate-test-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "futures", "substrate-test-utils-derive", @@ -10892,7 +10877,7 @@ dependencies = [ [[package]] name = "substrate-test-utils-derive" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -10903,7 +10888,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "ansi_term", "build-helper", @@ -11610,7 +11595,7 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a6da808575fe403ab2bd7f6cd009896cdc6fd71a" +source = "git+https://github.com/paritytech/substrate?branch=master#108d8eed88e71b5bb676a23fe983174fabf43c35" dependencies = [ "clap", "frame-try-runtime", From a5bff0c75e77effb5b7d3a1691de1b14bcdbd648 Mon Sep 17 00:00:00 2001 From: Andronik Date: Tue, 15 Nov 2022 16:26:03 +0100 Subject: [PATCH 19/34] remove the runtime check and test --- runtime/parachains/src/disputes.rs | 5 -- runtime/parachains/src/disputes/tests.rs | 64 ------------------------ 2 files changed, 69 deletions(-) diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index 9474d21d0d85..439d83d942dd 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -537,8 +537,6 @@ pub mod pallet { PotentialSpam, /// A dispute where there are only votes on one side. SingleSidedDispute, - /// No backing votes were provides along dispute statements. - MissingBackingVotes, } #[pallet::call] @@ -1232,9 +1230,6 @@ impl Pallet { Error::::SingleSidedDispute, ); - // Reject statements with no accompanying backing votes. - ensure!(!backers.is_empty(), Error::::MissingBackingVotes,); - >::insert(&set.session, &set.candidate_hash, backers.clone()); let DisputeStatementSet { ref session, ref candidate_hash, .. } = set; diff --git a/runtime/parachains/src/disputes/tests.rs b/runtime/parachains/src/disputes/tests.rs index 7c9aafe66137..3b1f212a38a1 100644 --- a/runtime/parachains/src/disputes/tests.rs +++ b/runtime/parachains/src/disputes/tests.rs @@ -737,70 +737,6 @@ fn test_provide_multi_dispute_is_providing() { }) } -#[test] -fn test_disputes_with_missing_backing_votes_are_rejected() { - new_test_ext(Default::default()).execute_with(|| { - let v0 = ::Pair::generate().0; - let v1 = ::Pair::generate().0; - - run_to_block(3, |b| { - // a new session at each block - if b == 1 { - Some(( - true, - b, - vec![(&0, v0.public()), (&1, v1.public())], - Some(vec![(&0, v0.public()), (&1, v1.public())]), - )) - } else { - Some((true, b, vec![(&1, v1.public())], Some(vec![(&1, v1.public())]))) - } - }); - - let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1)); - let session = 1; - - let stmts = vec![DisputeStatementSet { - candidate_hash: candidate_hash.clone(), - session, - statements: vec![ - ( - DisputeStatement::Valid(ValidDisputeStatementKind::Explicit), - ValidatorIndex(0), - v0.sign( - &ExplicitDisputeStatement { - valid: true, - candidate_hash: candidate_hash.clone(), - session, - } - .signing_payload(), - ), - ), - ( - DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), - ValidatorIndex(1), - v1.sign( - &ExplicitDisputeStatement { - valid: false, - candidate_hash: candidate_hash.clone(), - session, - } - .signing_payload(), - ), - ), - ], - }]; - - assert!(Pallet::::process_checked_multi_dispute_data( - stmts - .into_iter() - .map(CheckedDisputeStatementSet::unchecked_from_unchecked) - .collect() - ) - .is_err(),); - }) -} - #[test] fn test_freeze_on_note_included() { new_test_ext(Default::default()).execute_with(|| { From 8fba9e031217e918b3700ac3257fbf8bc3cbe9fd Mon Sep 17 00:00:00 2001 From: Andronik Date: Thu, 24 Nov 2022 15:37:20 +0000 Subject: [PATCH 20/34] append keys on past-session slashing --- runtime/parachains/src/disputes/slashing.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/runtime/parachains/src/disputes/slashing.rs b/runtime/parachains/src/disputes/slashing.rs index 462249a9d4e3..c983c95a7e6e 100644 --- a/runtime/parachains/src/disputes/slashing.rs +++ b/runtime/parachains/src/disputes/slashing.rs @@ -274,7 +274,15 @@ where .filter_map(|i| session_info.validators.get(i).cloned().map(|id| (i, id))) .collect(); let unapplied = PendingSlashes { keys, kind }; - >::insert(session_index, candidate_hash, unapplied); + + let append = |old: &mut Option| { + let old = old + .get_or_insert(PendingSlashes { keys: Default::default(), kind: unapplied.kind }); + debug_assert_eq!(old.kind, unapplied.kind); + + old.keys.extend(unapplied.keys) + }; + >::mutate(session_index, candidate_hash, append); } } From 77e777cc2358ec41ea65cd96ad5467642d374d2e Mon Sep 17 00:00:00 2001 From: Andronik Date: Fri, 25 Nov 2022 19:14:38 +0000 Subject: [PATCH 21/34] runtime/disputes: allow importing backing votes after explicit for --- runtime/parachains/src/disputes.rs | 123 +++++++++++++++++------ runtime/parachains/src/disputes/tests.rs | 49 +++++---- 2 files changed, 122 insertions(+), 50 deletions(-) diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index 439d83d942dd..44da600b3ded 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -33,7 +33,7 @@ use sp_runtime::{ traits::{AppVerify, One, Saturating, Zero}, DispatchError, RuntimeDebug, SaturatedConversion, }; -use sp_std::{cmp::Ordering, prelude::*}; +use sp_std::{cmp::Ordering, collections::btree_set::BTreeSet, prelude::*}; #[cfg(test)] #[allow(unused_imports)] @@ -472,7 +472,7 @@ pub mod pallet { SessionIndex, Blake2_128Concat, CandidateHash, - Vec, + BTreeSet, >; /// All included blocks on the chain, as well as the block number in this chain that @@ -600,6 +600,8 @@ enum SpamSlotChange { struct ImportSummary { /// The new state, with all votes imported. state: DisputeState, + /// List of validators who backed the candidate being disputed. + backers: BTreeSet, /// Changes to spam slots. Validator index paired with directional change. spam_slot_changes: Vec<(ValidatorIndex, SpamSlotChange)>, /// Validators to slash for being (wrongly) on the AGAINST side. @@ -620,6 +622,44 @@ enum VoteImportError { DuplicateStatement, } +#[derive(RuntimeDebug, Copy, Clone, PartialEq, Eq)] +enum VoteKind { + /// A backing vote that is counted as "for" vote in dispute resolution. + Backing, + /// Either an approval vote or and explicit dispute "for" vote. + ExplicitValid, + /// An explicit dispute "against" vote. + Invalid, +} + +impl<'a> From<&'a DisputeStatement> for VoteKind { + fn from(statement: &'a DisputeStatement) -> Self { + if statement.is_backing() { + Self::Backing + } else if statement.indicates_validity() { + Self::ExplicitValid + } else { + Self::Invalid + } + } +} + +impl VoteKind { + fn is_valid(&self) -> bool { + match self { + Self::Backing | Self::ExplicitValid => true, + Self::Invalid => false, + } + } + + fn is_backing(&self) -> bool { + match self { + Self::Backing => true, + Self::Invalid | Self::ExplicitValid => false, + } + } +} + impl From for Error { fn from(e: VoteImportError) -> Self { match e { @@ -634,8 +674,8 @@ impl From for Error { struct ImportUndo { /// The validator index to which to associate the statement import. validator_index: ValidatorIndex, - /// The direction of the vote, for block validity (`true`) or invalidity (`false`). - valid: bool, + /// The kind and direction of the vote. + kind: VoteKind, /// Has the validator participated before, i.e. in backing or /// with an opposing vote. new_participant: bool, @@ -643,25 +683,34 @@ struct ImportUndo { struct DisputeStateImporter { state: DisputeState, + backers: BTreeSet, now: BlockNumber, new_participants: bitvec::vec::BitVec, pre_flags: DisputeStateFlags, } impl DisputeStateImporter { - fn new(state: DisputeState, now: BlockNumber) -> Self { + fn new( + state: DisputeState, + backers: BTreeSet, + now: BlockNumber, + ) -> Self { let pre_flags = DisputeStateFlags::from_state(&state); let new_participants = bitvec::bitvec![u8, BitOrderLsb0; 0; state.validators_for.len()]; + // consistency checks + for i in backers.iter() { + debug_assert_eq!(state.validators_for.get(i.0 as usize).map(|b| *b), Some(true)); + } - DisputeStateImporter { state, now, new_participants, pre_flags } + DisputeStateImporter { state, backers, now, new_participants, pre_flags } } fn import( &mut self, validator: ValidatorIndex, - valid: bool, + kind: VoteKind, ) -> Result { - let (bits, other_bits) = if valid { + let (bits, other_bits) = if kind.is_valid() { (&mut self.state.validators_for, &mut self.state.validators_against) } else { (&mut self.state.validators_against, &mut self.state.validators_for) @@ -670,18 +719,27 @@ impl DisputeStateImporter { // out of bounds or already participated match bits.get(validator.0 as usize).map(|b| *b) { None => return Err(VoteImportError::ValidatorIndexOutOfBounds), - Some(true) => return Err(VoteImportError::DuplicateStatement), + Some(true) => { + // We allow backing statements to be imported after an + // explicit "for" vote, but not the other way around. + if !kind.is_backing() || self.backers.contains(&validator) { + return Err(VoteImportError::DuplicateStatement) + } + }, Some(false) => {}, } - // inefficient, and just for extra sanity. - if validator.0 as usize >= self.new_participants.len() { - return Err(VoteImportError::ValidatorIndexOutOfBounds) - } + // consistency check + debug_assert!((validator.0 as usize) < self.new_participants.len()); - let mut undo = ImportUndo { validator_index: validator, valid, new_participant: false }; + let mut undo = ImportUndo { validator_index: validator, kind, new_participant: false }; bits.set(validator.0 as usize, true); + if kind.is_backing() { + let is_new = self.backers.insert(validator); + // invariant check + debug_assert!(is_new); + } // New participants tracks those validators by index, which didn't appear on either // side of the dispute until now (so they make a first appearance). @@ -696,12 +754,16 @@ impl DisputeStateImporter { /// Revert a done transaction. fn undo(&mut self, undo: ImportUndo) { - if undo.valid { + if undo.kind.is_valid() { self.state.validators_for.set(undo.validator_index.0 as usize, false); } else { self.state.validators_against.set(undo.validator_index.0 as usize, false); } + if undo.kind.is_backing() { + self.backers.remove(&undo.validator_index); + } + if undo.new_participant { self.new_participants.set(undo.validator_index.0 as usize, false); } @@ -776,6 +838,7 @@ impl DisputeStateImporter { ImportSummary { state: self.state, + backers: self.backers, spam_slot_changes, slash_against, slash_for, @@ -998,9 +1061,12 @@ impl Pallet { } }; + let backers = + >::get(&set.session, &set.candidate_hash).unwrap_or_default(); + // Check and import all votes. let mut summary = { - let mut importer = DisputeStateImporter::new(dispute_state, now); + let mut importer = DisputeStateImporter::new(dispute_state, backers, now); for (i, (statement, validator_index, signature)) in set.statements.iter().enumerate() { // ensure the validator index is present in the session info // and the signature is valid @@ -1012,9 +1078,9 @@ impl Pallet { Some(v) => v, }; - let valid = statement.indicates_validity(); + let kind = VoteKind::from(statement); - let undo = match importer.import(*validator_index, valid) { + let undo = match importer.import(*validator_index, kind) { Ok(u) => u, Err(_) => { filter.remove_index(i); @@ -1200,24 +1266,16 @@ impl Pallet { } }; - let mut backers = + let backers = >::get(&set.session, &set.candidate_hash).unwrap_or_default(); // Import all votes. They were pre-checked. let summary = { - let mut importer = DisputeStateImporter::new(dispute_state, now); + let mut importer = DisputeStateImporter::new(dispute_state, backers, now); for (statement, validator_index, _signature) in &set.statements { - let valid = statement.indicates_validity(); - - importer.import(*validator_index, valid).map_err(Error::::from)?; + let kind = VoteKind::from(statement); - match statement { - DisputeStatement::Valid(ValidDisputeStatementKind::BackingSeconded(_)) | - DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(_)) => { - backers.push(validator_index.clone()); - }, - _ => {}, - } + importer.import(*validator_index, kind).map_err(Error::::from)?; } importer.finish() @@ -1230,7 +1288,10 @@ impl Pallet { Error::::SingleSidedDispute, ); - >::insert(&set.session, &set.candidate_hash, backers.clone()); + let backers = summary.backers; + if !backers.is_empty() { + >::insert(&set.session, &set.candidate_hash, backers.clone()); + } let DisputeStatementSet { ref session, ref candidate_hash, .. } = set; let session = *session; diff --git a/runtime/parachains/src/disputes/tests.rs b/runtime/parachains/src/disputes/tests.rs index 3b1f212a38a1..517b2e4b3987 100644 --- a/runtime/parachains/src/disputes/tests.rs +++ b/runtime/parachains/src/disputes/tests.rs @@ -31,6 +31,10 @@ use frame_support::{ use primitives::v2::BlockNumber; use sp_core::{crypto::CryptoType, Pair}; +const VOTE_FOR: VoteKind = VoteKind::ExplicitValid; +const VOTE_AGAINST: VoteKind = VoteKind::Invalid; +// const VOTE_BACKING: VoteKind = VoteKind::Backing; + /// Filtering updates the spam slots, as such update them. fn update_spam_slots(stmts: MultiDisputeStatementSet) -> CheckedMultiDisputeStatementSet { let config = >::config(); @@ -143,22 +147,26 @@ fn test_import_new_participant_spam_inc() { start: 0, concluded_at: None, }, + BTreeSet::new(), 0, ); assert_err!( - importer.import(ValidatorIndex(9), true), + importer.import(ValidatorIndex(9), VOTE_FOR), VoteImportError::ValidatorIndexOutOfBounds, ); - assert_err!(importer.import(ValidatorIndex(0), true), VoteImportError::DuplicateStatement); - assert_ok!(importer.import(ValidatorIndex(0), false)); + assert_err!(importer.import(ValidatorIndex(0), VOTE_FOR), VoteImportError::DuplicateStatement); + assert_ok!(importer.import(ValidatorIndex(0), VOTE_AGAINST)); - assert_ok!(importer.import(ValidatorIndex(2), true)); - assert_err!(importer.import(ValidatorIndex(2), true), VoteImportError::DuplicateStatement); + assert_ok!(importer.import(ValidatorIndex(2), VOTE_FOR)); + assert_err!(importer.import(ValidatorIndex(2), VOTE_FOR), VoteImportError::DuplicateStatement); - assert_ok!(importer.import(ValidatorIndex(2), false)); - assert_err!(importer.import(ValidatorIndex(2), false), VoteImportError::DuplicateStatement); + assert_ok!(importer.import(ValidatorIndex(2), VOTE_AGAINST)); + assert_err!( + importer.import(ValidatorIndex(2), VOTE_AGAINST), + VoteImportError::DuplicateStatement + ); let summary = importer.finish(); assert_eq!(summary.new_flags, DisputeStateFlags::default()); @@ -186,10 +194,11 @@ fn test_import_prev_participant_spam_dec_confirmed() { start: 0, concluded_at: None, }, + BTreeSet::new(), 0, ); - assert_ok!(importer.import(ValidatorIndex(2), true)); + assert_ok!(importer.import(ValidatorIndex(2), VOTE_FOR)); let summary = importer.finish(); assert_eq!( @@ -220,15 +229,16 @@ fn test_import_prev_participant_spam_dec_confirmed_slash_for() { start: 0, concluded_at: None, }, + BTreeSet::new(), 0, ); - assert_ok!(importer.import(ValidatorIndex(2), true)); - assert_ok!(importer.import(ValidatorIndex(2), false)); - assert_ok!(importer.import(ValidatorIndex(3), false)); - assert_ok!(importer.import(ValidatorIndex(4), false)); - assert_ok!(importer.import(ValidatorIndex(5), false)); - assert_ok!(importer.import(ValidatorIndex(6), false)); + assert_ok!(importer.import(ValidatorIndex(2), VOTE_FOR)); + assert_ok!(importer.import(ValidatorIndex(2), VOTE_AGAINST)); + assert_ok!(importer.import(ValidatorIndex(3), VOTE_AGAINST)); + assert_ok!(importer.import(ValidatorIndex(4), VOTE_AGAINST)); + assert_ok!(importer.import(ValidatorIndex(5), VOTE_AGAINST)); + assert_ok!(importer.import(ValidatorIndex(6), VOTE_AGAINST)); let summary = importer.finish(); assert_eq!( @@ -262,14 +272,15 @@ fn test_import_slash_against() { start: 0, concluded_at: None, }, + BTreeSet::new(), 0, ); - assert_ok!(importer.import(ValidatorIndex(3), true)); - assert_ok!(importer.import(ValidatorIndex(4), true)); - assert_ok!(importer.import(ValidatorIndex(5), false)); - assert_ok!(importer.import(ValidatorIndex(6), true)); - assert_ok!(importer.import(ValidatorIndex(7), true)); + assert_ok!(importer.import(ValidatorIndex(3), VOTE_FOR)); + assert_ok!(importer.import(ValidatorIndex(4), VOTE_FOR)); + assert_ok!(importer.import(ValidatorIndex(5), VOTE_AGAINST)); + assert_ok!(importer.import(ValidatorIndex(6), VOTE_FOR)); + assert_ok!(importer.import(ValidatorIndex(7), VOTE_FOR)); let summary = importer.finish(); assert_eq!( From cf90b174dbd8b92ee7c11b00393bd71724a3d897 Mon Sep 17 00:00:00 2001 From: Andronik Date: Sat, 26 Nov 2022 12:48:08 +0000 Subject: [PATCH 22/34] explicit MaliciousBacker error and a test --- runtime/parachains/src/disputes.rs | 14 ++++++-- runtime/parachains/src/disputes/tests.rs | 44 +++++++++++++++++++++++- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index 44da600b3ded..e70687a84cdd 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -537,6 +537,8 @@ pub mod pallet { PotentialSpam, /// A dispute where there are only votes on one side. SingleSidedDispute, + /// A dispute vote from a malicious backer. + MaliciousBacker, } #[pallet::call] @@ -620,6 +622,10 @@ enum VoteImportError { ValidatorIndexOutOfBounds, /// Found a duplicate statement in the dispute statement set. DuplicateStatement, + /// Found an explicit valid statement after backing statement. + /// Backers should not participate in explicit voting so this is + /// only possible on malicious backers. + MaliciousBacker, } #[derive(RuntimeDebug, Copy, Clone, PartialEq, Eq)] @@ -665,6 +671,7 @@ impl From for Error { match e { VoteImportError::ValidatorIndexOutOfBounds => Error::::ValidatorIndexOutOfBounds, VoteImportError::DuplicateStatement => Error::::DuplicateStatement, + VoteImportError::MaliciousBacker => Error::::MaliciousBacker, } } } @@ -722,8 +729,11 @@ impl DisputeStateImporter { Some(true) => { // We allow backing statements to be imported after an // explicit "for" vote, but not the other way around. - if !kind.is_backing() || self.backers.contains(&validator) { - return Err(VoteImportError::DuplicateStatement) + match (kind.is_backing(), self.backers.contains(&validator)) { + (true, true) | (false, false) => + return Err(VoteImportError::DuplicateStatement), + (false, true) => return Err(VoteImportError::MaliciousBacker), + (true, false) => {}, } }, Some(false) => {}, diff --git a/runtime/parachains/src/disputes/tests.rs b/runtime/parachains/src/disputes/tests.rs index 517b2e4b3987..ad62e59b9d3c 100644 --- a/runtime/parachains/src/disputes/tests.rs +++ b/runtime/parachains/src/disputes/tests.rs @@ -33,7 +33,7 @@ use sp_core::{crypto::CryptoType, Pair}; const VOTE_FOR: VoteKind = VoteKind::ExplicitValid; const VOTE_AGAINST: VoteKind = VoteKind::Invalid; -// const VOTE_BACKING: VoteKind = VoteKind::Backing; +const VOTE_BACKING: VoteKind = VoteKind::Backing; /// Filtering updates the spam slots, as such update them. fn update_spam_slots(stmts: MultiDisputeStatementSet) -> CheckedMultiDisputeStatementSet { @@ -299,6 +299,48 @@ fn test_import_slash_against() { assert_eq!(summary.new_flags, DisputeStateFlags::FOR_SUPERMAJORITY); } +#[test] +fn test_import_backing_votes() { + let mut importer = DisputeStateImporter::new( + DisputeState { + validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 1, 0, 0, 0, 0, 0], + validators_against: bitvec![u8, BitOrderLsb0; 0, 1, 0, 0, 0, 0, 0, 0], + start: 0, + concluded_at: None, + }, + BTreeSet::from_iter([ValidatorIndex(0)]), + 0, + ); + + assert_ok!(importer.import(ValidatorIndex(3), VOTE_FOR)); + assert_ok!(importer.import(ValidatorIndex(3), VOTE_BACKING)); + assert_ok!(importer.import(ValidatorIndex(3), VOTE_AGAINST)); + assert_ok!(importer.import(ValidatorIndex(6), VOTE_FOR)); + assert_ok!(importer.import(ValidatorIndex(7), VOTE_BACKING)); + // Don't import backing vote twice + assert_err!( + importer.import(ValidatorIndex(0), VOTE_BACKING), + VoteImportError::DuplicateStatement, + ); + // Don't import explicit votes after backing + assert_err!(importer.import(ValidatorIndex(7), VOTE_FOR), VoteImportError::MaliciousBacker,); + + let summary = importer.finish(); + assert_eq!( + summary.state, + DisputeState { + validators_for: bitvec![u8, BitOrderLsb0; 1, 0, 1, 1, 0, 0, 1, 1], + validators_against: bitvec![u8, BitOrderLsb0; 0, 1, 0, 1, 0, 0, 0, 0], + start: 0, + concluded_at: None, + }, + ); + assert_eq!( + summary.backers, + BTreeSet::from_iter([ValidatorIndex(0), ValidatorIndex(3), ValidatorIndex(7),]), + ); +} + fn generate_dispute_statement_set_entry( session: u32, candidate_hash: CandidateHash, From 7c4c3f5a848f16e2b61435e981d814f00333ed41 Mon Sep 17 00:00:00 2001 From: Andronik Date: Sat, 26 Nov 2022 13:10:26 +0000 Subject: [PATCH 23/34] update an outdated comment --- runtime/parachains/src/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/parachains/src/builder.rs b/runtime/parachains/src/builder.rs index d4cc217cbace..590eb31d043f 100644 --- a/runtime/parachains/src/builder.rs +++ b/runtime/parachains/src/builder.rs @@ -616,7 +616,7 @@ impl BenchBuilder { let dispute_statement = if validator_index % 4 == 0 { DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit) } else if validator_index < 3 { - // Set two votes as backing for the dispute set to be accepted + // Pass a couple of backing votes in this dispute set DisputeStatement::Valid( ValidDisputeStatementKind::BackingValid(relay_parent) ) From b47dc1558da49bbdac3fd894779be98888e656ec Mon Sep 17 00:00:00 2001 From: Andronik Date: Wed, 30 Nov 2022 18:29:15 +0000 Subject: [PATCH 24/34] Revert "update an outdated comment" This reverts commit 7c4c3f5a848f16e2b61435e981d814f00333ed41. --- runtime/parachains/src/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/parachains/src/builder.rs b/runtime/parachains/src/builder.rs index 590eb31d043f..d4cc217cbace 100644 --- a/runtime/parachains/src/builder.rs +++ b/runtime/parachains/src/builder.rs @@ -616,7 +616,7 @@ impl BenchBuilder { let dispute_statement = if validator_index % 4 == 0 { DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit) } else if validator_index < 3 { - // Pass a couple of backing votes in this dispute set + // Set two votes as backing for the dispute set to be accepted DisputeStatement::Valid( ValidDisputeStatementKind::BackingValid(relay_parent) ) From 4e36bed87f780f46006c737d121075258628adf5 Mon Sep 17 00:00:00 2001 From: Andronik Date: Wed, 30 Nov 2022 18:31:07 +0000 Subject: [PATCH 25/34] Revert "remove the runtime check and test" This reverts commit a5bff0c75e77effb5b7d3a1691de1b14bcdbd648. --- runtime/parachains/src/disputes.rs | 8 +-- runtime/parachains/src/disputes/tests.rs | 64 ++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index e70687a84cdd..438a71ca722a 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -539,6 +539,8 @@ pub mod pallet { SingleSidedDispute, /// A dispute vote from a malicious backer. MaliciousBacker, + /// No backing votes were provides along dispute statements. + MissingBackingVotes, } #[pallet::call] @@ -1299,9 +1301,9 @@ impl Pallet { ); let backers = summary.backers; - if !backers.is_empty() { - >::insert(&set.session, &set.candidate_hash, backers.clone()); - } + // Reject statements with no accompanying backing votes. + ensure!(!backers.is_empty(), Error::::MissingBackingVotes); + >::insert(&set.session, &set.candidate_hash, backers.clone()); let DisputeStatementSet { ref session, ref candidate_hash, .. } = set; let session = *session; diff --git a/runtime/parachains/src/disputes/tests.rs b/runtime/parachains/src/disputes/tests.rs index ad62e59b9d3c..5f0520d97091 100644 --- a/runtime/parachains/src/disputes/tests.rs +++ b/runtime/parachains/src/disputes/tests.rs @@ -790,6 +790,70 @@ fn test_provide_multi_dispute_is_providing() { }) } +#[test] +fn test_disputes_with_missing_backing_votes_are_rejected() { + new_test_ext(Default::default()).execute_with(|| { + let v0 = ::Pair::generate().0; + let v1 = ::Pair::generate().0; + + run_to_block(3, |b| { + // a new session at each block + if b == 1 { + Some(( + true, + b, + vec![(&0, v0.public()), (&1, v1.public())], + Some(vec![(&0, v0.public()), (&1, v1.public())]), + )) + } else { + Some((true, b, vec![(&1, v1.public())], Some(vec![(&1, v1.public())]))) + } + }); + + let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1)); + let session = 1; + + let stmts = vec![DisputeStatementSet { + candidate_hash: candidate_hash.clone(), + session, + statements: vec![ + ( + DisputeStatement::Valid(ValidDisputeStatementKind::Explicit), + ValidatorIndex(0), + v0.sign( + &ExplicitDisputeStatement { + valid: true, + candidate_hash: candidate_hash.clone(), + session, + } + .signing_payload(), + ), + ), + ( + DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), + ValidatorIndex(1), + v1.sign( + &ExplicitDisputeStatement { + valid: false, + candidate_hash: candidate_hash.clone(), + session, + } + .signing_payload(), + ), + ), + ], + }]; + + assert!(Pallet::::process_checked_multi_dispute_data( + stmts + .into_iter() + .map(CheckedDisputeStatementSet::unchecked_from_unchecked) + .collect() + ) + .is_err(),); + }) +} + #[test] fn test_freeze_on_note_included() { new_test_ext(Default::default()).execute_with(|| { From 01386f23733b4c4146dd66f71493a54c81a76e60 Mon Sep 17 00:00:00 2001 From: Andronik Date: Wed, 30 Nov 2022 19:59:49 +0000 Subject: [PATCH 26/34] incremental punishment post conclusion + test --- runtime/parachains/src/disputes.rs | 50 ++++-- runtime/parachains/src/disputes/tests.rs | 189 +++++++++++++++++++++++ 2 files changed, 228 insertions(+), 11 deletions(-) diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index 2e6c6db77b56..a6b41451cb5a 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -696,6 +696,7 @@ struct DisputeStateImporter { now: BlockNumber, new_participants: bitvec::vec::BitVec, pre_flags: DisputeStateFlags, + pre_state: DisputeState, } impl DisputeStateImporter { @@ -710,8 +711,9 @@ impl DisputeStateImporter { for i in backers.iter() { debug_assert_eq!(state.validators_for.get(i.0 as usize).map(|b| *b), Some(true)); } + let pre_state = state.clone(); - DisputeStateImporter { state, backers, now, new_participants, pre_flags } + DisputeStateImporter { state, backers, now, new_participants, pre_flags, pre_state } } fn import( @@ -818,9 +820,9 @@ impl DisputeStateImporter { }, }; - // 2. Check for fresh FOR supermajority. Only if not already concluded. - let slash_against = - if let (false, true) = pre_post_contains(DisputeStateFlags::FOR_SUPERMAJORITY) { + // 2. Check for FOR supermajority. + let slash_against = match pre_post_contains(DisputeStateFlags::FOR_SUPERMAJORITY) { + (false, true) => { if self.state.concluded_at.is_none() { self.state.concluded_at = Some(self.now.clone()); } @@ -831,22 +833,48 @@ impl DisputeStateImporter { .iter_ones() .map(|i| ValidatorIndex(i as _)) .collect() - } else { + }, + (true, true) => { + // provide new AGAINST voters to slash. + self.state + .validators_against + .iter_ones() + .filter(|i| self.pre_state.validators_against.get(*i).map_or(false, |b| !*b)) + .map(|i| ValidatorIndex(i as _)) + .collect() + }, + (true, false) => { + log::error!("Dispute statements are never removed. This is a bug"); Vec::new() - }; + }, + (false, false) => Vec::new(), + }; - // 3. Check for fresh AGAINST supermajority. - let slash_for = - if let (false, true) = pre_post_contains(DisputeStateFlags::AGAINST_SUPERMAJORITY) { + // 3. Check for AGAINST supermajority. + let slash_for = match pre_post_contains(DisputeStateFlags::AGAINST_SUPERMAJORITY) { + (false, true) => { if self.state.concluded_at.is_none() { self.state.concluded_at = Some(self.now.clone()); } // provide FOR voters to slash. self.state.validators_for.iter_ones().map(|i| ValidatorIndex(i as _)).collect() - } else { + }, + (true, true) => { + // provide new FOR voters to slash. + self.state + .validators_for + .iter_ones() + .filter(|i| self.pre_state.validators_for.get(*i).map_or(false, |b| !*b)) + .map(|i| ValidatorIndex(i as _)) + .collect() + }, + (true, false) => { + log::error!("Dispute statements are never removed. This is a bug"); Vec::new() - }; + }, + (false, false) => Vec::new(), + }; ImportSummary { state: self.state, diff --git a/runtime/parachains/src/disputes/tests.rs b/runtime/parachains/src/disputes/tests.rs index 5f0520d97091..cab6d692b9a2 100644 --- a/runtime/parachains/src/disputes/tests.rs +++ b/runtime/parachains/src/disputes/tests.rs @@ -1421,6 +1421,195 @@ fn test_provide_multi_dispute_success_and_other() { }) } +/// In this setup we have only one dispute concluding AGAINST. +/// There are some votes imported post dispute conclusion. +/// We make sure these votes are accounted for in punishment. +#[test] +fn test_punish_post_conclusion() { + new_test_ext(Default::default()).execute_with(|| { + // supermajority threshold is 5 + let v0 = ::Pair::generate().0; + let v1 = ::Pair::generate().0; + let v2 = ::Pair::generate().0; + let v3 = ::Pair::generate().0; + let v4 = ::Pair::generate().0; + let v5 = ::Pair::generate().0; + let v6 = ::Pair::generate().0; + + run_to_block(6, |b| { + // a new session at each block + Some(( + true, + b, + vec![ + (&0, v0.public()), + (&1, v1.public()), + (&2, v2.public()), + (&3, v3.public()), + (&4, v4.public()), + (&5, v5.public()), + (&6, v6.public()), + ], + Some(vec![ + (&0, v0.public()), + (&1, v1.public()), + (&2, v2.public()), + (&3, v3.public()), + (&4, v4.public()), + (&5, v5.public()), + (&6, v6.public()), + ]), + )) + }); + + let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1)); + let inclusion_parent = sp_core::H256::repeat_byte(0xff); + let session = 3; + + // v0 votes backing, v1 votes against + let stmts = vec![DisputeStatementSet { + candidate_hash: candidate_hash.clone(), + session, + statements: vec![ + ( + DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid( + inclusion_parent, + )), + ValidatorIndex(0), + v0.sign(&CompactStatement::Valid(candidate_hash).signing_payload( + &SigningContext { session_index: session, parent_hash: inclusion_parent }, + )), + ), + ( + DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), + ValidatorIndex(1), + v1.sign( + &ExplicitDisputeStatement { + valid: false, + candidate_hash: candidate_hash.clone(), + session, + } + .signing_payload(), + ), + ), + ], + }]; + + let stmts = update_spam_slots(stmts); + assert_ok!( + Pallet::::process_checked_multi_dispute_data(stmts), + vec![(3, candidate_hash)], + ); + + // v2, v3, v4, v5 vote against, dispute concludes AGAINST. + let stmts = vec![DisputeStatementSet { + candidate_hash: candidate_hash.clone(), + session, + statements: vec![ + ( + DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), + ValidatorIndex(2), + v2.sign( + &ExplicitDisputeStatement { + valid: false, + candidate_hash: candidate_hash.clone(), + session, + } + .signing_payload(), + ), + ), + ( + DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), + ValidatorIndex(3), + v3.sign( + &ExplicitDisputeStatement { + valid: false, + candidate_hash: candidate_hash.clone(), + session, + } + .signing_payload(), + ), + ), + ( + DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), + ValidatorIndex(4), + v4.sign( + &ExplicitDisputeStatement { + valid: false, + candidate_hash: candidate_hash.clone(), + session, + } + .signing_payload(), + ), + ), + ( + DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), + ValidatorIndex(5), + v5.sign( + &ExplicitDisputeStatement { + valid: false, + candidate_hash: candidate_hash.clone(), + session, + } + .signing_payload(), + ), + ), + ], + }]; + + let stmts = update_spam_slots(stmts); + assert_ok!(Pallet::::process_checked_multi_dispute_data(stmts), vec![],); + + assert_eq!( + PUNISH_VALIDATORS_FOR.with(|r| r.borrow().clone()), + vec![(3, vec![]), (3, vec![ValidatorIndex(0)]),], + ); + + // someone reveals v3 backing vote, v6 votes against + let stmts = vec![DisputeStatementSet { + candidate_hash: candidate_hash.clone(), + session, + statements: vec![ + ( + DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid( + inclusion_parent, + )), + ValidatorIndex(3), + v3.sign(&CompactStatement::Valid(candidate_hash).signing_payload( + &SigningContext { session_index: session, parent_hash: inclusion_parent }, + )), + ), + ( + DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), + ValidatorIndex(6), + v6.sign( + &ExplicitDisputeStatement { + valid: false, + candidate_hash: candidate_hash.clone(), + session, + } + .signing_payload(), + ), + ), + ], + }]; + + let stmts = update_spam_slots(stmts); + assert_ok!(Pallet::::process_checked_multi_dispute_data(stmts), vec![],); + + // Ensure punishment for is called + assert_eq!( + PUNISH_VALIDATORS_FOR.with(|r| r.borrow().clone()), + vec![(3, vec![]), (3, vec![ValidatorIndex(0)]), (3, vec![ValidatorIndex(3)]),], + ); + + assert_eq!( + PUNISH_VALIDATORS_AGAINST.with(|r| r.borrow().clone()), + vec![(3, vec![]), (3, vec![]), (3, vec![]),], + ); + }) +} + #[test] fn test_revert_and_freeze() { new_test_ext(Default::default()).execute_with(|| { From a08af12c2ce8b5e15f544d0f546347c9d3ce2dff Mon Sep 17 00:00:00 2001 From: Andronik Date: Thu, 1 Dec 2022 11:57:22 +0000 Subject: [PATCH 27/34] punish backers post FOR vote --- runtime/parachains/src/disputes.rs | 34 ++++++++++++++++------ runtime/parachains/src/disputes/tests.rs | 36 ++++++++++++++++++++---- runtime/parachains/src/mock.rs | 7 +++-- 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index a6b41451cb5a..25a811391146 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -684,7 +684,7 @@ struct ImportUndo { /// The validator index to which to associate the statement import. validator_index: ValidatorIndex, /// The kind and direction of the vote. - kind: VoteKind, + vote_kind: VoteKind, /// Has the validator participated before, i.e. in backing or /// with an opposing vote. new_participant: bool, @@ -697,6 +697,7 @@ struct DisputeStateImporter { new_participants: bitvec::vec::BitVec, pre_flags: DisputeStateFlags, pre_state: DisputeState, + pre_backers: BTreeSet, } impl DisputeStateImporter { @@ -712,8 +713,17 @@ impl DisputeStateImporter { debug_assert_eq!(state.validators_for.get(i.0 as usize).map(|b| *b), Some(true)); } let pre_state = state.clone(); - - DisputeStateImporter { state, backers, now, new_participants, pre_flags, pre_state } + let pre_backers = backers.clone(); + + DisputeStateImporter { + state, + backers, + now, + new_participants, + pre_flags, + pre_state, + pre_backers, + } } fn import( @@ -746,7 +756,8 @@ impl DisputeStateImporter { // consistency check debug_assert!((validator.0 as usize) < self.new_participants.len()); - let mut undo = ImportUndo { validator_index: validator, kind, new_participant: false }; + let mut undo = + ImportUndo { validator_index: validator, vote_kind: kind, new_participant: false }; bits.set(validator.0 as usize, true); if kind.is_backing() { @@ -768,13 +779,13 @@ impl DisputeStateImporter { /// Revert a done transaction. fn undo(&mut self, undo: ImportUndo) { - if undo.kind.is_valid() { + if undo.vote_kind.is_valid() { self.state.validators_for.set(undo.validator_index.0 as usize, false); } else { self.state.validators_against.set(undo.validator_index.0 as usize, false); } - if undo.kind.is_backing() { + if undo.vote_kind.is_backing() { self.backers.remove(&undo.validator_index); } @@ -861,11 +872,18 @@ impl DisputeStateImporter { self.state.validators_for.iter_ones().map(|i| ValidatorIndex(i as _)).collect() }, (true, true) => { - // provide new FOR voters to slash. + // provide new FOR voters to slash including new backers + // who might have voted FOR before + let new_backing_vote = |i: &ValidatorIndex| -> bool { + !self.pre_backers.contains(i) && self.backers.contains(i) + }; self.state .validators_for .iter_ones() - .filter(|i| self.pre_state.validators_for.get(*i).map_or(false, |b| !*b)) + .filter(|i| { + self.pre_state.validators_for.get(*i).map_or(false, |b| !*b) || + new_backing_vote(&ValidatorIndex(*i as _)) + }) .map(|i| ValidatorIndex(i as _)) .collect() }, diff --git a/runtime/parachains/src/disputes/tests.rs b/runtime/parachains/src/disputes/tests.rs index cab6d692b9a2..d177bb8cc991 100644 --- a/runtime/parachains/src/disputes/tests.rs +++ b/runtime/parachains/src/disputes/tests.rs @@ -20,7 +20,8 @@ use crate::{ disputes::DisputesHandler, mock::{ new_test_ext, AccountId, AllPalletsWithSystem, Initializer, MockGenesisConfig, System, - Test, PUNISH_VALIDATORS_AGAINST, PUNISH_VALIDATORS_FOR, REWARD_VALIDATORS, + Test, PUNISH_BACKERS_FOR, PUNISH_VALIDATORS_AGAINST, PUNISH_VALIDATORS_FOR, + REWARD_VALIDATORS, }, }; use assert_matches::assert_matches; @@ -1501,7 +1502,8 @@ fn test_punish_post_conclusion() { vec![(3, candidate_hash)], ); - // v2, v3, v4, v5 vote against, dispute concludes AGAINST. + // v2, v6, v4, v5 vote against, dispute concludes AGAINST. + // v3 votes FOR let stmts = vec![DisputeStatementSet { candidate_hash: candidate_hash.clone(), session, @@ -1520,8 +1522,8 @@ fn test_punish_post_conclusion() { ), ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), - ValidatorIndex(3), - v3.sign( + ValidatorIndex(6), + v6.sign( &ExplicitDisputeStatement { valid: false, candidate_hash: candidate_hash.clone(), @@ -1554,6 +1556,11 @@ fn test_punish_post_conclusion() { .signing_payload(), ), ), + ( + DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking), + ValidatorIndex(3), + v3.sign(&ApprovalVote(candidate_hash).signing_payload(session)), + ), ], }]; @@ -1562,7 +1569,11 @@ fn test_punish_post_conclusion() { assert_eq!( PUNISH_VALIDATORS_FOR.with(|r| r.borrow().clone()), - vec![(3, vec![]), (3, vec![ValidatorIndex(0)]),], + vec![(3, vec![]), (3, vec![ValidatorIndex(0), ValidatorIndex(3)]),], + ); + assert_eq!( + PUNISH_BACKERS_FOR.with(|r| r.borrow().clone()), + vec![(3, vec![ValidatorIndex(0)]), (3, vec![ValidatorIndex(0)]),], ); // someone reveals v3 backing vote, v6 votes against @@ -1600,7 +1611,20 @@ fn test_punish_post_conclusion() { // Ensure punishment for is called assert_eq!( PUNISH_VALIDATORS_FOR.with(|r| r.borrow().clone()), - vec![(3, vec![]), (3, vec![ValidatorIndex(0)]), (3, vec![ValidatorIndex(3)]),], + vec![ + (3, vec![]), + (3, vec![ValidatorIndex(0), ValidatorIndex(3)]), + (3, vec![ValidatorIndex(3)]), + ], + ); + + assert_eq!( + PUNISH_BACKERS_FOR.with(|r| r.borrow().clone()), + vec![ + (3, vec![ValidatorIndex(0)]), + (3, vec![ValidatorIndex(0)]), + (3, vec![ValidatorIndex(0), ValidatorIndex(3)]) + ], ); assert_eq!( diff --git a/runtime/parachains/src/mock.rs b/runtime/parachains/src/mock.rs index 5212521e069e..bf7901c2d9a3 100644 --- a/runtime/parachains/src/mock.rs +++ b/runtime/parachains/src/mock.rs @@ -253,6 +253,7 @@ thread_local! { pub static REWARD_VALIDATORS: RefCell)>> = RefCell::new(Vec::new()); pub static PUNISH_VALIDATORS_FOR: RefCell)>> = RefCell::new(Vec::new()); pub static PUNISH_VALIDATORS_AGAINST: RefCell)>> = RefCell::new(Vec::new()); + pub static PUNISH_BACKERS_FOR: RefCell)>> = RefCell::new(Vec::new()); } impl crate::disputes::RewardValidators for Test { @@ -269,9 +270,11 @@ impl crate::disputes::SlashingHandler for Test { session: SessionIndex, _: CandidateHash, losers: impl IntoIterator, - _backers: impl IntoIterator, + backers: impl IntoIterator, ) { - PUNISH_VALIDATORS_FOR.with(|r| r.borrow_mut().push((session, losers.into_iter().collect()))) + PUNISH_VALIDATORS_FOR + .with(|r| r.borrow_mut().push((session, losers.into_iter().collect()))); + PUNISH_BACKERS_FOR.with(|r| r.borrow_mut().push((session, backers.into_iter().collect()))); } fn punish_against_valid( From b83279bc76f4372414b13b719854ac9f3efecfee Mon Sep 17 00:00:00 2001 From: Andronik Date: Tue, 6 Dec 2022 17:02:27 +0100 Subject: [PATCH 28/34] remove unnecessary lifetime annotation --- runtime/parachains/src/disputes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index 25a811391146..ef4024ecce14 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -640,8 +640,8 @@ enum VoteKind { Invalid, } -impl<'a> From<&'a DisputeStatement> for VoteKind { - fn from(statement: &'a DisputeStatement) -> Self { +impl From<&DisputeStatement> for VoteKind { + fn from(statement: &DisputeStatement) -> Self { if statement.is_backing() { Self::Backing } else if statement.indicates_validity() { From 37b1d59e0b9cca760edb62d18f1472eae01c9653 Mon Sep 17 00:00:00 2001 From: Andronik Date: Tue, 6 Dec 2022 17:08:49 +0100 Subject: [PATCH 29/34] add a comment to zombinet test --- zombienet_tests/functional/0002-parachains-disputes.zndsl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zombienet_tests/functional/0002-parachains-disputes.zndsl b/zombienet_tests/functional/0002-parachains-disputes.zndsl index b56cd9b06c89..357a380e1949 100644 --- a/zombienet_tests/functional/0002-parachains-disputes.zndsl +++ b/zombienet_tests/functional/0002-parachains-disputes.zndsl @@ -48,6 +48,10 @@ eve: reports parachain_candidate_disputes_total is at least 10 within 15 seconds eve: reports parachain_candidate_dispute_concluded{validity="valid"} is at least 10 within 15 seconds eve: reports parachain_candidate_dispute_concluded{validity="invalid"} is 0 within 15 seconds +# As of , we don't slash on disputes +# with `valid` outcome, so there is no offence reported. +# alice: system event contains "There is an offence reported" within 60 seconds + # Check lag - approval alice: reports polkadot_parachain_approval_checking_finality_lag is 0 bob: reports polkadot_parachain_approval_checking_finality_lag is 0 From a489310c7e8b42c9568d2c7f1e1fc70a8cfe82c3 Mon Sep 17 00:00:00 2001 From: ordian Date: Mon, 12 Dec 2022 23:24:01 +0100 Subject: [PATCH 30/34] typo --- runtime/parachains/src/disputes/slashing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/parachains/src/disputes/slashing.rs b/runtime/parachains/src/disputes/slashing.rs index c983c95a7e6e..6c85730ea5f4 100644 --- a/runtime/parachains/src/disputes/slashing.rs +++ b/runtime/parachains/src/disputes/slashing.rs @@ -30,7 +30,7 @@ //! the dispute is concluded. This is also what `im-online` pallet does. //! However, since a dispute can conclude several sessions after the candidate //! was backed (see `dispute_period` in `HostConfiguration`), we can't rely on -//! this information be available in the context of the current block. The +//! this information being available in the context of the current block. The //! `babe` and `grandpa` equivocation handlers also have to deal with this //! problem. //! From 142d852d73c93a1f28441b009f69a6e637dabd63 Mon Sep 17 00:00:00 2001 From: Andronik Date: Sat, 7 Jan 2023 18:08:20 +0100 Subject: [PATCH 31/34] fmt --- runtime/parachains/src/disputes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index 3559fd3f9e0f..f802c0dfd217 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -1191,7 +1191,7 @@ impl Pallet { // Reject statements with no accompanying backing votes. ensure!(!backers.is_empty(), Error::::MissingBackingVotes); >::insert(&set.session, &set.candidate_hash, backers.clone()); - // AUDIT: from now on, no error should be returned. + // AUDIT: from now on, no error should be returned. let DisputeStatementSet { ref session, ref candidate_hash, .. } = set; let session = *session; From 8632334af60f143f9ff73d47a8971dc0017b94c1 Mon Sep 17 00:00:00 2001 From: Andronik Date: Sat, 7 Jan 2023 20:37:23 +0100 Subject: [PATCH 32/34] post merge test fixes --- runtime/parachains/src/disputes/tests.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/parachains/src/disputes/tests.rs b/runtime/parachains/src/disputes/tests.rs index ce749971e4ef..a7af5dc2034b 100644 --- a/runtime/parachains/src/disputes/tests.rs +++ b/runtime/parachains/src/disputes/tests.rs @@ -851,8 +851,6 @@ mod unconfirmed_disputes { }); let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1)); - let inclusion_parent = sp_core::H256::repeat_byte(0xff); - let session = 3; // v0 votes for 4, v1 votes against 4. DisputeStatementSet { @@ -966,6 +964,8 @@ fn test_provide_multi_dispute_success_and_other() { }); let candidate_hash = CandidateHash(sp_core::H256::repeat_byte(1)); + let inclusion_parent = sp_core::H256::repeat_byte(0xff); + let session = 3; // v0 and v1 vote for 3, v6 votes against let stmts = vec![DisputeStatementSet { @@ -1324,7 +1324,7 @@ fn test_punish_post_conclusion() { ], }]; - let stmts = update_spam_slots(stmts); + let stmts = filter_dispute_set(stmts); assert_ok!( Pallet::::process_checked_multi_dispute_data(stmts), vec![(3, candidate_hash)], @@ -1392,7 +1392,7 @@ fn test_punish_post_conclusion() { ], }]; - let stmts = update_spam_slots(stmts); + let stmts = filter_dispute_set(stmts); assert_ok!(Pallet::::process_checked_multi_dispute_data(stmts), vec![],); assert_eq!( @@ -1433,7 +1433,7 @@ fn test_punish_post_conclusion() { ], }]; - let stmts = update_spam_slots(stmts); + let stmts = filter_dispute_set(stmts); assert_ok!(Pallet::::process_checked_multi_dispute_data(stmts), vec![],); // Ensure punishment for is called From 9d81c26c2f80bfdf3e1c26cb678781d789216a34 Mon Sep 17 00:00:00 2001 From: Andronik Date: Tue, 10 Jan 2023 13:13:58 +0100 Subject: [PATCH 33/34] fix test after changes in master --- runtime/parachains/src/disputes/tests.rs | 63 +++++++++++------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/runtime/parachains/src/disputes/tests.rs b/runtime/parachains/src/disputes/tests.rs index a7af5dc2034b..7a3396f51b25 100644 --- a/runtime/parachains/src/disputes/tests.rs +++ b/runtime/parachains/src/disputes/tests.rs @@ -1264,6 +1264,14 @@ fn test_punish_post_conclusion() { let v4 = ::Pair::generate().0; let v5 = ::Pair::generate().0; let v6 = ::Pair::generate().0; + // Mapping between key pair and `ValidatorIndex` + // v0 -> 0 + // v1 -> 3 + // v2 -> 6 + // v3 -> 5 + // v4 -> 1 + // v5 -> 4 + // v6 -> 2 run_to_block(6, |b| { // a new session at each block @@ -1295,7 +1303,6 @@ fn test_punish_post_conclusion() { let inclusion_parent = sp_core::H256::repeat_byte(0xff); let session = 3; - // v0 votes backing, v1 votes against let stmts = vec![DisputeStatementSet { candidate_hash: candidate_hash.clone(), session, @@ -1312,7 +1319,7 @@ fn test_punish_post_conclusion() { ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), ValidatorIndex(1), - v1.sign( + v4.sign( &ExplicitDisputeStatement { valid: false, candidate_hash: candidate_hash.clone(), @@ -1321,25 +1328,10 @@ fn test_punish_post_conclusion() { .signing_payload(), ), ), - ], - }]; - - let stmts = filter_dispute_set(stmts); - assert_ok!( - Pallet::::process_checked_multi_dispute_data(stmts), - vec![(3, candidate_hash)], - ); - - // v2, v6, v4, v5 vote against, dispute concludes AGAINST. - // v3 votes FOR - let stmts = vec![DisputeStatementSet { - candidate_hash: candidate_hash.clone(), - session, - statements: vec![ ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), ValidatorIndex(2), - v2.sign( + v6.sign( &ExplicitDisputeStatement { valid: false, candidate_hash: candidate_hash.clone(), @@ -1351,7 +1343,7 @@ fn test_punish_post_conclusion() { ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), ValidatorIndex(6), - v6.sign( + v2.sign( &ExplicitDisputeStatement { valid: false, candidate_hash: candidate_hash.clone(), @@ -1363,7 +1355,7 @@ fn test_punish_post_conclusion() { ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), ValidatorIndex(4), - v4.sign( + v5.sign( &ExplicitDisputeStatement { valid: false, candidate_hash: candidate_hash.clone(), @@ -1375,7 +1367,7 @@ fn test_punish_post_conclusion() { ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), ValidatorIndex(5), - v5.sign( + v3.sign( &ExplicitDisputeStatement { valid: false, candidate_hash: candidate_hash.clone(), @@ -1387,24 +1379,27 @@ fn test_punish_post_conclusion() { ( DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking), ValidatorIndex(3), - v3.sign(&ApprovalVote(candidate_hash).signing_payload(session)), + v1.sign(&ApprovalVote(candidate_hash).signing_payload(session)), ), ], }]; let stmts = filter_dispute_set(stmts); - assert_ok!(Pallet::::process_checked_multi_dispute_data(stmts), vec![],); + assert_ok!( + Pallet::::process_checked_multi_dispute_data(stmts), + vec![(session, candidate_hash)], + ); assert_eq!( PUNISH_VALIDATORS_FOR.with(|r| r.borrow().clone()), - vec![(3, vec![]), (3, vec![ValidatorIndex(0), ValidatorIndex(3)]),], + vec![(session, vec![ValidatorIndex(0), ValidatorIndex(3)]),], ); assert_eq!( PUNISH_BACKERS_FOR.with(|r| r.borrow().clone()), - vec![(3, vec![ValidatorIndex(0)]), (3, vec![ValidatorIndex(0)]),], + vec![(session, vec![ValidatorIndex(0)]),], ); - // someone reveals v3 backing vote, v6 votes against + // someone reveals 3 backing vote, 6 votes against let stmts = vec![DisputeStatementSet { candidate_hash: candidate_hash.clone(), session, @@ -1414,14 +1409,14 @@ fn test_punish_post_conclusion() { inclusion_parent, )), ValidatorIndex(3), - v3.sign(&CompactStatement::Valid(candidate_hash).signing_payload( + v1.sign(&CompactStatement::Valid(candidate_hash).signing_payload( &SigningContext { session_index: session, parent_hash: inclusion_parent }, )), ), ( DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit), ValidatorIndex(6), - v6.sign( + v2.sign( &ExplicitDisputeStatement { valid: false, candidate_hash: candidate_hash.clone(), @@ -1440,24 +1435,22 @@ fn test_punish_post_conclusion() { assert_eq!( PUNISH_VALIDATORS_FOR.with(|r| r.borrow().clone()), vec![ - (3, vec![]), - (3, vec![ValidatorIndex(0), ValidatorIndex(3)]), - (3, vec![ValidatorIndex(3)]), + (session, vec![ValidatorIndex(0), ValidatorIndex(3)]), + (session, vec![ValidatorIndex(3)]), ], ); assert_eq!( PUNISH_BACKERS_FOR.with(|r| r.borrow().clone()), vec![ - (3, vec![ValidatorIndex(0)]), - (3, vec![ValidatorIndex(0)]), - (3, vec![ValidatorIndex(0), ValidatorIndex(3)]) + (session, vec![ValidatorIndex(0)]), + (session, vec![ValidatorIndex(0), ValidatorIndex(3)]) ], ); assert_eq!( PUNISH_VALIDATORS_AGAINST.with(|r| r.borrow().clone()), - vec![(3, vec![]), (3, vec![]), (3, vec![]),], + vec![(session, vec![]), (session, vec![]),], ); }) } From b8de8d6691d4e3437321b0c790ba619587762ecb Mon Sep 17 00:00:00 2001 From: Andronik Date: Mon, 16 Jan 2023 15:41:35 +0100 Subject: [PATCH 34/34] address review nits --- runtime/parachains/src/disputes.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index 8d4094f0a109..95117633aae6 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -680,6 +680,13 @@ struct DisputeStateImporter { new_participants: bitvec::vec::BitVec, pre_flags: DisputeStateFlags, pre_state: DisputeState, + // The list of backing votes before importing the batch of votes. This field should be + // initialized as empty on the first import of the dispute votes and should remain non-empty + // afterwards. + // + // If a dispute has concluded and the candidate was found invalid, we may want to slash as many + // backers as possible. This list allows us to slash these backers once their votes have been + // imported post dispute conclusion. pre_backers: BTreeSet, } @@ -784,7 +791,7 @@ impl DisputeStateImporter { let pre_post_contains = |flags| (pre_flags.contains(flags), post_flags.contains(flags)); - // 2. Check for FOR supermajority. + // 1. Check for FOR supermajority. let slash_against = match pre_post_contains(DisputeStateFlags::FOR_SUPERMAJORITY) { (false, true) => { if self.state.concluded_at.is_none() { @@ -814,7 +821,7 @@ impl DisputeStateImporter { (false, false) => Vec::new(), }; - // 3. Check for AGAINST supermajority. + // 2. Check for AGAINST supermajority. let slash_for = match pre_post_contains(DisputeStateFlags::AGAINST_SUPERMAJORITY) { (false, true) => { if self.state.concluded_at.is_none() {