Check signatures as "Compact" in statement distribution (#5071)

* allow converting payloads _up_

* convert to superpayload in statement-distribution

* Update primitives/src/v2/signed.rs

Co-authored-by: Andronik <write@reusable.software>

Co-authored-by: Andronik <write@reusable.software>
This commit is contained in:
Robert Habermeier
2022-03-10 14:49:35 -06:00
committed by GitHub
parent 22a7fad75f
commit 8a17c614f0
5 changed files with 109 additions and 27 deletions
@@ -36,7 +36,8 @@ use polkadot_node_subsystem_util::{self as util, MIN_GOSSIP_PEERS};
use polkadot_primitives::v2::{
AuthorityDiscoveryId, CandidateHash, CommittedCandidateReceipt, CompactStatement, Hash,
SigningContext, ValidatorId, ValidatorIndex, ValidatorSignature,
SignedStatement, SigningContext, UncheckedSignedStatement, ValidatorId, ValidatorIndex,
ValidatorSignature,
};
use polkadot_subsystem::{
jaeger,
@@ -780,10 +781,10 @@ impl ActiveHeadData {
/// without modifying the internal state.
fn check_useful_or_unknown(
&self,
statement: &UncheckedSignedFullStatement,
statement: &UncheckedSignedStatement,
) -> std::result::Result<(), DeniedStatement> {
let validator_index = statement.unchecked_validator_index();
let compact = statement.unchecked_payload().to_compact();
let compact = statement.unchecked_payload();
let comparator = StoredStatementComparator {
compact: compact.clone(),
validator_index,
@@ -857,8 +858,8 @@ impl ActiveHeadData {
fn check_statement_signature(
head: &ActiveHeadData,
relay_parent: Hash,
statement: UncheckedSignedFullStatement,
) -> std::result::Result<SignedFullStatement, UncheckedSignedFullStatement> {
statement: UncheckedSignedStatement,
) -> std::result::Result<SignedStatement, UncheckedSignedStatement> {
let signing_context =
SigningContext { session_index: head.session_index, parent_hash: relay_parent };
@@ -1387,24 +1388,57 @@ async fn handle_incoming_message<'a>(
return None
}
let checked_compact = {
let (compact, validator_index) = message.get_fingerprint();
let signature = message.get_signature();
let unchecked_compact = UncheckedSignedStatement::new(compact, validator_index, signature);
match active_head.check_useful_or_unknown(&unchecked_compact) {
Ok(()) => {},
Err(DeniedStatement::NotUseful) => return None,
Err(DeniedStatement::UsefulButKnown) => {
report_peer(ctx, peer, BENEFIT_VALID_STATEMENT).await;
return None
},
}
// check the signature on the statement.
match check_statement_signature(&active_head, relay_parent, unchecked_compact) {
Err(statement) => {
tracing::debug!(
target: LOG_TARGET,
?peer,
?statement,
"Invalid statement signature"
);
report_peer(ctx, peer, COST_INVALID_SIGNATURE).await;
return None
},
Ok(statement) => statement,
}
};
// Fetch from the network only after signature and usefulness checks are completed.
let is_large_statement = message.is_large_statement();
let statement =
retrieve_statement_from_message(peer, message, active_head, ctx, req_sender, metrics)
.await?;
match active_head.check_useful_or_unknown(&statement) {
Ok(()) => {},
Err(DeniedStatement::NotUseful) => return None,
Err(DeniedStatement::UsefulButKnown) => {
report_peer(ctx, peer, BENEFIT_VALID_STATEMENT).await;
return None
},
}
let payload = statement.unchecked_into_payload();
// check the signature on the statement.
let statement = match check_statement_signature(&active_head, relay_parent, statement) {
Err(statement) => {
tracing::debug!(target: LOG_TARGET, ?peer, ?statement, "Invalid statement signature");
report_peer(ctx, peer, COST_INVALID_SIGNATURE).await;
// Upgrade the `Signed` wrapper from the compact payload to the full payload.
// This fails if the payload doesn't encode correctly.
let statement: SignedFullStatement = match checked_compact.convert_to_superpayload(payload) {
Err((compact, _)) => {
tracing::debug!(
target: LOG_TARGET,
?peer,
?compact,
is_large_statement,
"Full statement had bad payload."
);
report_peer(ctx, peer, COST_WRONG_HASH).await;
return None
},
Ok(statement) => statement,
@@ -105,14 +105,16 @@ fn active_head_accepts_only_2_seconded_per_validator() {
.ok()
.flatten()
.expect("should be signed");
assert!(head_data.check_useful_or_unknown(&a_seconded_val_0.clone().into()).is_ok());
assert!(head_data
.check_useful_or_unknown(&a_seconded_val_0.clone().convert_payload().into())
.is_ok());
let noted = head_data.note_statement(a_seconded_val_0.clone());
assert_matches!(noted, NotedStatement::Fresh(_));
// note A (duplicate)
assert_eq!(
head_data.check_useful_or_unknown(&a_seconded_val_0.clone().into()),
head_data.check_useful_or_unknown(&a_seconded_val_0.clone().convert_payload().into()),
Err(DeniedStatement::UsefulButKnown),
);
let noted = head_data.note_statement(a_seconded_val_0);
@@ -130,7 +132,9 @@ fn active_head_accepts_only_2_seconded_per_validator() {
.ok()
.flatten()
.expect("should be signed");
assert!(head_data.check_useful_or_unknown(&statement.clone().into()).is_ok());
assert!(head_data
.check_useful_or_unknown(&statement.clone().convert_payload().into())
.is_ok());
let noted = head_data.note_statement(statement);
assert_matches!(noted, NotedStatement::Fresh(_));
@@ -146,7 +150,7 @@ fn active_head_accepts_only_2_seconded_per_validator() {
.flatten()
.expect("should be signed");
assert_eq!(
head_data.check_useful_or_unknown(&statement.clone().into()),
head_data.check_useful_or_unknown(&statement.clone().convert_payload().into()),
Err(DeniedStatement::NotUseful),
);
let noted = head_data.note_statement(statement);
@@ -163,7 +167,9 @@ fn active_head_accepts_only_2_seconded_per_validator() {
.ok()
.flatten()
.expect("should be signed");
assert!(head_data.check_useful_or_unknown(&statement.clone().into()).is_ok());
assert!(head_data
.check_useful_or_unknown(&statement.clone().convert_payload().into())
.is_ok());
let noted = head_data.note_statement(statement);
assert_matches!(noted, NotedStatement::Fresh(_));
@@ -178,7 +184,9 @@ fn active_head_accepts_only_2_seconded_per_validator() {
.ok()
.flatten()
.expect("should be signed");
assert!(head_data.check_useful_or_unknown(&statement.clone().into()).is_ok());
assert!(head_data
.check_useful_or_unknown(&statement.clone().convert_payload().into())
.is_ok());
let noted = head_data.note_statement(statement);
assert_matches!(noted, NotedStatement::Fresh(_));
}
@@ -428,7 +436,9 @@ fn peer_view_update_sends_messages() {
.ok()
.flatten()
.expect("should be signed");
assert!(data.check_useful_or_unknown(&statement.clone().into()).is_ok());
assert!(data
.check_useful_or_unknown(&statement.clone().convert_payload().into())
.is_ok());
let noted = data.note_statement(statement);
assert_matches!(noted, NotedStatement::Fresh(_));
@@ -443,7 +453,9 @@ fn peer_view_update_sends_messages() {
.ok()
.flatten()
.expect("should be signed");
assert!(data.check_useful_or_unknown(&statement.clone().into()).is_ok());
assert!(data
.check_useful_or_unknown(&statement.clone().convert_payload().into())
.is_ok());
let noted = data.note_statement(statement);
assert_matches!(noted, NotedStatement::Fresh(_));
@@ -458,7 +470,9 @@ fn peer_view_update_sends_messages() {
.ok()
.flatten()
.expect("should be signed");
assert!(data.check_useful_or_unknown(&statement.clone().into()).is_ok());
assert!(data
.check_useful_or_unknown(&statement.clone().convert_payload().into())
.is_ok());
let noted = data.note_statement(statement);
assert_matches!(noted, NotedStatement::Fresh(_));