mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-09 23:38:10 -05:00
chore: add status enum for handshake to support status69 decoding (#15543)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
extern crate alloc;
|
||||
|
||||
mod status;
|
||||
pub use status::{Status, StatusBuilder, StatusEth69};
|
||||
pub use status::{Status, StatusBuilder, StatusEth69, StatusMessage};
|
||||
|
||||
pub mod version;
|
||||
pub use version::{EthVersion, ProtocolVersion};
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
use super::{
|
||||
broadcast::NewBlockHashes, BlockBodies, BlockHeaders, GetBlockBodies, GetBlockHeaders,
|
||||
GetNodeData, GetPooledTransactions, GetReceipts, NewBlock, NewPooledTransactionHashes66,
|
||||
NewPooledTransactionHashes68, NodeData, PooledTransactions, Receipts, Status, Transactions,
|
||||
NewPooledTransactionHashes68, NodeData, PooledTransactions, Receipts, Status, StatusEth69,
|
||||
Transactions,
|
||||
};
|
||||
use crate::{
|
||||
EthNetworkPrimitives, EthVersion, NetworkPrimitives, RawCapabilityMessage, SharedTransactions,
|
||||
status::StatusMessage, EthNetworkPrimitives, EthVersion, NetworkPrimitives,
|
||||
RawCapabilityMessage, SharedTransactions,
|
||||
};
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use alloy_primitives::{
|
||||
@@ -56,8 +58,14 @@ impl<N: NetworkPrimitives> ProtocolMessage<N> {
|
||||
pub fn decode_message(version: EthVersion, buf: &mut &[u8]) -> Result<Self, MessageError> {
|
||||
let message_type = EthMessageID::decode(buf)?;
|
||||
|
||||
// For EIP-7642 (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7642.md):
|
||||
// pre-merge (legacy) status messages include total difficulty, whereas eth/69 omits it.
|
||||
let message = match message_type {
|
||||
EthMessageID::Status => EthMessage::Status(Status::decode(buf)?),
|
||||
EthMessageID::Status => EthMessage::Status(if version < EthVersion::Eth69 {
|
||||
StatusMessage::Legacy(Status::decode(buf)?)
|
||||
} else {
|
||||
StatusMessage::Eth69(StatusEth69::decode(buf)?)
|
||||
}),
|
||||
EthMessageID::NewBlockHashes => {
|
||||
if version.is_eth69() {
|
||||
return Err(MessageError::Invalid(version, EthMessageID::NewBlockHashes));
|
||||
@@ -186,7 +194,7 @@ impl<N: NetworkPrimitives> From<EthBroadcastMessage<N>> for ProtocolBroadcastMes
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum EthMessage<N: NetworkPrimitives = EthNetworkPrimitives> {
|
||||
/// Represents a Status message required for the protocol handshake.
|
||||
Status(Status),
|
||||
Status(StatusMessage),
|
||||
/// Represents a `NewBlockHashes` message broadcast to the network.
|
||||
NewBlockHashes(NewBlockHashes),
|
||||
/// Represents a `NewBlock` message broadcast to the network.
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::EthVersion;
|
||||
use alloy_chains::{Chain, NamedChain};
|
||||
use alloy_hardforks::{EthereumHardfork, ForkId, Head};
|
||||
use alloy_primitives::{hex, B256, U256};
|
||||
use alloy_rlp::{RlpDecodable, RlpEncodable};
|
||||
use alloy_rlp::{BufMut, Encodable, RlpDecodable, RlpEncodable};
|
||||
use core::fmt::{Debug, Display};
|
||||
use reth_chainspec::{EthChainSpec, Hardforks, MAINNET};
|
||||
use reth_codecs_derive::add_arbitrary_tests;
|
||||
@@ -306,6 +306,85 @@ impl From<Status> for StatusEth69 {
|
||||
}
|
||||
}
|
||||
|
||||
/// `StatusMessage` can store either the Legacy version (with TD) or the
|
||||
/// eth/69 version (omits TD).
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum StatusMessage {
|
||||
/// The legacy status (`eth/66` through `eth/68`) with `total_difficulty`.
|
||||
Legacy(Status),
|
||||
/// The new `eth/69` status with no `total_difficulty`.
|
||||
Eth69(StatusEth69),
|
||||
}
|
||||
|
||||
impl StatusMessage {
|
||||
/// Returns the genesis hash from the status message.
|
||||
pub const fn genesis(&self) -> B256 {
|
||||
match self {
|
||||
Self::Legacy(legacy_status) => legacy_status.genesis,
|
||||
Self::Eth69(status_69) => status_69.genesis,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the protocol version.
|
||||
pub const fn version(&self) -> EthVersion {
|
||||
match self {
|
||||
Self::Legacy(legacy_status) => legacy_status.version,
|
||||
Self::Eth69(status_69) => status_69.version,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the chain identifier.
|
||||
pub const fn chain(&self) -> &Chain {
|
||||
match self {
|
||||
Self::Legacy(legacy_status) => &legacy_status.chain,
|
||||
Self::Eth69(status_69) => &status_69.chain,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the fork identifier.
|
||||
pub const fn forkid(&self) -> ForkId {
|
||||
match self {
|
||||
Self::Legacy(legacy_status) => legacy_status.forkid,
|
||||
Self::Eth69(status_69) => status_69.forkid,
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts to legacy Status since full support for EIP-7642
|
||||
/// is not fully implemented
|
||||
/// `<https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7642.md>`
|
||||
pub fn to_legacy(self) -> Status {
|
||||
match self {
|
||||
Self::Legacy(legacy_status) => legacy_status,
|
||||
Self::Eth69(status_69) => Status {
|
||||
version: status_69.version,
|
||||
chain: status_69.chain,
|
||||
// total_difficulty is omitted in Eth69.
|
||||
total_difficulty: U256::default(),
|
||||
blockhash: status_69.blockhash,
|
||||
genesis: status_69.genesis,
|
||||
forkid: status_69.forkid,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for StatusMessage {
|
||||
fn encode(&self, out: &mut dyn BufMut) {
|
||||
match self {
|
||||
Self::Legacy(s) => s.encode(out),
|
||||
Self::Eth69(s) => s.encode(out),
|
||||
}
|
||||
}
|
||||
|
||||
fn length(&self) -> usize {
|
||||
match self {
|
||||
Self::Legacy(s) => s.length(),
|
||||
Self::Eth69(s) => s.length(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{EthVersion, Status, StatusEth69};
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::{Sink, SinkExt, Stream};
|
||||
use reth_eth_wire_types::{
|
||||
DisconnectReason, EthMessage, EthNetworkPrimitives, ProtocolMessage, Status,
|
||||
DisconnectReason, EthMessage, EthNetworkPrimitives, ProtocolMessage, Status, StatusMessage,
|
||||
};
|
||||
use reth_ethereum_forks::ForkFilter;
|
||||
use reth_primitives_traits::GotExpected;
|
||||
@@ -90,7 +90,7 @@ where
|
||||
alloy_rlp::encode(ProtocolMessage::<EthNetworkPrimitives>::from(EthMessage::<
|
||||
EthNetworkPrimitives,
|
||||
>::Status(
|
||||
status
|
||||
StatusMessage::Legacy(status),
|
||||
)))
|
||||
.into();
|
||||
unauth.send(status_msg).await.map_err(EthStreamError::from)?;
|
||||
@@ -135,39 +135,43 @@ where
|
||||
|
||||
// Validate peer response
|
||||
match msg.message {
|
||||
EthMessage::Status(their_status) => {
|
||||
EthMessage::Status(their_status_message) => {
|
||||
trace!("Validating incoming ETH status from peer");
|
||||
|
||||
if status.genesis != their_status.genesis {
|
||||
if status.genesis != their_status_message.genesis() {
|
||||
unauth
|
||||
.disconnect(DisconnectReason::ProtocolBreach)
|
||||
.await
|
||||
.map_err(EthStreamError::from)?;
|
||||
return Err(EthHandshakeError::MismatchedGenesis(
|
||||
GotExpected { expected: status.genesis, got: their_status.genesis }.into(),
|
||||
GotExpected {
|
||||
expected: status.genesis,
|
||||
got: their_status_message.genesis(),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if status.version != their_status.version {
|
||||
if status.version != their_status_message.version() {
|
||||
unauth
|
||||
.disconnect(DisconnectReason::ProtocolBreach)
|
||||
.await
|
||||
.map_err(EthStreamError::from)?;
|
||||
return Err(EthHandshakeError::MismatchedProtocolVersion(GotExpected {
|
||||
got: their_status.version,
|
||||
got: their_status_message.version(),
|
||||
expected: status.version,
|
||||
})
|
||||
.into());
|
||||
}
|
||||
|
||||
if status.chain != their_status.chain {
|
||||
if status.chain != *their_status_message.chain() {
|
||||
unauth
|
||||
.disconnect(DisconnectReason::ProtocolBreach)
|
||||
.await
|
||||
.map_err(EthStreamError::from)?;
|
||||
return Err(EthHandshakeError::MismatchedChain(GotExpected {
|
||||
got: their_status.chain,
|
||||
got: *their_status_message.chain(),
|
||||
expected: status.chain,
|
||||
})
|
||||
.into());
|
||||
@@ -188,7 +192,7 @@ where
|
||||
|
||||
// Fork validation
|
||||
if let Err(err) = fork_filter
|
||||
.validate(their_status.forkid)
|
||||
.validate(their_status_message.forkid())
|
||||
.map_err(EthHandshakeError::InvalidFork)
|
||||
{
|
||||
unauth
|
||||
@@ -198,7 +202,7 @@ where
|
||||
return Err(err.into());
|
||||
}
|
||||
|
||||
Ok(their_status)
|
||||
Ok(their_status_message.to_legacy())
|
||||
}
|
||||
_ => {
|
||||
unauth
|
||||
|
||||
Reference in New Issue
Block a user