Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions lib/net/peer/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,19 @@ pub(in crate::net::peer) mod connection {
}
}

impl Receive {
/// True when the peer sent another network's magic bytes.
pub fn is_bad_magic(&self) -> bool {
matches!(self, Self::BadMagic(_))
}
}

impl ReceiveRequest {
pub fn is_bad_magic(&self) -> bool {
self.0.is_bad_magic()
}
}

impl Recoverable for ReceiveRequest {
fn may_reconnect(&self) -> bool {
self.0.may_reconnect()
Expand All @@ -232,6 +245,12 @@ pub(in crate::net::peer) mod connection {
#[repr(transparent)]
pub struct ReceiveResponse(#[source] Receive);

impl ReceiveResponse {
pub fn is_bad_magic(&self) -> bool {
self.0.is_bad_magic()
}
}

impl<E> From<E> for ReceiveResponse
where
Receive: From<E>,
Expand Down Expand Up @@ -526,6 +545,15 @@ pub mod mailbox {
SendResponse(#[from] super::connection::SendResponse),
}

impl Error {
pub fn is_bad_magic(&self) -> bool {
match self {
Self::ReceiveRequest(err) => err.is_bad_magic(),
_ => false,
}
}
}

impl Recoverable for Error {
fn may_reconnect(&self) -> bool {
match self {
Expand Down Expand Up @@ -587,3 +615,57 @@ impl Recoverable for Error {
}
}
}

impl Error {
/// True when the peer answered with another network's magic bytes. Such a
/// peer runs a different chain, so it never becomes useful.
pub fn is_bad_magic(&self) -> bool {
match self {
Self::Mailbox(err) => err.is_bad_magic(),
Self::ReceiveResponse(err) => err.is_bad_magic(),
_ => false,
}
}
}

#[cfg(test)]
mod test {
use super::{Error, connection, mailbox};
use crate::net::peer::message;

const FOREIGN_MAGIC: message::MagicBytes = [0x85, 0x18, 0x95, 0x01];

// A peer that answers a request with another network's magic must read as
// bad magic through every wrapper the error passes.
#[test]
fn bad_magic_survives_the_request_path() {
let inner = connection::Receive::BadMagic(FOREIGN_MAGIC);
let err = Error::Mailbox(mailbox::Error::ReceiveRequest(
connection::ReceiveRequest::from(inner),
));
assert!(err.is_bad_magic());
}

#[test]
fn bad_magic_survives_the_response_path() {
let inner = connection::Receive::BadMagic(FOREIGN_MAGIC);
let err =
Error::ReceiveResponse(connection::ReceiveResponse::from(inner));
assert!(err.is_bad_magic());
}

// A timeout says nothing about the peer's network, so the node keeps it.
#[test]
fn a_timeout_is_not_bad_magic() {
let err = Error::ReceiveResponse(connection::ReceiveResponse::from(
connection::Receive::Timeout,
));
assert!(!err.is_bad_magic());
}

#[test]
fn a_heartbeat_timeout_is_not_bad_magic() {
let err = Error::Mailbox(mailbox::Error::HeartbeatTimeout);
assert!(!err.is_bad_magic());
}
}
24 changes: 24 additions & 0 deletions lib/node/net_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,30 @@ impl NetTask {
},
);
let () = self.ctxt.net.remove_active_peer(addr);
// A peer on another network never becomes useful,
// so it must not survive into the next start.
if err.is_bad_magic() {
let peer_address = resolved_peer_addr
.as_peer_address()
.to_owned();
let mut rwtxn = self
.ctxt
.env
.write_txn()
.map_err(EnvError::from)?;
let forgotten = self
.ctxt
.net
.forget_peer(&mut rwtxn, &peer_address)?;
rwtxn.commit().map_err(RwTxnError::from)?;
if forgotten {
tracing::warn!(
%peer_address,
"forgot peer: it runs another network"
);
}
continue;
}
let Some(received_msg_successfully) =
received_msg_successfully
else {
Expand Down
Loading