From 8961628e7c4597f0c76417fd8bd8995eeaf6e33d Mon Sep 17 00:00:00 2001
From: Andronik Ordian <write@reusable.software>
Date: Fri, 9 Apr 2021 19:31:34 +0200
Subject: [PATCH] bitfield-dist: check signatures once (#2868)

---
 .../network/bitfield-distribution/src/lib.rs  | 101 ++++++++++++------
 1 file changed, 71 insertions(+), 30 deletions(-)

diff --git a/polkadot/node/network/bitfield-distribution/src/lib.rs b/polkadot/node/network/bitfield-distribution/src/lib.rs
index e53759b1eb2..8437fc41226 100644
--- a/polkadot/node/network/bitfield-distribution/src/lib.rs
+++ b/polkadot/node/network/bitfield-distribution/src/lib.rs
@@ -462,32 +462,35 @@ where
 		return;
 	};
 
+	let one_per_validator = &mut (job_data.one_per_validator);
+
+	// only relay_message a message of a validator once
+	if let Some(old_message) = one_per_validator.get(&validator) {
+		tracing::trace!(
+			target: LOG_TARGET,
+			validator_index,
+			"already received a message for validator",
+		);
+		if old_message.signed_availability == message.signed_availability {
+			modify_reputation(ctx, origin, BENEFIT_VALID_MESSAGE).await;
+		}
+		return;
+	}
 	if message
 		.signed_availability
 		.check_signature(&signing_context, &validator)
-		.is_ok()
+		.is_err()
 	{
-		metrics.on_bitfield_received();
-		let one_per_validator = &mut (job_data.one_per_validator);
+		modify_reputation(ctx, origin, COST_SIGNATURE_INVALID).await;
+		return;
+	}
 
-		// only relay_message a message of a validator once
-		if one_per_validator.get(&validator).is_some() {
-			tracing::trace!(
-				target: LOG_TARGET,
-				validator_index,
-				"already received a message for validator",
-			);
-			modify_reputation(ctx, origin, BENEFIT_VALID_MESSAGE).await;
-			return;
-		}
-		one_per_validator.insert(validator.clone(), message.clone());
+	metrics.on_bitfield_received();
+	one_per_validator.insert(validator.clone(), message.clone());
 
-		relay_message(ctx, job_data, &mut state.peer_views, validator, message).await;
+	relay_message(ctx, job_data, &mut state.peer_views, validator, message).await;
 
-		modify_reputation(ctx, origin, BENEFIT_VALID_MESSAGE_FIRST).await
-	} else {
-		modify_reputation(ctx, origin, COST_SIGNATURE_INVALID).await
-	}
+	modify_reputation(ctx, origin, BENEFIT_VALID_MESSAGE_FIRST).await
 }
 
 /// Deal with network bridge updates and track what needs to be tracked
@@ -926,22 +929,47 @@ mod test {
 		// another validator not part of the validatorset
 		let keystore : SyncCryptoStorePtr = Arc::new(KeyStore::new());
 		let malicious = SyncCryptoStore::sr25519_generate_new(&*keystore, ValidatorId::ID, None)
-								.expect("Malicious key created");
-		let validator = SyncCryptoStore::sr25519_generate_new(&*keystore, ValidatorId::ID, None)
-								.expect("Malicious key created");
+			.expect("Malicious key created");
+		let validator_0 = SyncCryptoStore::sr25519_generate_new(&*keystore, ValidatorId::ID, None)
+			.expect("key created");
+		let validator_1 = SyncCryptoStore::sr25519_generate_new(&*keystore, ValidatorId::ID, None)
+			.expect("key created");
 
 		let payload = AvailabilityBitfield(bitvec![bitvec::order::Lsb0, u8; 1u8; 32]);
-		let signed = executor::block_on(Signed::<AvailabilityBitfield>::sign(
+		let invalid_signed = executor::block_on(Signed::<AvailabilityBitfield>::sign(
 			&keystore,
-			payload,
+			payload.clone(),
 			&signing_context,
 			ValidatorIndex(0),
 			&malicious.into(),
 		)).ok().flatten().expect("should be signed");
+		let invalid_signed_2 = executor::block_on(Signed::<AvailabilityBitfield>::sign(
+			&keystore,
+			payload.clone(),
+			&signing_context,
+			ValidatorIndex(1),
+			&malicious.into(),
+		)).ok().flatten().expect("should be signed");
 
-		let msg = BitfieldGossipMessage {
+		let valid_signed = executor::block_on(Signed::<AvailabilityBitfield>::sign(
+			&keystore,
+			payload,
+			&signing_context,
+			ValidatorIndex(0),
+			&validator_0.into(),
+		)).ok().flatten().expect("should be signed");
+
+		let invalid_msg = BitfieldGossipMessage {
 			relay_parent: hash_a.clone(),
-			signed_availability: signed.clone(),
+			signed_availability: invalid_signed.clone(),
+		};
+		let invalid_msg_2 = BitfieldGossipMessage {
+			relay_parent: hash_a.clone(),
+			signed_availability: invalid_signed_2.clone(),
+		};
+		let valid_msg = BitfieldGossipMessage {
+			relay_parent: hash_a.clone(),
+			signed_availability: valid_signed.clone(),
 		};
 
 		let pool = sp_core::testing::TaskExecutor::new();
@@ -949,21 +977,34 @@ mod test {
 			make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
 
 		let mut state = prewarmed_state(
-			validator.into(),
+			validator_0.into(),
 			signing_context.clone(),
-			msg.clone(),
+			valid_msg,
 			vec![peer_b.clone()],
 		);
+		state.per_relay_parent.get_mut(&hash_a)
+			.unwrap()
+			.validator_set
+			.push(validator_1.into());
 
 		executor::block_on(async move {
 			launch!(handle_network_msg(
 				&mut ctx,
 				&mut state,
 				&Default::default(),
-				NetworkBridgeEvent::PeerMessage(peer_b.clone(), msg.into_network_message()),
+				NetworkBridgeEvent::PeerMessage(peer_b.clone(), invalid_msg.into_network_message()),
 			));
 
-			// reputation change due to invalid validator index
+			// reputation doesn't change due to one_job_per_validator check
+			assert!(handle.recv().timeout(Duration::from_millis(10)).await.is_none());
+
+			launch!(handle_network_msg(
+				&mut ctx,
+				&mut state,
+				&Default::default(),
+				NetworkBridgeEvent::PeerMessage(peer_b.clone(), invalid_msg_2.into_network_message()),
+			));
+			// reputation change due to invalid signature
 			assert_matches!(
 				handle.recv().await,
 				AllMessages::NetworkBridge(
-- 
GitLab