diff --git a/crates/net/eth-wire-types/src/lib.rs b/crates/net/eth-wire-types/src/lib.rs index 84fa4d928..9bbbc2359 100644 --- a/crates/net/eth-wire-types/src/lib.rs +++ b/crates/net/eth-wire-types/src/lib.rs @@ -12,7 +12,7 @@ extern crate alloc; mod status; -pub use status::{Status, StatusBuilder}; +pub use status::{Status, StatusBuilder, StatusEth69}; pub mod version; pub use version::{EthVersion, ProtocolVersion}; diff --git a/crates/net/eth-wire-types/src/status.rs b/crates/net/eth-wire-types/src/status.rs index d0259d2fe..7c1f8faf5 100644 --- a/crates/net/eth-wire-types/src/status.rs +++ b/crates/net/eth-wire-types/src/status.rs @@ -213,9 +213,97 @@ impl StatusBuilder { } } +/// Similar to [`Status`], but for `eth/69` version, which does not contain +/// the `total_difficulty` field. +#[derive(Copy, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] +#[add_arbitrary_tests(rlp)] +pub struct StatusEth69 { + /// The current protocol version. + /// Here, version is `eth/69`. + pub version: EthVersion, + + /// The chain id, as introduced in + /// [EIP155](https://eips.ethereum.org/EIPS/eip-155#list-of-chain-ids). + pub chain: Chain, + + /// The highest difficulty block hash the peer has seen + pub blockhash: B256, + + /// The genesis hash of the peer's chain. + pub genesis: B256, + + /// The fork identifier, a [CRC32 + /// checksum](https://en.wikipedia.org/wiki/Cyclic_redundancy_check#CRC-32_algorithm) for + /// identifying the peer's fork as defined by + /// [EIP-2124](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2124.md). + /// This was added in [`eth/64`](https://eips.ethereum.org/EIPS/eip-2364) + pub forkid: ForkId, +} + +impl Display for StatusEth69 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let hexed_blockhash = hex::encode(self.blockhash); + let hexed_genesis = hex::encode(self.genesis); + write!( + f, + "Status {{ version: {}, chain: {}, blockhash: {}, genesis: {}, forkid: {:X?} }}", + self.version, self.chain, hexed_blockhash, hexed_genesis, self.forkid + ) + } +} + +impl Debug for StatusEth69 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let hexed_blockhash = hex::encode(self.blockhash); + let hexed_genesis = hex::encode(self.genesis); + if f.alternate() { + write!( + f, + "Status {{\n\tversion: {:?},\n\tchain: {:?},\n\tblockhash: {},\n\tgenesis: {},\n\tforkid: {:X?}\n}}", + self.version, + self.chain, + hexed_blockhash, + hexed_genesis, + self.forkid + ) + } else { + write!( + f, + "Status {{ version: {:?}, chain: {:?}, blockhash: {}, genesis: {}, forkid: {:X?} }}", + self.version, + self.chain, + hexed_blockhash, + hexed_genesis, + self.forkid + ) + } + } +} + +// +impl Default for StatusEth69 { + fn default() -> Self { + Status::default().into() + } +} + +impl From for StatusEth69 { + fn from(status: Status) -> Self { + Self { + version: EthVersion::Eth69, + chain: status.chain, + blockhash: status.blockhash, + genesis: status.genesis, + forkid: status.forkid, + } + } +} + #[cfg(test)] mod tests { - use crate::{EthVersion, Status}; + use crate::{EthVersion, Status, StatusEth69}; use alloy_consensus::constants::MAINNET_GENESIS_HASH; use alloy_genesis::Genesis; use alloy_primitives::{hex, B256, U256}; @@ -263,6 +351,83 @@ mod tests { assert_eq!(status, expected); } + #[test] + fn test_status_to_statuseth69_conversion() { + let status = StatusEth69 { + version: EthVersion::Eth69, + chain: Chain::from_named(NamedChain::Mainnet), + blockhash: B256::from_str( + "feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13d", + ) + .unwrap(), + genesis: MAINNET_GENESIS_HASH, + forkid: ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 0 }, + }; + let status_converted: StatusEth69 = Status { + version: EthVersion::Eth69, + chain: Chain::from_named(NamedChain::Mainnet), + total_difficulty: U256::from(36206751599115524359527u128), + blockhash: B256::from_str( + "feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13d", + ) + .unwrap(), + genesis: MAINNET_GENESIS_HASH, + forkid: ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 0 }, + } + .into(); + assert_eq!(status, status_converted); + } + + #[test] + fn encode_eth69_status_message() { + let expected = hex!("f84b4501a0feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13da0d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3c684b715077d80"); + let status = StatusEth69 { + version: EthVersion::Eth69, + chain: Chain::from_named(NamedChain::Mainnet), + blockhash: B256::from_str( + "feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13d", + ) + .unwrap(), + genesis: MAINNET_GENESIS_HASH, + forkid: ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 0 }, + }; + + let mut rlp_status = vec![]; + status.encode(&mut rlp_status); + assert_eq!(rlp_status, expected); + + let status: StatusEth69 = Status::builder() + .chain(Chain::from_named(NamedChain::Mainnet)) + .blockhash( + B256::from_str("feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13d") + .unwrap(), + ) + .genesis(MAINNET_GENESIS_HASH) + .forkid(ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 0 }) + .build() + .into(); + let mut rlp_status = vec![]; + status.encode(&mut rlp_status); + assert_eq!(rlp_status, expected); + } + + #[test] + fn decode_eth69_status_message() { + let data = hex!("0xf84b4501a0feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13da0d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3c684b715077d80"); + let expected = StatusEth69 { + version: EthVersion::Eth69, + chain: Chain::from_named(NamedChain::Mainnet), + blockhash: B256::from_str( + "feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13d", + ) + .unwrap(), + genesis: MAINNET_GENESIS_HASH, + forkid: ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 0 }, + }; + let status = StatusEth69::decode(&mut &data[..]).unwrap(); + assert_eq!(status, expected); + } + #[test] fn encode_network_status_message() { let expected = hex!("f850423884024190faa0f8514c4680ef27700751b08f37645309ce65a449616a3ea966bf39dd935bb27ba00d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5bc6845d43d2fd80");