3535//! impolite to send messages about r+1 or later. "future-round" messages can
3636//! be dropped and ignored.
3737//!
38- //! It is impolite to send a neighbor packet which moves backwards in protocol state.
38+ //! It is impolite to send a neighbor packet which moves backwards or does not progress
39+ //! protocol state.
3940//!
4041//! This is beneficial if it conveys some progress in the protocol state of the peer.
4142//!
@@ -97,7 +98,7 @@ use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnbound
9798use sp_finality_grandpa:: AuthorityId ;
9899use sp_runtime:: traits:: { Block as BlockT , NumberFor , Zero } ;
99100
100- use super :: { benefit, cost, Round , SetId } ;
101+ use super :: { benefit, cost, Round , SetId , NEIGHBOR_REBROADCAST_PERIOD } ;
101102use crate :: { environment, CatchUp , CompactCommit , SignedMessage } ;
102103
103104use std:: {
@@ -148,14 +149,15 @@ enum Consider {
148149/// A view of protocol state.
149150#[ derive( Debug ) ]
150151struct View < N > {
151- round : Round , // the current round we are at.
152- set_id : SetId , // the current voter set id.
153- last_commit : Option < N > , // commit-finalized block height, if any.
152+ round : Round , // the current round we are at.
153+ set_id : SetId , // the current voter set id.
154+ last_commit : Option < N > , // commit-finalized block height, if any.
155+ last_update : Option < Instant > , // last time we heard from peer, used for spamming detection.
154156}
155157
156158impl < N > Default for View < N > {
157159 fn default ( ) -> Self {
158- View { round : Round ( 1 ) , set_id : SetId ( 0 ) , last_commit : None }
160+ View { round : Round ( 1 ) , set_id : SetId ( 0 ) , last_commit : None , last_update : None }
159161 }
160162}
161163
@@ -225,7 +227,12 @@ impl<N> LocalView<N> {
225227 /// Converts the local view to a `View` discarding round and set id
226228 /// information about the last commit.
227229 fn as_view ( & self ) -> View < & N > {
228- View { round : self . round , set_id : self . set_id , last_commit : self . last_commit_height ( ) }
230+ View {
231+ round : self . round ,
232+ set_id : self . set_id ,
233+ last_commit : self . last_commit_height ( ) ,
234+ last_update : None ,
235+ }
229236 }
230237
231238 /// Update the set ID. implies a reset to round 1.
@@ -417,6 +424,8 @@ pub(super) struct FullCatchUpMessage<Block: BlockT> {
417424pub ( super ) enum Misbehavior {
418425 // invalid neighbor message, considering the last one.
419426 InvalidViewChange ,
427+ // duplicate neighbor message.
428+ DuplicateNeighborMessage ,
420429 // could not decode neighbor message. bytes-length of the packet.
421430 UndecodablePacket ( i32 ) ,
422431 // Bad catch up message (invalid signatures).
@@ -438,6 +447,7 @@ impl Misbehavior {
438447
439448 match * self {
440449 InvalidViewChange => cost:: INVALID_VIEW_CHANGE ,
450+ DuplicateNeighborMessage => cost:: DUPLICATE_NEIGHBOR_MESSAGE ,
441451 UndecodablePacket ( bytes) => ReputationChange :: new (
442452 bytes. saturating_mul ( cost:: PER_UNDECODABLE_BYTE ) ,
443453 "Grandpa: Bad packet" ,
@@ -488,20 +498,22 @@ struct Peers<N> {
488498 second_stage_peers : HashSet < PeerId > ,
489499 /// The randomly picked set of `LUCKY_PEERS` light clients we'll gossip commit messages to.
490500 lucky_light_peers : HashSet < PeerId > ,
501+ /// Neighbor packet rebroadcast period --- we reduce the reputation of peers sending duplicate
502+ /// packets too often.
503+ neighbor_rebroadcast_period : Duration ,
491504}
492505
493- impl < N > Default for Peers < N > {
494- fn default ( ) -> Self {
506+ impl < N : Ord > Peers < N > {
507+ fn new ( neighbor_rebroadcast_period : Duration ) -> Self {
495508 Peers {
496509 inner : Default :: default ( ) ,
497510 first_stage_peers : Default :: default ( ) ,
498511 second_stage_peers : Default :: default ( ) ,
499512 lucky_light_peers : Default :: default ( ) ,
513+ neighbor_rebroadcast_period,
500514 }
501515 }
502- }
503516
504- impl < N : Ord > Peers < N > {
505517 fn new_peer ( & mut self , who : PeerId , role : ObservedRole ) {
506518 match role {
507519 ObservedRole :: Authority if self . first_stage_peers . len ( ) < LUCKY_PEERS => {
@@ -547,10 +559,23 @@ impl<N: Ord> Peers<N> {
547559 return Err ( Misbehavior :: InvalidViewChange )
548560 }
549561
562+ let now = Instant :: now ( ) ;
563+ let duplicate_packet = ( update. set_id , update. round , Some ( & update. commit_finalized_height ) ) ==
564+ ( peer. view . set_id , peer. view . round , peer. view . last_commit . as_ref ( ) ) ;
565+
566+ if duplicate_packet {
567+ if let Some ( last_update) = peer. view . last_update {
568+ if now < last_update + self . neighbor_rebroadcast_period / 2 {
569+ return Err ( Misbehavior :: DuplicateNeighborMessage )
570+ }
571+ }
572+ }
573+
550574 peer. view = View {
551575 round : update. round ,
552576 set_id : update. set_id ,
553577 last_commit : Some ( update. commit_finalized_height ) ,
578+ last_update : Some ( now) ,
554579 } ;
555580
556581 trace ! ( target: "afg" , "Peer {} updated view. Now at {:?}, {:?}" ,
@@ -748,7 +773,7 @@ impl<Block: BlockT> Inner<Block> {
748773
749774 Inner {
750775 local_view : None ,
751- peers : Peers :: default ( ) ,
776+ peers : Peers :: new ( NEIGHBOR_REBROADCAST_PERIOD ) ,
752777 live_topics : KeepTopics :: new ( ) ,
753778 next_rebroadcast : Instant :: now ( ) + REBROADCAST_AFTER ,
754779 authorities : Vec :: new ( ) ,
@@ -758,13 +783,16 @@ impl<Block: BlockT> Inner<Block> {
758783 }
759784 }
760785
761- /// Note a round in the current set has started.
786+ /// Note a round in the current set has started. Does nothing if the last
787+ /// call to the function was with the same `round`.
762788 fn note_round ( & mut self , round : Round ) -> MaybeMessage < Block > {
763789 {
764790 let local_view = match self . local_view {
765791 None => return None ,
766792 Some ( ref mut v) =>
767793 if v. round == round {
794+ // Do not send neighbor packets out if `round` has not changed ---
795+ // such behavior is punishable.
768796 return None
769797 } else {
770798 v
@@ -803,6 +831,8 @@ impl<Block: BlockT> Inner<Block> {
803831 ) ;
804832 self . authorities = authorities;
805833 }
834+ // Do not send neighbor packets out if the `set_id` has not changed ---
835+ // such behavior is punishable.
806836 return None
807837 } else {
808838 v
@@ -816,7 +846,9 @@ impl<Block: BlockT> Inner<Block> {
816846 self . multicast_neighbor_packet ( )
817847 }
818848
819- /// Note that we've imported a commit finalizing a given block.
849+ /// Note that we've imported a commit finalizing a given block. Does nothing if the last
850+ /// call to the function was with the same or higher `finalized` number.
851+ /// `set_id` & `round` are the ones the commit message is from.
820852 fn note_commit_finalized (
821853 & mut self ,
822854 round : Round ,
@@ -1357,6 +1389,8 @@ impl<Block: BlockT> GossipValidator<Block> {
13571389 }
13581390
13591391 /// Note that we've imported a commit finalizing a given block.
1392+ /// `set_id` & `round` are the ones the commit message is from and not necessarily
1393+ /// the latest set ID & round started.
13601394 pub ( super ) fn note_commit_finalized < F > (
13611395 & self ,
13621396 round : Round ,
@@ -1647,11 +1681,12 @@ pub(super) struct PeerReport {
16471681
16481682#[ cfg( test) ]
16491683mod tests {
1650- use super :: { environment:: SharedVoterSetState , * } ;
1684+ use super :: { super :: NEIGHBOR_REBROADCAST_PERIOD , environment:: SharedVoterSetState , * } ;
16511685 use crate :: communication;
16521686 use sc_network:: config:: Role ;
16531687 use sc_network_gossip:: Validator as GossipValidatorT ;
16541688 use sp_core:: { crypto:: UncheckedFrom , H256 } ;
1689+ use std:: time:: Instant ;
16551690 use substrate_test_runtime_client:: runtime:: { Block , Header } ;
16561691
16571692 // some random config (not really needed)
@@ -1684,7 +1719,12 @@ mod tests {
16841719
16851720 #[ test]
16861721 fn view_vote_rules ( ) {
1687- let view = View { round : Round ( 100 ) , set_id : SetId ( 1 ) , last_commit : Some ( 1000u64 ) } ;
1722+ let view = View {
1723+ round : Round ( 100 ) ,
1724+ set_id : SetId ( 1 ) ,
1725+ last_commit : Some ( 1000u64 ) ,
1726+ last_update : None ,
1727+ } ;
16881728
16891729 assert_eq ! ( view. consider_vote( Round ( 98 ) , SetId ( 1 ) ) , Consider :: RejectPast ) ;
16901730 assert_eq ! ( view. consider_vote( Round ( 1 ) , SetId ( 0 ) ) , Consider :: RejectPast ) ;
@@ -1701,7 +1741,12 @@ mod tests {
17011741
17021742 #[ test]
17031743 fn view_global_message_rules ( ) {
1704- let view = View { round : Round ( 100 ) , set_id : SetId ( 2 ) , last_commit : Some ( 1000u64 ) } ;
1744+ let view = View {
1745+ round : Round ( 100 ) ,
1746+ set_id : SetId ( 2 ) ,
1747+ last_commit : Some ( 1000u64 ) ,
1748+ last_update : None ,
1749+ } ;
17051750
17061751 assert_eq ! ( view. consider_global( SetId ( 3 ) , 1 ) , Consider :: RejectFuture ) ;
17071752 assert_eq ! ( view. consider_global( SetId ( 3 ) , 1000 ) , Consider :: RejectFuture ) ;
@@ -1719,7 +1764,7 @@ mod tests {
17191764
17201765 #[ test]
17211766 fn unknown_peer_cannot_be_updated ( ) {
1722- let mut peers = Peers :: default ( ) ;
1767+ let mut peers = Peers :: new ( NEIGHBOR_REBROADCAST_PERIOD ) ;
17231768 let id = PeerId :: random ( ) ;
17241769
17251770 let update =
@@ -1750,27 +1795,35 @@ mod tests {
17501795 let update4 =
17511796 NeighborPacket { round : Round ( 3 ) , set_id : SetId ( 11 ) , commit_finalized_height : 80 } ;
17521797
1753- let mut peers = Peers :: default ( ) ;
1798+ // Use shorter rebroadcast period to safely roll the clock back in the last test
1799+ // and don't hit the system boot time on systems with unsigned time.
1800+ const SHORT_NEIGHBOR_REBROADCAST_PERIOD : Duration = Duration :: from_secs ( 1 ) ;
1801+ let mut peers = Peers :: new ( SHORT_NEIGHBOR_REBROADCAST_PERIOD ) ;
17541802 let id = PeerId :: random ( ) ;
17551803
17561804 peers. new_peer ( id, ObservedRole :: Authority ) ;
17571805
1758- let mut check_update = move | update : NeighborPacket < _ > | {
1806+ let check_update = | peers : & mut Peers < _ > , update : NeighborPacket < _ > | {
17591807 let view = peers. update_peer_state ( & id, update. clone ( ) ) . unwrap ( ) . unwrap ( ) ;
17601808 assert_eq ! ( view. round, update. round) ;
17611809 assert_eq ! ( view. set_id, update. set_id) ;
17621810 assert_eq ! ( view. last_commit, Some ( update. commit_finalized_height) ) ;
17631811 } ;
17641812
1765- check_update ( update1) ;
1766- check_update ( update2) ;
1767- check_update ( update3) ;
1768- check_update ( update4) ;
1813+ check_update ( & mut peers, update1) ;
1814+ check_update ( & mut peers, update2) ;
1815+ check_update ( & mut peers, update3) ;
1816+ check_update ( & mut peers, update4. clone ( ) ) ;
1817+
1818+ // Allow duplicate neighbor packets if enough time has passed.
1819+ peers. inner . get_mut ( & id) . unwrap ( ) . view . last_update =
1820+ Some ( Instant :: now ( ) - SHORT_NEIGHBOR_REBROADCAST_PERIOD ) ;
1821+ check_update ( & mut peers, update4) ;
17691822 }
17701823
17711824 #[ test]
17721825 fn invalid_view_change ( ) {
1773- let mut peers = Peers :: default ( ) ;
1826+ let mut peers = Peers :: new ( NEIGHBOR_REBROADCAST_PERIOD ) ;
17741827
17751828 let id = PeerId :: random ( ) ;
17761829 peers. new_peer ( id, ObservedRole :: Authority ) ;
@@ -1783,29 +1836,41 @@ mod tests {
17831836 . unwrap ( )
17841837 . unwrap ( ) ;
17851838
1786- let mut check_update = move |update : NeighborPacket < _ > | {
1839+ let mut check_update = move |update : NeighborPacket < _ > , misbehavior | {
17871840 let err = peers. update_peer_state ( & id, update. clone ( ) ) . unwrap_err ( ) ;
1788- assert_eq ! ( err, Misbehavior :: InvalidViewChange ) ;
1841+ assert_eq ! ( err, misbehavior ) ;
17891842 } ;
17901843
17911844 // round moves backwards.
1792- check_update ( NeighborPacket {
1793- round : Round ( 9 ) ,
1794- set_id : SetId ( 10 ) ,
1795- commit_finalized_height : 10 ,
1796- } ) ;
1797- // commit finalized height moves backwards.
1798- check_update ( NeighborPacket {
1799- round : Round ( 10 ) ,
1800- set_id : SetId ( 10 ) ,
1801- commit_finalized_height : 9 ,
1802- } ) ;
1845+ check_update (
1846+ NeighborPacket { round : Round ( 9 ) , set_id : SetId ( 10 ) , commit_finalized_height : 10 } ,
1847+ Misbehavior :: InvalidViewChange ,
1848+ ) ;
18031849 // set ID moves backwards.
1804- check_update ( NeighborPacket {
1805- round : Round ( 10 ) ,
1806- set_id : SetId ( 9 ) ,
1807- commit_finalized_height : 10 ,
1808- } ) ;
1850+ check_update (
1851+ NeighborPacket { round : Round ( 10 ) , set_id : SetId ( 9 ) , commit_finalized_height : 10 } ,
1852+ Misbehavior :: InvalidViewChange ,
1853+ ) ;
1854+ // commit finalized height moves backwards.
1855+ check_update (
1856+ NeighborPacket { round : Round ( 10 ) , set_id : SetId ( 10 ) , commit_finalized_height : 9 } ,
1857+ Misbehavior :: InvalidViewChange ,
1858+ ) ;
1859+ // duplicate packet without grace period.
1860+ check_update (
1861+ NeighborPacket { round : Round ( 10 ) , set_id : SetId ( 10 ) , commit_finalized_height : 10 } ,
1862+ Misbehavior :: DuplicateNeighborMessage ,
1863+ ) ;
1864+ // commit finalized height moves backwards while round moves forward.
1865+ check_update (
1866+ NeighborPacket { round : Round ( 11 ) , set_id : SetId ( 10 ) , commit_finalized_height : 9 } ,
1867+ Misbehavior :: InvalidViewChange ,
1868+ ) ;
1869+ // commit finalized height moves backwards while set ID moves forward.
1870+ check_update (
1871+ NeighborPacket { round : Round ( 10 ) , set_id : SetId ( 11 ) , commit_finalized_height : 9 } ,
1872+ Misbehavior :: InvalidViewChange ,
1873+ ) ;
18091874 }
18101875
18111876 #[ test]
0 commit comments