diff --git a/polkadot/node/core/dispute-coordinator/src/real/initialized.rs b/polkadot/node/core/dispute-coordinator/src/real/initialized.rs
index 9f9f1305a9a82d0c439dfd9c4d9e2bb8add1ce41..d9d71c67e9bb9356d490ef3e1308f4dcace7cdf0 100644
--- a/polkadot/node/core/dispute-coordinator/src/real/initialized.rs
+++ b/polkadot/node/core/dispute-coordinator/src/real/initialized.rs
@@ -244,8 +244,10 @@ impl Initialized {
 			if !overlay_db.is_empty() {
 				let ops = overlay_db.into_write_ops();
 				backend.write(ops)?;
-				confirm_write()?;
 			}
+			// even if the changeset was empty,
+			// otherwise the caller will error.
+			confirm_write()?;
 		}
 	}
 
@@ -882,7 +884,8 @@ impl Initialized {
 
 		// Whether or not we know already that this is a good dispute:
 		//
-		// Note we can only know for sure whether we reached the `byzantine_threshold`  after updating candidate votes above, therefore the spam checking is afterwards:
+		// Note we can only know for sure whether we reached the `byzantine_threshold`  after
+		// updating candidate votes above, therefore the spam checking is afterwards:
 		let is_confirmed = is_included ||
 			was_confirmed ||
 			is_local || votes.voted_indices().len() >
@@ -890,13 +893,19 @@ impl Initialized {
 
 		// Potential spam:
 		if !is_confirmed {
-			let mut free_spam_slots = statements.is_empty();
+			let mut free_spam_slots_available = true;
+			// Only allow import if all validators voting invalid, have not exceeded
+			// their spam slots:
 			for (statement, index) in statements.iter() {
-				free_spam_slots |= statement.statement().is_backing() ||
+				// Disputes can only be triggered via an invalidity stating vote, thus we only
+				// need to increase spam slots on invalid votes. (If we did not, we would also
+				// increase spam slots for backing validators for example - as validators have to
+				// provide some opposing vote for dispute-distribution).
+				free_spam_slots_available &= statement.statement().indicates_validity() ||
 					self.spam_slots.add_unconfirmed(session, candidate_hash, *index);
 			}
-			// No reporting validator had a free spam slot:
-			if !free_spam_slots {
+			// Only validity stating votes or validator had free spam slot?
+			if !free_spam_slots_available {
 				tracing::debug!(
 					target: LOG_TARGET,
 					?candidate_hash,
diff --git a/polkadot/node/core/dispute-coordinator/src/real/spam_slots.rs b/polkadot/node/core/dispute-coordinator/src/real/spam_slots.rs
index 6c8707152a685acb3cad65054090a153a67155dc..b58b812b042a436290782034e168094fac004846 100644
--- a/polkadot/node/core/dispute-coordinator/src/real/spam_slots.rs
+++ b/polkadot/node/core/dispute-coordinator/src/real/spam_slots.rs
@@ -23,19 +23,24 @@ use crate::real::LOG_TARGET;
 /// Type used for counting potential spam votes.
 type SpamCount = u32;
 
-/// How many unconfirmed disputes a validator is allowed to be a participant in (per session).
+/// How many unconfirmed disputes a validator is allowed to import (per session).
 ///
 /// Unconfirmed means: Node has not seen the candidate be included on any chain, it has not cast a
 /// vote itself on that dispute, the dispute has not yet reached more than a third of
 /// validator's votes and the including relay chain block has not yet been finalized.
 ///
 /// Exact number of `MAX_SPAM_VOTES` is not that important here. It is important that the number is
-/// low enough to not cause resource exhaustion, if multiple validators spend their limits. Also
-/// if things are working properly, this number cannot really be too low either, as all relevant
-/// disputes _should_ have been seen as included my enough validators. (Otherwise the candidate
-/// would not have been available in the first place and could not have been included.) So this is
-/// really just a fallback mechanism if things go terribly wrong.
+/// low enough to not cause resource exhaustion (disk & memory) on the importing validator, even if
+/// multiple validators fully make use of their assigned spam slots.
+///
+/// Also if things are working properly, this number cannot really be too low either, as all
+/// relevant disputes _should_ have been seen as included by enough validators. (Otherwise the
+/// candidate would not have been available in the first place and could not have been included.)
+/// So this is really just a fallback mechanism if things go terribly wrong.
+#[cfg(not(test))]
 const MAX_SPAM_VOTES: SpamCount = 50;
+#[cfg(test)]
+const MAX_SPAM_VOTES: SpamCount = 1;
 
 /// Spam slots for raised disputes concerning unknown candidates.
 pub struct SpamSlots {
@@ -76,7 +81,12 @@ impl SpamSlots {
 		Self { slots, unconfirmed: unconfirmed_disputes }
 	}
 
-	/// Add an unconfirmed dispute if free slots are available.
+	/// Increase a "voting invalid" validator's spam slot.
+	///
+	/// This function should get called for any validator's invalidity vote for any not yet
+	/// confirmed dispute.
+	///
+	/// Returns: `true` if validator still had vacant spam slots, `false` otherwise.
 	pub fn add_unconfirmed(
 		&mut self,
 		session: SessionIndex,
@@ -90,14 +100,16 @@ impl SpamSlots {
 		let validators = self.unconfirmed.entry((session, candidate)).or_default();
 
 		if validators.insert(validator) {
+			// We only increment spam slots once per candidate, as each validator has to provide an
+			// opposing vote for sending out its own vote. Therefore, receiving multiple votes for
+			// a single candidate is expected and should not get punished here.
 			*c += 1;
-			true
-		} else {
-			false
 		}
+
+		true
 	}
 
-	/// Clear out spam slots for a given candiate in a session.
+	/// Clear out spam slots for a given candidate in a session.
 	///
 	/// This effectively reduces the spam slot count for all validators participating in a dispute
 	/// for that candidate. You should call this function once a dispute became obsolete or got
diff --git a/polkadot/node/core/dispute-coordinator/src/real/tests.rs b/polkadot/node/core/dispute-coordinator/src/real/tests.rs
index aed361f5886faf8f13545c8c3dc8091f94adde7e..9d031a70b22f473f655640104ee3d13e9c5c9e98 100644
--- a/polkadot/node/core/dispute-coordinator/src/real/tests.rs
+++ b/polkadot/node/core/dispute-coordinator/src/real/tests.rs
@@ -32,7 +32,7 @@ use futures::{
 use kvdb::KeyValueDB;
 use parity_scale_codec::Encode;
 
-use polkadot_node_primitives::SignedDisputeStatement;
+use polkadot_node_primitives::{SignedDisputeStatement, SignedFullStatement, Statement};
 use polkadot_node_subsystem::{
 	messages::{
 		ChainApiMessage, DisputeCoordinatorMessage, DisputeDistributionMessage,
@@ -43,7 +43,7 @@ use polkadot_node_subsystem::{
 };
 use polkadot_node_subsystem_util::TimeoutExt;
 use sc_keystore::LocalKeystore;
-use sp_core::testing::TaskExecutor;
+use sp_core::{sr25519::Pair, testing::TaskExecutor, Pair as PairT};
 use sp_keyring::Sr25519Keyring;
 use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
 
@@ -57,8 +57,8 @@ use polkadot_node_subsystem_test_helpers::{make_subsystem_context, TestSubsystem
 use polkadot_primitives::{
 	v1::{
 		BlakeTwo256, BlockNumber, CandidateCommitments, CandidateHash, CandidateReceipt, Hash,
-		HashT, Header, MultiDisputeStatementSet, ScrapedOnChainVotes, SessionIndex, ValidatorId,
-		ValidatorIndex,
+		HashT, Header, MultiDisputeStatementSet, ScrapedOnChainVotes, SessionIndex, SigningContext,
+		ValidatorId, ValidatorIndex,
 	},
 	v2::SessionInfo,
 };
@@ -78,12 +78,12 @@ use super::db::v1::DbBackend;
 const TEST_TIMEOUT: Duration = Duration::from_secs(2);
 
 // sets up a keystore with the given keyring accounts.
-fn make_keystore(accounts: &[Sr25519Keyring]) -> LocalKeystore {
+fn make_keystore(seeds: impl Iterator<Item = String>) -> LocalKeystore {
 	let store = LocalKeystore::in_memory();
 
-	for s in accounts.iter().copied().map(|k| k.to_seed()) {
+	for s in seeds {
 		store
-			.sr25519_generate_new(polkadot_primitives::v1::PARACHAIN_KEY_TYPE_ID, Some(s.as_str()))
+			.sr25519_generate_new(polkadot_primitives::v1::PARACHAIN_KEY_TYPE_ID, Some(&s))
 			.unwrap();
 	}
 
@@ -120,7 +120,7 @@ impl MockClock {
 }
 
 struct TestState {
-	validators: Vec<Sr25519Keyring>,
+	validators: Vec<Pair>,
 	validator_public: Vec<ValidatorId>,
 	validator_groups: Vec<Vec<ValidatorIndex>>,
 	master_keystore: Arc<sc_keystore::LocalKeystore>,
@@ -133,17 +133,28 @@ struct TestState {
 
 impl Default for TestState {
 	fn default() -> TestState {
+		let p1 = Pair::from_string("//Polka", None).unwrap();
+		let p2 = Pair::from_string("//Dot", None).unwrap();
+		let p3 = Pair::from_string("//Kusama", None).unwrap();
 		let validators = vec![
-			Sr25519Keyring::Alice,
-			Sr25519Keyring::Bob,
-			Sr25519Keyring::Charlie,
-			Sr25519Keyring::Dave,
-			Sr25519Keyring::Eve,
-			Sr25519Keyring::One,
-			Sr25519Keyring::Ferdie,
+			(Sr25519Keyring::Alice.pair(), Sr25519Keyring::Alice.to_seed()),
+			(Sr25519Keyring::Bob.pair(), Sr25519Keyring::Bob.to_seed()),
+			(Sr25519Keyring::Charlie.pair(), Sr25519Keyring::Charlie.to_seed()),
+			(Sr25519Keyring::Dave.pair(), Sr25519Keyring::Dave.to_seed()),
+			(Sr25519Keyring::Eve.pair(), Sr25519Keyring::Eve.to_seed()),
+			(Sr25519Keyring::One.pair(), Sr25519Keyring::One.to_seed()),
+			(Sr25519Keyring::Ferdie.pair(), Sr25519Keyring::Ferdie.to_seed()),
+			// Two more keys needed so disputes are not confirmed already with only 3 statements.
+			(p1, "//Polka".into()),
+			(p2, "//Dot".into()),
+			(p3, "//Kusama".into()),
 		];
 
-		let validator_public = validators.iter().map(|k| ValidatorId::from(k.public())).collect();
+		let validator_public = validators
+			.clone()
+			.into_iter()
+			.map(|k| ValidatorId::from(k.0.public()))
+			.collect();
 
 		let validator_groups = vec![
 			vec![ValidatorIndex(0), ValidatorIndex(1)],
@@ -151,14 +162,15 @@ impl Default for TestState {
 			vec![ValidatorIndex(4), ValidatorIndex(5), ValidatorIndex(6)],
 		];
 
-		let master_keystore = make_keystore(&validators).into();
-		let subsystem_keystore = make_keystore(&[Sr25519Keyring::Alice]).into();
+		let master_keystore = make_keystore(validators.iter().map(|v| v.1.clone())).into();
+		let subsystem_keystore =
+			make_keystore(vec![Sr25519Keyring::Alice.to_seed()].into_iter()).into();
 
 		let db = Arc::new(kvdb_memorydb::create(1));
 		let config = Config { col_data: 0 };
 
 		TestState {
-			validators,
+			validators: validators.into_iter().map(|(pair, _)| pair).collect(),
 			validator_public,
 			validator_groups,
 			master_keystore,
@@ -322,7 +334,7 @@ impl TestState {
 		}
 	}
 
-	async fn issue_statement_with_index(
+	async fn issue_explicit_statement_with_index(
 		&self,
 		index: usize,
 		candidate_hash: CandidateHash,
@@ -339,6 +351,32 @@ impl TestState {
 			.unwrap()
 	}
 
+	async fn issue_backing_statement_with_index(
+		&self,
+		index: usize,
+		candidate_hash: CandidateHash,
+		session: SessionIndex,
+	) -> SignedDisputeStatement {
+		let keystore = self.master_keystore.clone() as SyncCryptoStorePtr;
+		let validator_id = self.validators[index].public().into();
+		let context =
+			SigningContext { session_index: session, parent_hash: Hash::repeat_byte(0xac) };
+
+		let statement = SignedFullStatement::sign(
+			&keystore,
+			Statement::Valid(candidate_hash),
+			&context,
+			ValidatorIndex(index as _),
+			&validator_id,
+		)
+		.await
+		.unwrap()
+		.unwrap()
+		.into_unchecked();
+
+		SignedDisputeStatement::from_backing_statement(&statement, context, validator_id).unwrap()
+	}
+
 	fn resume<F>(self, test: F) -> Self
 	where
 		F: FnOnce(TestState, VirtualOverseer) -> BoxFuture<'static, TestState>,
@@ -393,6 +431,477 @@ fn make_invalid_candidate_receipt() -> CandidateReceipt {
 	dummy_candidate_receipt_bad_sig(Default::default(), Some(Default::default()))
 }
 
+#[test]
+fn too_many_unconfirmed_statements_are_considered_spam() {
+	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_receipt1 = make_valid_candidate_receipt();
+			let candidate_hash1 = candidate_receipt1.hash();
+			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;
+
+			let valid_vote1 =
+				test_state.issue_backing_statement_with_index(3, candidate_hash1, session).await;
+
+			let invalid_vote1 = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash1, session, false)
+				.await;
+
+			let valid_vote2 =
+				test_state.issue_backing_statement_with_index(3, candidate_hash1, session).await;
+
+			let invalid_vote2 = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash1, session, false)
+				.await;
+
+			let (pending_confirmation, _confirmation_rx) = oneshot::channel();
+			virtual_overseer
+				.send(FromOverseer::Communication {
+					msg: DisputeCoordinatorMessage::ImportStatements {
+						candidate_hash: candidate_hash1,
+						candidate_receipt: candidate_receipt1.clone(),
+						session,
+						statements: vec![
+							(valid_vote1, ValidatorIndex(3)),
+							(invalid_vote1, ValidatorIndex(1)),
+						],
+						pending_confirmation,
+					},
+				})
+				.await;
+
+			// Participation has to fail, otherwise the dispute will be confirmed.
+			participation_missing_availability(&mut virtual_overseer).await;
+
+			{
+				let (tx, rx) = oneshot::channel();
+				virtual_overseer
+					.send(FromOverseer::Communication {
+						msg: DisputeCoordinatorMessage::ActiveDisputes(tx),
+					})
+					.await;
+
+				assert_eq!(rx.await.unwrap(), vec![(session, candidate_hash1)]);
+
+				let (tx, rx) = oneshot::channel();
+				virtual_overseer
+					.send(FromOverseer::Communication {
+						msg: DisputeCoordinatorMessage::QueryCandidateVotes(
+							vec![(session, candidate_hash1)],
+							tx,
+						),
+					})
+					.await;
+
+				let (_, _, votes) = rx.await.unwrap().get(0).unwrap().clone();
+				assert_eq!(votes.valid.len(), 1);
+				assert_eq!(votes.invalid.len(), 1);
+			}
+
+			let (pending_confirmation, confirmation_rx) = oneshot::channel();
+			virtual_overseer
+				.send(FromOverseer::Communication {
+					msg: DisputeCoordinatorMessage::ImportStatements {
+						candidate_hash: candidate_hash2,
+						candidate_receipt: candidate_receipt2.clone(),
+						session,
+						statements: vec![
+							(valid_vote2, ValidatorIndex(3)),
+							(invalid_vote2, ValidatorIndex(1)),
+						],
+						pending_confirmation,
+					},
+				})
+				.await;
+
+			{
+				let (tx, rx) = oneshot::channel();
+				virtual_overseer
+					.send(FromOverseer::Communication {
+						msg: DisputeCoordinatorMessage::QueryCandidateVotes(
+							vec![(session, candidate_hash2)],
+							tx,
+						),
+					})
+					.await;
+
+				assert_matches!(rx.await.unwrap().get(0), None);
+			}
+
+			// Result should be invalid, because it should be considered spam.
+			assert_matches!(confirmation_rx.await, Ok(ImportStatementsResult::InvalidImport));
+
+			virtual_overseer.send(FromOverseer::Signal(OverseerSignal::Conclude)).await;
+
+			// No more messages expected:
+			assert!(virtual_overseer.try_recv().await.is_none());
+
+			test_state
+		})
+	});
+}
+
+#[test]
+fn dispute_gets_confirmed_via_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_receipt1 = make_valid_candidate_receipt();
+			let candidate_hash1 = candidate_receipt1.hash();
+			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;
+
+			let valid_vote1 = test_state
+				.issue_explicit_statement_with_index(3, candidate_hash1, session, true)
+				.await;
+
+			let invalid_vote1 = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash1, session, false)
+				.await;
+
+			let valid_vote2 = test_state
+				.issue_explicit_statement_with_index(3, candidate_hash1, session, true)
+				.await;
+
+			let invalid_vote2 = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash1, session, false)
+				.await;
+
+			let (pending_confirmation, _confirmation_rx) = oneshot::channel();
+			virtual_overseer
+				.send(FromOverseer::Communication {
+					msg: DisputeCoordinatorMessage::ImportStatements {
+						candidate_hash: candidate_hash1,
+						candidate_receipt: candidate_receipt1.clone(),
+						session,
+						statements: vec![
+							(valid_vote1, ValidatorIndex(3)),
+							(invalid_vote1, ValidatorIndex(1)),
+						],
+						pending_confirmation,
+					},
+				})
+				.await;
+
+			participation_with_distribution(&mut virtual_overseer, &candidate_hash1).await;
+
+			{
+				let (tx, rx) = oneshot::channel();
+				virtual_overseer
+					.send(FromOverseer::Communication {
+						msg: DisputeCoordinatorMessage::ActiveDisputes(tx),
+					})
+					.await;
+
+				assert_eq!(rx.await.unwrap(), vec![(session, candidate_hash1)]);
+
+				let (tx, rx) = oneshot::channel();
+				virtual_overseer
+					.send(FromOverseer::Communication {
+						msg: DisputeCoordinatorMessage::QueryCandidateVotes(
+							vec![(session, candidate_hash1)],
+							tx,
+						),
+					})
+					.await;
+
+				let (_, _, votes) = rx.await.unwrap().get(0).unwrap().clone();
+				assert_eq!(votes.valid.len(), 2);
+				assert_eq!(votes.invalid.len(), 1);
+			}
+
+			let (pending_confirmation, confirmation_rx) = oneshot::channel();
+			virtual_overseer
+				.send(FromOverseer::Communication {
+					msg: DisputeCoordinatorMessage::ImportStatements {
+						candidate_hash: candidate_hash2,
+						candidate_receipt: candidate_receipt2.clone(),
+						session,
+						statements: vec![
+							(valid_vote2, ValidatorIndex(3)),
+							(invalid_vote2, ValidatorIndex(1)),
+						],
+						pending_confirmation,
+					},
+				})
+				.await;
+
+			participation_missing_availability(&mut virtual_overseer).await;
+
+			{
+				let (tx, rx) = oneshot::channel();
+				virtual_overseer
+					.send(FromOverseer::Communication {
+						msg: DisputeCoordinatorMessage::QueryCandidateVotes(
+							vec![(session, candidate_hash2)],
+							tx,
+						),
+					})
+					.await;
+
+				let (_, _, votes) = rx.await.unwrap().get(0).unwrap().clone();
+				assert_eq!(votes.valid.len(), 1);
+				assert_eq!(votes.invalid.len(), 1);
+			}
+
+			// Result should be valid, because our node participated, so spam slots are cleared:
+			assert_matches!(confirmation_rx.await, Ok(ImportStatementsResult::ValidImport));
+
+			virtual_overseer.send(FromOverseer::Signal(OverseerSignal::Conclude)).await;
+
+			// No more messages expected:
+			assert!(virtual_overseer.try_recv().await.is_none());
+
+			test_state
+		})
+	});
+}
+
+#[test]
+fn dispute_gets_confirmed_at_byzantine_threshold() {
+	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_receipt1 = make_valid_candidate_receipt();
+			let candidate_hash1 = candidate_receipt1.hash();
+			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;
+
+			let valid_vote1 = test_state
+				.issue_explicit_statement_with_index(3, candidate_hash1, session, true)
+				.await;
+
+			let invalid_vote1 = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash1, session, false)
+				.await;
+
+			let valid_vote1a = test_state
+				.issue_explicit_statement_with_index(4, candidate_hash1, session, true)
+				.await;
+
+			let invalid_vote1a = test_state
+				.issue_explicit_statement_with_index(5, candidate_hash1, session, false)
+				.await;
+
+			let valid_vote2 = test_state
+				.issue_explicit_statement_with_index(3, candidate_hash1, session, true)
+				.await;
+
+			let invalid_vote2 = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash1, session, false)
+				.await;
+
+			let (pending_confirmation, _confirmation_rx) = oneshot::channel();
+			virtual_overseer
+				.send(FromOverseer::Communication {
+					msg: DisputeCoordinatorMessage::ImportStatements {
+						candidate_hash: candidate_hash1,
+						candidate_receipt: candidate_receipt1.clone(),
+						session,
+						statements: vec![
+							(valid_vote1, ValidatorIndex(3)),
+							(invalid_vote1, ValidatorIndex(1)),
+							(valid_vote1a, ValidatorIndex(4)),
+							(invalid_vote1a, ValidatorIndex(5)),
+						],
+						pending_confirmation,
+					},
+				})
+				.await;
+
+			participation_missing_availability(&mut virtual_overseer).await;
+
+			{
+				let (tx, rx) = oneshot::channel();
+				virtual_overseer
+					.send(FromOverseer::Communication {
+						msg: DisputeCoordinatorMessage::ActiveDisputes(tx),
+					})
+					.await;
+
+				assert_eq!(rx.await.unwrap(), vec![(session, candidate_hash1)]);
+
+				let (tx, rx) = oneshot::channel();
+				virtual_overseer
+					.send(FromOverseer::Communication {
+						msg: DisputeCoordinatorMessage::QueryCandidateVotes(
+							vec![(session, candidate_hash1)],
+							tx,
+						),
+					})
+					.await;
+
+				let (_, _, votes) = rx.await.unwrap().get(0).unwrap().clone();
+				assert_eq!(votes.valid.len(), 2);
+				assert_eq!(votes.invalid.len(), 2);
+			}
+
+			let (pending_confirmation, confirmation_rx) = oneshot::channel();
+			virtual_overseer
+				.send(FromOverseer::Communication {
+					msg: DisputeCoordinatorMessage::ImportStatements {
+						candidate_hash: candidate_hash2,
+						candidate_receipt: candidate_receipt2.clone(),
+						session,
+						statements: vec![
+							(valid_vote2, ValidatorIndex(3)),
+							(invalid_vote2, ValidatorIndex(1)),
+						],
+						pending_confirmation,
+					},
+				})
+				.await;
+
+			participation_missing_availability(&mut virtual_overseer).await;
+
+			{
+				let (tx, rx) = oneshot::channel();
+				virtual_overseer
+					.send(FromOverseer::Communication {
+						msg: DisputeCoordinatorMessage::QueryCandidateVotes(
+							vec![(session, candidate_hash2)],
+							tx,
+						),
+					})
+					.await;
+
+				let (_, _, votes) = rx.await.unwrap().get(0).unwrap().clone();
+				assert_eq!(votes.valid.len(), 1);
+				assert_eq!(votes.invalid.len(), 1);
+			}
+
+			// Result should be valid, because byzantine threshold has been reached in first
+			// import, so spam slots are cleared:
+			assert_matches!(confirmation_rx.await, Ok(ImportStatementsResult::ValidImport));
+
+			virtual_overseer.send(FromOverseer::Signal(OverseerSignal::Conclude)).await;
+
+			// No more messages expected:
+			assert!(virtual_overseer.try_recv().await.is_none());
+
+			test_state
+		})
+	});
+}
+
+#[test]
+fn backing_statements_import_works_and_no_spam() {
+	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).await;
+
+			let valid_vote1 =
+				test_state.issue_backing_statement_with_index(3, candidate_hash, session).await;
+
+			let valid_vote2 =
+				test_state.issue_backing_statement_with_index(4, candidate_hash, session).await;
+
+			let (pending_confirmation, confirmation_rx) = oneshot::channel();
+			virtual_overseer
+				.send(FromOverseer::Communication {
+					msg: DisputeCoordinatorMessage::ImportStatements {
+						candidate_hash,
+						candidate_receipt: candidate_receipt.clone(),
+						session,
+						statements: vec![
+							(valid_vote1, ValidatorIndex(3)),
+							(valid_vote2, ValidatorIndex(4)),
+						],
+						pending_confirmation,
+					},
+				})
+				.await;
+			assert_matches!(confirmation_rx.await, Ok(ImportStatementsResult::ValidImport));
+
+			{
+				// Just backing votes - we should not have any active disputes now.
+				let (tx, rx) = oneshot::channel();
+				virtual_overseer
+					.send(FromOverseer::Communication {
+						msg: DisputeCoordinatorMessage::ActiveDisputes(tx),
+					})
+					.await;
+
+				assert!(rx.await.unwrap().is_empty());
+
+				let (tx, rx) = oneshot::channel();
+				virtual_overseer
+					.send(FromOverseer::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);
+				assert_eq!(votes.invalid.len(), 0);
+			}
+
+			let candidate_receipt = make_invalid_candidate_receipt();
+			let candidate_hash = candidate_receipt.hash();
+
+			let valid_vote1 =
+				test_state.issue_backing_statement_with_index(3, candidate_hash, session).await;
+
+			let valid_vote2 =
+				test_state.issue_backing_statement_with_index(4, candidate_hash, session).await;
+
+			let (pending_confirmation, confirmation_rx) = oneshot::channel();
+			// Backing vote import should not have accounted to spam slots, so this should succeed
+			// as well:
+			virtual_overseer
+				.send(FromOverseer::Communication {
+					msg: DisputeCoordinatorMessage::ImportStatements {
+						candidate_hash,
+						candidate_receipt: candidate_receipt.clone(),
+						session,
+						statements: vec![
+							(valid_vote1, ValidatorIndex(3)),
+							(valid_vote2, ValidatorIndex(4)),
+						],
+						pending_confirmation,
+					},
+				})
+				.await;
+
+			// Result should be valid, because our node participated, so spam slots are cleared:
+			assert_matches!(confirmation_rx.await, Ok(ImportStatementsResult::ValidImport));
+
+			virtual_overseer.send(FromOverseer::Signal(OverseerSignal::Conclude)).await;
+
+			// No more messages expected:
+			assert!(virtual_overseer.try_recv().await.is_none());
+
+			test_state
+		})
+	});
+}
+
 #[test]
 fn conflicting_votes_lead_to_dispute_participation() {
 	test_harness(|mut test_state, mut virtual_overseer| {
@@ -406,14 +915,17 @@ fn conflicting_votes_lead_to_dispute_participation() {
 
 			test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await;
 
-			let valid_vote =
-				test_state.issue_statement_with_index(3, candidate_hash, session, true).await;
+			let valid_vote = test_state
+				.issue_explicit_statement_with_index(3, candidate_hash, session, true)
+				.await;
 
-			let invalid_vote =
-				test_state.issue_statement_with_index(1, candidate_hash, session, false).await;
+			let invalid_vote = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash, session, false)
+				.await;
 
-			let invalid_vote_2 =
-				test_state.issue_statement_with_index(2, candidate_hash, session, false).await;
+			let invalid_vote_2 = test_state
+				.issue_explicit_statement_with_index(2, candidate_hash, session, false)
+				.await;
 
 			let (pending_confirmation, _confirmation_rx) = oneshot::channel();
 			virtual_overseer
@@ -510,11 +1022,13 @@ fn positive_votes_dont_trigger_participation() {
 
 			test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await;
 
-			let valid_vote =
-				test_state.issue_statement_with_index(2, candidate_hash, session, true).await;
+			let valid_vote = test_state
+				.issue_explicit_statement_with_index(2, candidate_hash, session, true)
+				.await;
 
-			let valid_vote_2 =
-				test_state.issue_statement_with_index(1, candidate_hash, session, true).await;
+			let valid_vote_2 = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash, session, true)
+				.await;
 
 			let (pending_confirmation, _confirmation_rx) = oneshot::channel();
 			virtual_overseer
@@ -615,11 +1129,13 @@ fn wrong_validator_index_is_ignored() {
 
 			test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await;
 
-			let valid_vote =
-				test_state.issue_statement_with_index(2, candidate_hash, session, true).await;
+			let valid_vote = test_state
+				.issue_explicit_statement_with_index(2, candidate_hash, session, true)
+				.await;
 
-			let invalid_vote =
-				test_state.issue_statement_with_index(1, candidate_hash, session, false).await;
+			let invalid_vote = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash, session, false)
+				.await;
 
 			let (pending_confirmation, _confirmation_rx) = oneshot::channel();
 			virtual_overseer
@@ -685,11 +1201,13 @@ fn finality_votes_ignore_disputed_candidates() {
 
 			test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await;
 
-			let valid_vote =
-				test_state.issue_statement_with_index(2, candidate_hash, session, true).await;
+			let valid_vote = test_state
+				.issue_explicit_statement_with_index(2, candidate_hash, session, true)
+				.await;
 
-			let invalid_vote =
-				test_state.issue_statement_with_index(1, candidate_hash, session, false).await;
+			let invalid_vote = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash, session, false)
+				.await;
 
 			let (pending_confirmation, _confirmation_rx) = oneshot::channel();
 			virtual_overseer
@@ -781,11 +1299,13 @@ fn supermajority_valid_dispute_may_be_finalized() {
 			let supermajority_threshold =
 				polkadot_primitives::v1::supermajority_threshold(test_state.validators.len());
 
-			let valid_vote =
-				test_state.issue_statement_with_index(2, candidate_hash, session, true).await;
+			let valid_vote = test_state
+				.issue_explicit_statement_with_index(2, candidate_hash, session, true)
+				.await;
 
-			let invalid_vote =
-				test_state.issue_statement_with_index(1, candidate_hash, session, false).await;
+			let invalid_vote = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash, session, false)
+				.await;
 
 			let (pending_confirmation, _confirmation_rx) = oneshot::channel();
 			virtual_overseer
@@ -807,8 +1327,9 @@ fn supermajority_valid_dispute_may_be_finalized() {
 
 			let mut statements = Vec::new();
 			for i in (0..supermajority_threshold - 1).map(|i| i + 3) {
-				let vote =
-					test_state.issue_statement_with_index(i, candidate_hash, session, true).await;
+				let vote = test_state
+					.issue_explicit_statement_with_index(i, candidate_hash, session, true)
+					.await;
 
 				statements.push((vote, ValidatorIndex(i as _)));
 			}
@@ -898,11 +1419,13 @@ fn concluded_supermajority_for_non_active_after_time() {
 			let supermajority_threshold =
 				polkadot_primitives::v1::supermajority_threshold(test_state.validators.len());
 
-			let valid_vote =
-				test_state.issue_statement_with_index(2, candidate_hash, session, true).await;
+			let valid_vote = test_state
+				.issue_explicit_statement_with_index(2, candidate_hash, session, true)
+				.await;
 
-			let invalid_vote =
-				test_state.issue_statement_with_index(1, candidate_hash, session, false).await;
+			let invalid_vote = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash, session, false)
+				.await;
 
 			let (pending_confirmation, _confirmation_rx) = oneshot::channel();
 			virtual_overseer
@@ -925,8 +1448,9 @@ fn concluded_supermajority_for_non_active_after_time() {
 			let mut statements = Vec::new();
 			// -2: 1 for already imported vote and one for local vote (which is valid).
 			for i in (0..supermajority_threshold - 2).map(|i| i + 3) {
-				let vote =
-					test_state.issue_statement_with_index(i, candidate_hash, session, true).await;
+				let vote = test_state
+					.issue_explicit_statement_with_index(i, candidate_hash, session, true)
+					.await;
 
 				statements.push((vote, ValidatorIndex(i as _)));
 			}
@@ -993,11 +1517,13 @@ fn concluded_supermajority_against_non_active_after_time() {
 			let supermajority_threshold =
 				polkadot_primitives::v1::supermajority_threshold(test_state.validators.len());
 
-			let valid_vote =
-				test_state.issue_statement_with_index(2, candidate_hash, session, true).await;
+			let valid_vote = test_state
+				.issue_explicit_statement_with_index(2, candidate_hash, session, true)
+				.await;
 
-			let invalid_vote =
-				test_state.issue_statement_with_index(1, candidate_hash, session, false).await;
+			let invalid_vote = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash, session, false)
+				.await;
 
 			let (pending_confirmation, confirmation_rx) = oneshot::channel();
 			virtual_overseer
@@ -1023,8 +1549,9 @@ fn concluded_supermajority_against_non_active_after_time() {
 			let mut statements = Vec::new();
 			// minus 2, because of local vote and one previously imported invalid vote.
 			for i in (0..supermajority_threshold - 2).map(|i| i + 3) {
-				let vote =
-					test_state.issue_statement_with_index(i, candidate_hash, session, false).await;
+				let vote = test_state
+					.issue_explicit_statement_with_index(i, candidate_hash, session, false)
+					.await;
 
 				statements.push((vote, ValidatorIndex(i as _)));
 			}
@@ -1090,11 +1617,13 @@ fn resume_dispute_without_local_statement() {
 
 			test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await;
 
-			let valid_vote =
-				test_state.issue_statement_with_index(1, candidate_hash, session, true).await;
+			let valid_vote = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash, session, true)
+				.await;
 
-			let invalid_vote =
-				test_state.issue_statement_with_index(2, candidate_hash, session, false).await;
+			let invalid_vote = test_state
+				.issue_explicit_statement_with_index(2, candidate_hash, session, false)
+				.await;
 
 			let (pending_confirmation, confirmation_rx) = oneshot::channel();
 			virtual_overseer
@@ -1146,14 +1675,24 @@ fn resume_dispute_without_local_statement() {
 
 			participation_with_distribution(&mut virtual_overseer, &candidate_hash).await;
 
-			let valid_vote0 =
-				test_state.issue_statement_with_index(0, candidate_hash, session, true).await;
-			let valid_vote3 =
-				test_state.issue_statement_with_index(3, candidate_hash, session, true).await;
-			let valid_vote4 =
-				test_state.issue_statement_with_index(4, candidate_hash, session, true).await;
-			let valid_vote5 =
-				test_state.issue_statement_with_index(5, candidate_hash, session, true).await;
+			let valid_vote0 = test_state
+				.issue_explicit_statement_with_index(0, candidate_hash, session, true)
+				.await;
+			let valid_vote3 = test_state
+				.issue_explicit_statement_with_index(3, candidate_hash, session, true)
+				.await;
+			let valid_vote4 = test_state
+				.issue_explicit_statement_with_index(4, candidate_hash, session, true)
+				.await;
+			let valid_vote5 = test_state
+				.issue_explicit_statement_with_index(5, candidate_hash, session, true)
+				.await;
+			let valid_vote6 = test_state
+				.issue_explicit_statement_with_index(6, candidate_hash, session, true)
+				.await;
+			let valid_vote7 = test_state
+				.issue_explicit_statement_with_index(7, candidate_hash, session, true)
+				.await;
 
 			let (pending_confirmation, _confirmation_rx) = oneshot::channel();
 			virtual_overseer
@@ -1167,6 +1706,8 @@ fn resume_dispute_without_local_statement() {
 							(valid_vote3, ValidatorIndex(3)),
 							(valid_vote4, ValidatorIndex(4)),
 							(valid_vote5, ValidatorIndex(5)),
+							(valid_vote6, ValidatorIndex(6)),
+							(valid_vote7, ValidatorIndex(7)),
 						],
 						pending_confirmation,
 					},
@@ -1210,14 +1751,17 @@ fn resume_dispute_with_local_statement() {
 
 			test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await;
 
-			let local_valid_vote =
-				test_state.issue_statement_with_index(0, candidate_hash, session, true).await;
+			let local_valid_vote = test_state
+				.issue_explicit_statement_with_index(0, candidate_hash, session, true)
+				.await;
 
-			let valid_vote =
-				test_state.issue_statement_with_index(1, candidate_hash, session, true).await;
+			let valid_vote = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash, session, true)
+				.await;
 
-			let invalid_vote =
-				test_state.issue_statement_with_index(2, candidate_hash, session, false).await;
+			let invalid_vote = test_state
+				.issue_explicit_statement_with_index(2, candidate_hash, session, false)
+				.await;
 
 			let (pending_confirmation, confirmation_rx) = oneshot::channel();
 			virtual_overseer
@@ -1277,7 +1821,8 @@ fn resume_dispute_with_local_statement() {
 fn resume_dispute_without_local_statement_or_local_key() {
 	let session = 1;
 	let mut test_state = TestState::default();
-	test_state.subsystem_keystore = make_keystore(&[Sr25519Keyring::Two]).into();
+	test_state.subsystem_keystore =
+		make_keystore(vec![Sr25519Keyring::Two.to_seed()].into_iter()).into();
 	test_state
 		.resume(|mut test_state, mut virtual_overseer| {
 			Box::pin(async move {
@@ -1288,11 +1833,13 @@ fn resume_dispute_without_local_statement_or_local_key() {
 
 				test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await;
 
-				let valid_vote =
-					test_state.issue_statement_with_index(1, candidate_hash, session, true).await;
+				let valid_vote = test_state
+					.issue_explicit_statement_with_index(1, candidate_hash, session, true)
+					.await;
 
-				let invalid_vote =
-					test_state.issue_statement_with_index(2, candidate_hash, session, false).await;
+				let invalid_vote = test_state
+					.issue_explicit_statement_with_index(2, candidate_hash, session, false)
+					.await;
 
 				let (pending_confirmation, confirmation_rx) = oneshot::channel();
 				virtual_overseer
@@ -1364,14 +1911,17 @@ fn resume_dispute_with_local_statement_without_local_key() {
 
 			test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await;
 
-			let local_valid_vote =
-				test_state.issue_statement_with_index(0, candidate_hash, session, true).await;
+			let local_valid_vote = test_state
+				.issue_explicit_statement_with_index(0, candidate_hash, session, true)
+				.await;
 
-			let valid_vote =
-				test_state.issue_statement_with_index(1, candidate_hash, session, true).await;
+			let valid_vote = test_state
+				.issue_explicit_statement_with_index(1, candidate_hash, session, true)
+				.await;
 
-			let invalid_vote =
-				test_state.issue_statement_with_index(2, candidate_hash, session, false).await;
+			let invalid_vote = test_state
+				.issue_explicit_statement_with_index(2, candidate_hash, session, false)
+				.await;
 
 			let (pending_confirmation, confirmation_rx) = oneshot::channel();
 			virtual_overseer
@@ -1411,7 +1961,8 @@ fn resume_dispute_with_local_statement_without_local_key() {
 		})
 	});
 	// No keys:
-	test_state.subsystem_keystore = make_keystore(&[Sr25519Keyring::Two]).into();
+	test_state.subsystem_keystore =
+		make_keystore(vec![Sr25519Keyring::Two.to_seed()].into_iter()).into();
 	// Two should not send a DisputeParticiationMessage::Participate on restart since we gave
 	// her a non existing key.
 	test_state.resume(|test_state, mut virtual_overseer| {
@@ -1453,7 +2004,7 @@ fn issue_local_statement_does_cause_distribution_but_not_duplicate_participation
 			test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await;
 
 			let other_vote = test_state
-				.issue_statement_with_index(1, candidate_hash, session, !validity)
+				.issue_explicit_statement_with_index(1, candidate_hash, session, !validity)
 				.await;
 
 			let (pending_confirmation, confirmation_rx) = oneshot::channel();
@@ -1606,10 +2157,10 @@ fn redundant_votes_ignored() {
 			test_state.activate_leaf_at_session(&mut virtual_overseer, session, 1).await;
 
 			let valid_vote =
-				test_state.issue_statement_with_index(1, candidate_hash, session, true).await;
+				test_state.issue_backing_statement_with_index(1, candidate_hash, session).await;
 
 			let valid_vote_2 =
-				test_state.issue_statement_with_index(1, candidate_hash, session, true).await;
+				test_state.issue_backing_statement_with_index(1, candidate_hash, session).await;
 
 			assert!(valid_vote.validator_signature() != valid_vote_2.validator_signature());