diff --git a/network/src/lib.rs b/network/src/lib.rs index baef7c522b20..ac98ec32614e 100644 --- a/network/src/lib.rs +++ b/network/src/lib.rs @@ -80,6 +80,17 @@ mod cost { pub(super) const UNKNOWN_PEER: i32 = -50; pub(super) const COLLATOR_ALREADY_KNOWN: i32 = -100; pub(super) const BAD_COLLATION: i32 = -1000; + pub(super) const BAD_POV_BLOCK: i32 = -1000; +} + +mod benefit { + pub(super) const EXPECTED_MESSAGE: i32 = 20; + pub(super) const VALID_FORMAT: i32 = 20; + + pub(super) const KNOWN_PEER: i32 = 5; + pub(super) const NEW_COLLATOR: i32 = 10; + pub(super) const GOOD_COLLATION: i32 = 100; + pub(super) const GOOD_POV_BLOCK: i32 = 100; } type FullStatus = GenericFullStatus; @@ -382,6 +393,7 @@ impl PolkadotProtocol { ctx.report_peer(who, cost::UNEXPECTED_MESSAGE); return; } + ctx.report_peer(who.clone(), benefit::EXPECTED_MESSAGE); let local_collations = &mut self.local_collations; let new_collations = match info.validator_keys.insert(key.clone()) { @@ -418,10 +430,21 @@ impl PolkadotProtocol { ) { match self.in_flight.remove(&(req_id, who.clone())) { Some(mut req) => { - if let Some(pov_block) = pov_block { - match req.process_response(pov_block) { - Ok(()) => return, - Err(r) => { req = r; } + match pov_block { + Some(pov_block) => { + match req.process_response(pov_block) { + Ok(()) => { + ctx.report_peer(who, benefit::GOOD_POV_BLOCK); + return; + } + Err(r) => { + ctx.report_peer(who, cost::BAD_POV_BLOCK); + req = r; + } + } + }, + None => { + ctx.report_peer(who, benefit::EXPECTED_MESSAGE); } } @@ -493,6 +516,7 @@ impl Specialization for PolkadotProtocol { ctx.report_peer(who, cost::COLLATOR_ALREADY_KNOWN); return } + ctx.report_peer(who.clone(), benefit::NEW_COLLATOR); let collator_role = self.collators.on_new_collator(acc_id.clone(), para_id.clone()); @@ -566,7 +590,10 @@ impl Specialization for PolkadotProtocol { match message.take() { Some(generic_message::Message::ChainSpecific(raw)) => { match Message::decode(&mut raw.as_slice()) { - Some(msg) => self.on_polkadot_message(ctx, who, msg), + Some(msg) => { + ctx.report_peer(who.clone(), benefit::VALID_FORMAT); + self.on_polkadot_message(ctx, who, msg) + }, None => { trace!(target: "p_net", "Bad message from {}", who); ctx.report_peer(who, cost::INVALID_FORMAT); @@ -614,16 +641,21 @@ impl PolkadotProtocol { match self.peers.get(&from) { None => ctx.report_peer(from, cost::UNKNOWN_PEER), - Some(peer_info) => match peer_info.collating_for { - None => ctx.report_peer(from, cost::UNEXPECTED_MESSAGE), - Some((ref acc_id, ref para_id)) => { - let structurally_valid = para_id == &collation_para && acc_id == &collated_acc; - if structurally_valid && collation.receipt.check_signature().is_ok() { - debug!(target: "p_net", "Received collation for parachain {:?} from peer {}", para_id, from); - self.collators.on_collation(acc_id.clone(), relay_parent, collation) - } else { - ctx.report_peer(from, cost::INVALID_FORMAT) - }; + Some(peer_info) => { + ctx.report_peer(from.clone(), benefit::KNOWN_PEER); + match peer_info.collating_for { + None => ctx.report_peer(from, cost::UNEXPECTED_MESSAGE), + Some((ref acc_id, ref para_id)) => { + ctx.report_peer(from.clone(), benefit::EXPECTED_MESSAGE); + let structurally_valid = para_id == &collation_para && acc_id == &collated_acc; + if structurally_valid && collation.receipt.check_signature().is_ok() { + debug!(target: "p_net", "Received collation for parachain {:?} from peer {}", para_id, from); + ctx.report_peer(from, benefit::GOOD_COLLATION); + self.collators.on_collation(acc_id.clone(), relay_parent, collation) + } else { + ctx.report_peer(from, cost::INVALID_FORMAT) + }; + } } }, } diff --git a/network/src/tests/mod.rs b/network/src/tests/mod.rs index 892dca418497..0304c63a10ae 100644 --- a/network/src/tests/mod.rs +++ b/network/src/tests/mod.rs @@ -16,6 +16,7 @@ //! Tests for polkadot and validation network. +use std::collections::HashMap; use super::{PolkadotProtocol, Status, Message, FullStatus}; use validation::SessionParams; @@ -41,6 +42,7 @@ mod validation; struct TestContext { disabled: Vec, disconnected: Vec, + reputations: HashMap, messages: Vec<(PeerId, Vec)>, } @@ -50,6 +52,9 @@ impl Context for TestContext { } fn report_peer(&mut self, peer: PeerId, reputation: i32) { + let reputation = self.reputations.get(&peer).map_or(reputation, |v| v + reputation); + self.reputations.insert(peer.clone(), reputation); + match reputation { i if i < -100 => self.disabled.push(peer), i if i < 0 => self.disconnected.push(peer), @@ -306,6 +311,22 @@ fn remove_bad_collator() { } } +#[test] +fn kick_collator() { + let mut protocol = PolkadotProtocol::new(None); + + let who = PeerId::random(); + let collator_id: CollatorId = [2; 32].unchecked_into(); + + let mut ctx = TestContext::default(); + let status = Status { collating_for: Some((collator_id.clone(), 5.into())) }; + protocol.on_connect(&mut ctx, who.clone(), make_status(&status, Roles::NONE)); + assert!(!ctx.disconnected.contains(&who)); + + protocol.on_connect(&mut ctx, who.clone(), make_status(&status, Roles::NONE)); + assert!(ctx.disconnected.contains(&who)); +} + #[test] fn many_session_keys() { let mut protocol = PolkadotProtocol::new(None);