From 75c5c3d4efadf636db8ec4b5beb6c0dbf76121fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bastian=20K=C3=B6cher?= <bkchr@users.noreply.github.com>
Date: Sat, 30 Jan 2021 00:49:14 +0100
Subject: [PATCH] Block announce validation should use the correct `Validation`
 result (#315)

* Block announce validation should use the correct `Validation` result

The error variant is just for internal errors and we need to return
`Failure` always when the other node send us an invalid statement.

* Update network/src/lib.rs

Co-authored-by: Sergei Shulepov <sergei@parity.io>

Co-authored-by: Sergei Shulepov <sergei@parity.io>
---
 cumulus/network/src/lib.rs   | 66 +++++++++++++++++++++---------------
 cumulus/network/src/tests.rs | 46 +++++--------------------
 2 files changed, 47 insertions(+), 65 deletions(-)

diff --git a/cumulus/network/src/lib.rs b/cumulus/network/src/lib.rs
index b24b7bbbd14..6225233e9cf 100644
--- a/cumulus/network/src/lib.rs
+++ b/cumulus/network/src/lib.rs
@@ -54,10 +54,12 @@ use futures::{
 };
 use log::trace;
 
-use std::{fmt, marker::PhantomData, pin::Pin, sync::Arc, convert::TryFrom};
+use std::{convert::TryFrom, fmt, marker::PhantomData, pin::Pin, sync::Arc};
 
 use wait_on_relay_chain_block::WaitOnRelayChainBlock;
 
+const LOG_TARGET: &str = "cumulus-network";
+
 type BoxedError = Box<dyn std::error::Error + Send>;
 
 #[derive(Debug)]
@@ -84,26 +86,33 @@ impl BlockAnnounceData {
 	/// Validate that the receipt, statement and announced header match.
 	///
 	/// This will not check the signature, for this you should use [`BlockAnnounceData::check_signature`].
-	fn validate(&self, encoded_header: Vec<u8>) -> Result<(), BlockAnnounceError> {
+	fn validate(&self, encoded_header: Vec<u8>) -> Result<(), Validation> {
 		let candidate_hash = if let CompactStatement::Candidate(h) = self.statement.payload() {
 			h
 		} else {
-			return Err(BlockAnnounceError(
-				"`CompactStatement` isn't the candidate variant!".into(),
-			));
+			log::debug!(
+				target: LOG_TARGET,
+				"`CompactStatement` isn't the candidate variant!",
+			);
+			return Err(Validation::Failure { disconnect: true });
 		};
 
 		if *candidate_hash != self.receipt.hash() {
-			return Err(BlockAnnounceError(
-				"Receipt candidate hash doesn't match candidate hash in statement".into(),
-			));
+			log::debug!(
+				target: LOG_TARGET,
+				"Receipt candidate hash doesn't match candidate hash in statement",
+			);
+			return Err(Validation::Failure { disconnect: true });
 		}
 
-		if polkadot_parachain::primitives::HeadData(encoded_header).hash() != self.receipt.descriptor.para_head
+		if polkadot_parachain::primitives::HeadData(encoded_header).hash()
+			!= self.receipt.descriptor.para_head
 		{
-			return Err(BlockAnnounceError(
-				"Receipt para head hash doesn't match the hash of the header in the block announcement".into(),
-			));
+			log::debug!(
+				target: LOG_TARGET,
+				"Receipt para head hash doesn't match the hash of the header in the block announcement",
+			);
+			return Err(Validation::Failure { disconnect: true });
 		}
 
 		Ok(())
@@ -112,7 +121,7 @@ impl BlockAnnounceData {
 	/// Check the signature of the statement.
 	///
 	/// Returns an `Err(_)` if it failed.
-	fn check_signature<P>(&self, relay_chain_client: &Arc<P>) -> Result<(), BlockAnnounceError>
+	fn check_signature<P>(&self, relay_chain_client: &Arc<P>) -> Result<Validation, BlockAnnounceError>
 	where
 		P: ProvideRuntimeApi<PBlock> + Send + Sync + 'static,
 		P::Api: ParachainHost<PBlock>,
@@ -143,10 +152,12 @@ impl BlockAnnounceData {
 		let signer = match authorities.get(validator_index as usize) {
 			Some(r) => r,
 			None => {
-				return Err(BlockAnnounceError(
-					"block accouncement justification signer is a validator index out of bound"
-						.to_string(),
-				));
+				log::debug!(
+					target: LOG_TARGET,
+					"Block announcement justification signer is a validator index out of bound",
+				);
+
+				return Ok(Validation::Failure { disconnect: true })
 			}
 		};
 
@@ -156,12 +167,15 @@ impl BlockAnnounceData {
 			.check_signature(&signing_context, &signer)
 			.is_err()
 		{
-			return Err(BlockAnnounceError(
-				"block announcement justification signature is invalid".to_string(),
-			));
+			log::debug!(
+				target: LOG_TARGET,
+				"Block announcement justification signature is invalid.",
+			);
+
+			return Ok(Validation::Failure { disconnect: true });
 		}
 
-		Ok(())
+		Ok(Validation::Success { is_new_best: true })
 	}
 }
 
@@ -340,9 +354,9 @@ where
 		let wait_on_relay_chain_block = self.wait_on_relay_chain_block.clone();
 
 		async move {
-			block_announce_data
-				.validate(header_encoded)
-				.map_err(|e| Box::new(e) as Box<_>)?;
+			if let Err(e) = block_announce_data.validate(header_encoded) {
+				return Ok(e);
+			}
 
 			let relay_parent = block_announce_data.receipt.descriptor.relay_parent;
 
@@ -353,9 +367,7 @@ where
 
 			block_announce_data
 				.check_signature(&relay_chain_client)
-				.map_err(|e| Box::new(e) as Box<_>)?;
-
-			Ok(Validation::Success { is_new_best: true })
+				.map_err(|e| Box::new(e) as Box<_>)
 		}
 		.boxed()
 	}
diff --git a/cumulus/network/src/tests.rs b/cumulus/network/src/tests.rs
index 19c2938e0c9..2937c06ac99 100644
--- a/cumulus/network/src/tests.rs
+++ b/cumulus/network/src/tests.rs
@@ -205,14 +205,8 @@ fn check_signer_is_legit_validator() {
 	let (signed_statement, header) = block_on(make_gossip_message_and_header_using_genesis(api, 1));
 	let data = BlockAnnounceData::try_from(signed_statement).unwrap().encode();
 
-	let res = block_on(validator.validate(&header, &data))
-		.err()
-		.expect("Should fail on invalid validator");
-
-	assert!(matches!(
-		*res.downcast::<BlockAnnounceError>().unwrap(),
-		BlockAnnounceError(x) if x.contains("signer is a validator")
-	));
+	let res = block_on(validator.validate(&header, &data));
+	assert_eq!(Validation::Failure { disconnect: true }, res.unwrap());
 }
 
 #[test]
@@ -227,16 +221,8 @@ fn check_statement_is_correctly_signed() {
 	let last = data.len() - 1;
 	data[last] = data[last].wrapping_add(1);
 
-	let res = block_on(validator.validate(&header, &data))
-		.err()
-		.expect("Validation should fail if the statement is not signed correctly");
-
-	check_error(res, |error| {
-		matches!(
-			error,
-			BlockAnnounceError(x) if x.contains("signature is invalid")
-		)
-	});
+	let res = block_on(validator.validate(&header, &data));
+	assert_eq!(Validation::Failure { disconnect: true }, res.unwrap());
 }
 
 #[test]
@@ -276,16 +262,8 @@ fn check_statement_seconded() {
 		statement: signed_statement.convert_payload(),
 	}.encode();
 
-	let res = block_on(validator.validate(&header, &data))
-		.err()
-		.expect("validation should fail if not seconded statement");
-
-	check_error(res, |error| {
-		matches!(
-			error,
-			BlockAnnounceError(x) if x.contains("`CompactStatement` isn't the candidate variant")
-		)
-	});
+	let res = block_on(validator.validate(&header, &data));
+	assert_eq!(Validation::Failure { disconnect: true }, res.unwrap());
 }
 
 #[test]
@@ -297,16 +275,8 @@ fn check_header_match_candidate_receipt_header() {
 	let data = BlockAnnounceData::try_from(signed_statement).unwrap().encode();
 	header.number = 300;
 
-	let res = block_on(validator.validate(&header, &data))
-		.err()
-		.expect("validation should fail if the header in doesn't match");
-
-	check_error(res, |error| {
-		matches!(
-			error,
-			BlockAnnounceError(x) if x.contains("Receipt para head hash doesn't match")
-		)
-	});
+	let res = block_on(validator.validate(&header, &data));
+	assert_eq!(Validation::Failure { disconnect: true }, res.unwrap());
 }
 
 /// Test that ensures that we postpone the block announce verification until
-- 
GitLab