diff --git a/lib/node/net_task.rs b/lib/node/net_task.rs index 9b61f78..2a1888d 100644 --- a/lib/node/net_task.rs +++ b/lib/node/net_task.rs @@ -256,6 +256,11 @@ fn disconnect_tip_( Ok(()) } +// peer-caused state errors must not be fatal +fn is_fatal_reorg_error(err: &Error) -> bool { + !matches!(err, Error::State(_)) +} + /// Re-org to the specified tip, if it is better than the current tip. /// The new tip block and all ancestor blocks must exist in the node's archive. /// A result of `Ok(true)` indicates a successful re-org. @@ -1022,8 +1027,8 @@ impl NetTask { } } } - MailboxItem::NewTipReady(new_tip, _addr, resp_tx) => { - let reorg_applied = task::block_in_place(|| { + MailboxItem::NewTipReady(new_tip, addr, resp_tx) => { + let reorg_result = task::block_in_place(|| { reorg_to_tip( &self.ctxt.env, &self.ctxt.archive, @@ -1033,7 +1038,26 @@ impl NetTask { &self.ctxt.zmq_pub_handler, new_tip, ) - })?; + }); + let reorg_applied = match reorg_result { + Ok(applied) => applied, + Err(err) if is_fatal_reorg_error(&err) => { + return Err(err); + } + // invalid block: drop the peer, keep running + Err(err) => { + tracing::warn!( + ?new_tip, + ?addr, + err = format!("{err:#}"), + "rejecting invalid tip from peer" + ); + if let Some(addr) = addr { + self.ctxt.net.remove_active_peer(addr); + } + false + } + }; if let Some(resp_tx) = resp_tx { let () = resp_tx .send(reorg_applied) @@ -1259,3 +1283,20 @@ impl Drop for NetTaskHandle { } } } + +#[cfg(test)] +mod tests { + use super::{Error, is_fatal_reorg_error}; + use crate::state; + + #[test] + fn invalid_peer_block_is_not_fatal() { + let err = Error::State(Box::new(state::Error::NotEnoughValueIn)); + assert!(!is_fatal_reorg_error(&err)); + } + + #[test] + fn infrastructure_error_is_fatal() { + assert!(is_fatal_reorg_error(&Error::PeerInfoRxClosed)); + } +}