@@ -118,7 +118,7 @@ pub struct BlockRequestsSummary {
118118}
119119
120120#[ derive( thiserror:: Error , Debug ) ]
121- pub enum InsertBlockResponseError {
121+ pub enum InsertBlockResponseError < N : Network > {
122122 #[ error( "Empty block response" ) ]
123123 EmptyBlockResponse ,
124124 #[ error( "The peer did not send a consensus version" ) ]
@@ -127,10 +127,36 @@ pub enum InsertBlockResponseError {
127127 "The peer's consensus version for height {last_height} does not match ours: expected {expected_version}, got {peer_version}"
128128 ) ]
129129 ConsensusVersionMismatch { peer_version : ConsensusVersion , expected_version : ConsensusVersion , last_height : u32 } ,
130+ #[ error( "Block Sync already advanced to block {height}" ) ]
131+ BlockSyncAlreadyAdvanced { height : u32 } ,
132+ #[ error( "No such request for height {height}" ) ]
133+ NoSuchRequest { height : u32 } ,
134+ #[ error( "Invalid block hash for height {height} from '{peer_ip}'" ) ]
135+ InvalidBlockHash { height : u32 , peer_ip : SocketAddr } ,
136+ #[ error(
137+ "The previous block hash in candidate block {height} from '{peer_ip}' is incorrect: expected {expected}, but got {actual}"
138+ ) ]
139+ InvalidPreviousBlockHash { height : u32 , peer_ip : SocketAddr , expected : N :: BlockHash , actual : N :: BlockHash } ,
140+ #[ error( "Candidate block {height} from '{peer_ip}' is malformed" ) ]
141+ MalformedBlock { height : u32 , peer_ip : SocketAddr } ,
142+ #[ error( "The sync pool did not request block {height} from '{peer_ip}'" ) ]
143+ WrongSyncPeer { height : u32 , peer_ip : SocketAddr } ,
130144 #[ error( "{}" , flatten_error( . 0 ) ) ]
131145 Other ( #[ from] anyhow:: Error ) ,
132146}
133147
148+ impl < N : Network > InsertBlockResponseError < N > {
149+ /// Returns `true` if the error does not indicate malicious or faulty behavior.
150+ pub fn is_benign ( & self ) -> bool {
151+ matches ! ( self , Self :: NoSuchRequest { .. } | Self :: BlockSyncAlreadyAdvanced { .. } )
152+ }
153+
154+ // Returns true if the error is about an invalid consensus version.
155+ pub fn is_invalid_consensus_version ( & self ) -> bool {
156+ matches ! ( self , Self :: ConsensusVersionMismatch { .. } | Self :: NoConsensusVersion )
157+ }
158+ }
159+
134160impl < N : Network > OutstandingRequest < N > {
135161 /// Get a reference to the IPs of peers that have not responded to the request (yet).
136162 fn sync_ips ( & self ) -> & IndexSet < SocketAddr > {
@@ -473,7 +499,7 @@ impl<N: Network> BlockSync<N> {
473499 peer_ip : SocketAddr ,
474500 blocks : Vec < Block < N > > ,
475501 latest_consensus_version : Option < ConsensusVersion > ,
476- ) -> Result < ( ) , InsertBlockResponseError > {
502+ ) -> Result < ( ) , InsertBlockResponseError < N > > {
477503 // Attempt to insert the block responses, and break if we encounter an error.
478504 let result = ' outer: {
479505 let Some ( last_height) = blocks. as_slice ( ) . last ( ) . map ( |b| b. height ( ) ) else {
@@ -500,9 +526,7 @@ impl<N: Network> BlockSync<N> {
500526
501527 // Insert the candidate blocks into the sync pool.
502528 for block in blocks {
503- if let Err ( error) = self . insert_block_response ( peer_ip, block) {
504- break ' outer Err ( error. into ( ) ) ;
505- }
529+ self . insert_block_response ( peer_ip, block) ?;
506530 }
507531
508532 Ok ( ( ) )
@@ -916,35 +940,42 @@ impl<N: Network> BlockSync<N> {
916940
917941 /// Inserts the given block response, after checking that the request exists and the response is well-formed.
918942 /// On success, this function removes the peer IP from the request sync peers and inserts the response.
919- fn insert_block_response ( & self , peer_ip : SocketAddr , block : Block < N > ) -> Result < ( ) > {
943+ fn insert_block_response ( & self , peer_ip : SocketAddr , block : Block < N > ) -> Result < ( ) , InsertBlockResponseError < N > > {
920944 // Retrieve the block height.
921945 let height = block. height ( ) ;
922946 let mut requests = self . requests . write ( ) ;
923947
924948 if self . ledger . contains_block_height ( height) {
925- bail ! ( "The sync request was removed because we already advanced" ) ;
949+ return Err ( InsertBlockResponseError :: BlockSyncAlreadyAdvanced { height } ) ;
926950 }
927951
928- let Some ( entry) = requests. get_mut ( & height) else { bail ! ( "The sync pool did not request block {height}" ) } ;
952+ let Some ( entry) = requests. get_mut ( & height) else {
953+ return Err ( InsertBlockResponseError :: NoSuchRequest { height } ) ;
954+ } ;
929955
930956 // Retrieve the request entry for the candidate block.
931957 let ( expected_hash, expected_previous_hash, sync_ips) = & entry. request ;
932958
933959 // Ensure the candidate block hash matches the expected hash.
934960 if let Some ( expected_hash) = expected_hash {
935961 if block. hash ( ) != * expected_hash {
936- bail ! ( "The block hash for candidate block { height} from '{ peer_ip}' is incorrect" )
962+ return Err ( InsertBlockResponseError :: InvalidBlockHash { height, peer_ip } ) ;
937963 }
938964 }
939965 // Ensure the previous block hash matches if it exists.
940966 if let Some ( expected_previous_hash) = expected_previous_hash {
941967 if block. previous_hash ( ) != * expected_previous_hash {
942- bail ! ( "The previous block hash in candidate block {height} from '{peer_ip}' is incorrect" )
968+ return Err ( InsertBlockResponseError :: InvalidPreviousBlockHash {
969+ height,
970+ peer_ip,
971+ expected : * expected_previous_hash,
972+ actual : block. previous_hash ( ) ,
973+ } ) ;
943974 }
944975 }
945976 // Ensure the sync pool requested this block from the given peer.
946977 if !sync_ips. contains ( & peer_ip) {
947- bail ! ( "The sync pool did not request block { height} from '{ peer_ip}'" )
978+ return Err ( InsertBlockResponseError :: WrongSyncPeer { height, peer_ip } ) ;
948979 }
949980
950981 // Remove the peer IP from the request entry.
@@ -953,7 +984,7 @@ impl<N: Network> BlockSync<N> {
953984 if let Some ( existing_block) = & entry. response {
954985 // If the candidate block was already present, ensure it is the same block.
955986 if block != * existing_block {
956- bail ! ( "Candidate block { height} from '{ peer_ip}' is malformed" ) ;
987+ return Err ( InsertBlockResponseError :: MalformedBlock { height, peer_ip } ) ;
957988 }
958989 } else {
959990 entry. response = Some ( block. clone ( ) ) ;
0 commit comments