reth_eth_wire_types/
disconnect_reason.rs1use alloy_primitives::bytes::{Buf, BufMut};
4use alloy_rlp::{Decodable, Encodable, Header};
5use derive_more::Display;
6use reth_codecs_derive::add_arbitrary_tests;
7use thiserror::Error;
8
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Display)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
13#[add_arbitrary_tests(rlp)]
14pub enum DisconnectReason {
15 #[default]
17 #[display("disconnect requested")]
18 DisconnectRequested = 0x00,
19 #[display("TCP sub-system error")]
21 TcpSubsystemError = 0x01,
22 #[display("breach of protocol, e.g. a malformed message, bad RLP, etc.")]
24 ProtocolBreach = 0x02,
25 #[display("useless peer")]
27 UselessPeer = 0x03,
28 #[display("too many peers")]
30 TooManyPeers = 0x04,
31 #[display("already connected")]
33 AlreadyConnected = 0x05,
34 #[display("incompatible P2P protocol version")]
36 IncompatibleP2PProtocolVersion = 0x06,
37 #[display("null node identity received - this is automatically invalid")]
39 NullNodeIdentity = 0x07,
40 #[display("client quitting")]
42 ClientQuitting = 0x08,
43 #[display("unexpected identity in handshake")]
45 UnexpectedHandshakeIdentity = 0x09,
46 #[display("identity is the same as this node (i.e. connected to itself)")]
48 ConnectedToSelf = 0x0a,
49 #[display("ping timeout")]
51 PingTimeout = 0x0b,
52 #[display("some other reason specific to a subprotocol")]
54 SubprotocolSpecific = 0x10,
55}
56
57impl TryFrom<u8> for DisconnectReason {
58 type Error = UnknownDisconnectReason;
61
62 fn try_from(value: u8) -> Result<Self, Self::Error> {
63 match value {
64 0x00 => Ok(Self::DisconnectRequested),
65 0x01 => Ok(Self::TcpSubsystemError),
66 0x02 => Ok(Self::ProtocolBreach),
67 0x03 => Ok(Self::UselessPeer),
68 0x04 => Ok(Self::TooManyPeers),
69 0x05 => Ok(Self::AlreadyConnected),
70 0x06 => Ok(Self::IncompatibleP2PProtocolVersion),
71 0x07 => Ok(Self::NullNodeIdentity),
72 0x08 => Ok(Self::ClientQuitting),
73 0x09 => Ok(Self::UnexpectedHandshakeIdentity),
74 0x0a => Ok(Self::ConnectedToSelf),
75 0x0b => Ok(Self::PingTimeout),
76 0x10 => Ok(Self::SubprotocolSpecific),
77 _ => Err(UnknownDisconnectReason(value)),
78 }
79 }
80}
81
82impl Encodable for DisconnectReason {
83 fn encode(&self, out: &mut dyn BufMut) {
86 alloy_rlp::encode_list(&[*self as u8], out);
87 }
88 fn length(&self) -> usize {
89 alloy_rlp::list_length(&[*self as u8])
90 }
91}
92
93impl Decodable for DisconnectReason {
94 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
97 if buf.is_empty() {
98 return Err(alloy_rlp::Error::InputTooShort)
99 } else if buf.len() > 2 {
100 return Err(alloy_rlp::Error::Overflow)
101 }
102
103 if buf.len() > 1 {
104 let header = Header::decode(buf)?;
107
108 if !header.list {
109 return Err(alloy_rlp::Error::UnexpectedString)
110 }
111
112 if header.payload_length != 1 {
113 return Err(alloy_rlp::Error::ListLengthMismatch {
114 expected: 1,
115 got: header.payload_length,
116 })
117 }
118 }
119
120 if buf[0] == 0x00 {
123 buf.advance(1);
124 Ok(Self::DisconnectRequested)
125 } else {
126 Self::try_from(u8::decode(buf)?)
127 .map_err(|_| alloy_rlp::Error::Custom("unknown disconnect reason"))
128 }
129 }
130}
131
132#[derive(Debug, Clone, Error)]
134#[error("unknown disconnect reason: {0}")]
135pub struct UnknownDisconnectReason(u8);