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>
This commit is contained in:
Bastian Köcher
2021-01-30 00:49:14 +01:00
committed by GitHub
parent 52fcabdbb0
commit 75c5c3d4ef
2 changed files with 47 additions and 65 deletions
+39 -27
View File
@@ -54,10 +54,12 @@ use futures::{
}; };
use log::trace; 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; use wait_on_relay_chain_block::WaitOnRelayChainBlock;
const LOG_TARGET: &str = "cumulus-network";
type BoxedError = Box<dyn std::error::Error + Send>; type BoxedError = Box<dyn std::error::Error + Send>;
#[derive(Debug)] #[derive(Debug)]
@@ -84,26 +86,33 @@ impl BlockAnnounceData {
/// Validate that the receipt, statement and announced header match. /// Validate that the receipt, statement and announced header match.
/// ///
/// This will not check the signature, for this you should use [`BlockAnnounceData::check_signature`]. /// 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() { let candidate_hash = if let CompactStatement::Candidate(h) = self.statement.payload() {
h h
} else { } else {
return Err(BlockAnnounceError( log::debug!(
"`CompactStatement` isn't the candidate variant!".into(), target: LOG_TARGET,
)); "`CompactStatement` isn't the candidate variant!",
);
return Err(Validation::Failure { disconnect: true });
}; };
if *candidate_hash != self.receipt.hash() { if *candidate_hash != self.receipt.hash() {
return Err(BlockAnnounceError( log::debug!(
"Receipt candidate hash doesn't match candidate hash in statement".into(), 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( log::debug!(
"Receipt para head hash doesn't match the hash of the header in the block announcement".into(), 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(()) Ok(())
@@ -112,7 +121,7 @@ impl BlockAnnounceData {
/// Check the signature of the statement. /// Check the signature of the statement.
/// ///
/// Returns an `Err(_)` if it failed. /// 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 where
P: ProvideRuntimeApi<PBlock> + Send + Sync + 'static, P: ProvideRuntimeApi<PBlock> + Send + Sync + 'static,
P::Api: ParachainHost<PBlock>, P::Api: ParachainHost<PBlock>,
@@ -143,10 +152,12 @@ impl BlockAnnounceData {
let signer = match authorities.get(validator_index as usize) { let signer = match authorities.get(validator_index as usize) {
Some(r) => r, Some(r) => r,
None => { None => {
return Err(BlockAnnounceError( log::debug!(
"block accouncement justification signer is a validator index out of bound" target: LOG_TARGET,
.to_string(), "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) .check_signature(&signing_context, &signer)
.is_err() .is_err()
{ {
return Err(BlockAnnounceError( log::debug!(
"block announcement justification signature is invalid".to_string(), 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(); let wait_on_relay_chain_block = self.wait_on_relay_chain_block.clone();
async move { async move {
block_announce_data if let Err(e) = block_announce_data.validate(header_encoded) {
.validate(header_encoded) return Ok(e);
.map_err(|e| Box::new(e) as Box<_>)?; }
let relay_parent = block_announce_data.receipt.descriptor.relay_parent; let relay_parent = block_announce_data.receipt.descriptor.relay_parent;
@@ -353,9 +367,7 @@ where
block_announce_data block_announce_data
.check_signature(&relay_chain_client) .check_signature(&relay_chain_client)
.map_err(|e| Box::new(e) as Box<_>)?; .map_err(|e| Box::new(e) as Box<_>)
Ok(Validation::Success { is_new_best: true })
} }
.boxed() .boxed()
} }
+8 -38
View File
@@ -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 (signed_statement, header) = block_on(make_gossip_message_and_header_using_genesis(api, 1));
let data = BlockAnnounceData::try_from(signed_statement).unwrap().encode(); let data = BlockAnnounceData::try_from(signed_statement).unwrap().encode();
let res = block_on(validator.validate(&header, &data)) let res = block_on(validator.validate(&header, &data));
.err() assert_eq!(Validation::Failure { disconnect: true }, res.unwrap());
.expect("Should fail on invalid validator");
assert!(matches!(
*res.downcast::<BlockAnnounceError>().unwrap(),
BlockAnnounceError(x) if x.contains("signer is a validator")
));
} }
#[test] #[test]
@@ -227,16 +221,8 @@ fn check_statement_is_correctly_signed() {
let last = data.len() - 1; let last = data.len() - 1;
data[last] = data[last].wrapping_add(1); data[last] = data[last].wrapping_add(1);
let res = block_on(validator.validate(&header, &data)) let res = block_on(validator.validate(&header, &data));
.err() assert_eq!(Validation::Failure { disconnect: true }, res.unwrap());
.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")
)
});
} }
#[test] #[test]
@@ -276,16 +262,8 @@ fn check_statement_seconded() {
statement: signed_statement.convert_payload(), statement: signed_statement.convert_payload(),
}.encode(); }.encode();
let res = block_on(validator.validate(&header, &data)) let res = block_on(validator.validate(&header, &data));
.err() assert_eq!(Validation::Failure { disconnect: true }, res.unwrap());
.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")
)
});
} }
#[test] #[test]
@@ -297,16 +275,8 @@ fn check_header_match_candidate_receipt_header() {
let data = BlockAnnounceData::try_from(signed_statement).unwrap().encode(); let data = BlockAnnounceData::try_from(signed_statement).unwrap().encode();
header.number = 300; header.number = 300;
let res = block_on(validator.validate(&header, &data)) let res = block_on(validator.validate(&header, &data));
.err() assert_eq!(Validation::Failure { disconnect: true }, res.unwrap());
.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")
)
});
} }
/// Test that ensures that we postpone the block announce verification until /// Test that ensures that we postpone the block announce verification until