mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: add eth-wire (#20)
This commit is contained in:
71
Cargo.lock
generated
71
Cargo.lock
generated
@ -160,6 +160,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "build_const"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7"
|
||||
|
||||
[[package]]
|
||||
name = "byte-slice-cast"
|
||||
version = "1.2.1"
|
||||
@ -244,6 +250,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
|
||||
dependencies = [
|
||||
"build_const",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
@ -376,6 +391,19 @@ dependencies = [
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethereum-forkid"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70b823f6b913b97e58a2bd67a7beeb48b0338d4aa8e3cc21d9cdab457716e4d4"
|
||||
dependencies = [
|
||||
"crc",
|
||||
"fastrlp",
|
||||
"maplit",
|
||||
"primitive-types",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethereum-types"
|
||||
version = "0.13.1"
|
||||
@ -400,7 +428,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"elliptic-curve",
|
||||
"ethabi",
|
||||
"fastrlp 0.1.3",
|
||||
"fastrlp",
|
||||
"generic-array",
|
||||
"hex",
|
||||
"k256",
|
||||
@ -438,17 +466,6 @@ dependencies = [
|
||||
"fastrlp-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrlp"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f9244dd9f0a2b54815814926640439146f469837ec4141e33354b6b258a0493"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"auto_impl",
|
||||
"bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrlp-derive"
|
||||
version = "0.1.2"
|
||||
@ -684,6 +701,12 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hex-literal"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
@ -1012,6 +1035,12 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "mdbx-sys"
|
||||
version = "0.12.1-0"
|
||||
@ -1431,6 +1460,20 @@ dependencies = [
|
||||
"libmdbx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reth-eth-wire"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"ethereum-forkid",
|
||||
"ethers-core",
|
||||
"fastrlp",
|
||||
"hex",
|
||||
"hex-literal",
|
||||
"reth-primitives",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reth-executor"
|
||||
version = "0.1.0"
|
||||
@ -1465,7 +1508,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"ethers-core",
|
||||
"fastrlp 0.2.0",
|
||||
"fastrlp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1493,7 +1536,7 @@ dependencies = [
|
||||
name = "reth-rpc-types"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fastrlp 0.1.3",
|
||||
"fastrlp",
|
||||
"reth-primitives",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@ -10,6 +10,7 @@ members = [
|
||||
"crates/executor",
|
||||
"crates/interfaces",
|
||||
"crates/net/p2p",
|
||||
"crates/net/eth-wire",
|
||||
"crates/net/rpc",
|
||||
"crates/net/rpc-api",
|
||||
"crates/net/rpc-types",
|
||||
|
||||
23
crates/net/eth-wire/Cargo.toml
Normal file
23
crates/net/eth-wire/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "reth-eth-wire"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/foundry-rs/reth"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
bytes = { version = "1.1" }
|
||||
|
||||
# can remove these restrictions once ethereum-types is bumped across the board
|
||||
fastrlp = { version = "0.1.3", features = ["alloc", "derive", "std", "ethereum-types"] }
|
||||
ethereum-forkid = "=0.10"
|
||||
hex = "0.4"
|
||||
thiserror = "1"
|
||||
|
||||
# reth
|
||||
reth-primitives = { path = "../../primitives" }
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.3"
|
||||
ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false }
|
||||
14
crates/net/eth-wire/src/lib.rs
Normal file
14
crates/net/eth-wire/src/lib.rs
Normal file
@ -0,0 +1,14 @@
|
||||
#![warn(missing_docs, unreachable_pub)]
|
||||
#![deny(unused_must_use, rust_2018_idioms)]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
))]
|
||||
|
||||
//! Types for the eth wire protocol.
|
||||
|
||||
mod status;
|
||||
pub use status::Status;
|
||||
|
||||
mod version;
|
||||
pub use version::EthVersion;
|
||||
210
crates/net/eth-wire/src/status.rs
Normal file
210
crates/net/eth-wire/src/status.rs
Normal file
@ -0,0 +1,210 @@
|
||||
use ethereum_forkid::ForkId;
|
||||
use fastrlp::{RlpDecodable, RlpEncodable};
|
||||
use reth_primitives::{Chain, H256, U256};
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
/// The status message is used in the eth protocol handshake to ensure that peers are on the same
|
||||
/// network and are following the same fork.
|
||||
/// The total difficulty and best block hash are used to identify whether or not the requesting
|
||||
/// client should be sent historical blocks for a full blockchain sync.
|
||||
///
|
||||
/// When performing a handshake, the total difficulty is not guaranteed to correspond to the block
|
||||
/// hash. This information should be treated as untrusted.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
|
||||
pub struct Status {
|
||||
/// The current protocol version. For example, peers running `eth/66` would have a version of
|
||||
/// 66.
|
||||
pub version: u8,
|
||||
|
||||
/// The chain id, as introduced in
|
||||
/// [EIP155](https://eips.ethereum.org/EIPS/eip-155#list-of-chain-ids).
|
||||
pub chain: Chain,
|
||||
|
||||
/// Total difficulty of the best chain.
|
||||
pub total_difficulty: U256,
|
||||
|
||||
/// The highest difficulty block hash the peer has seen
|
||||
pub blockhash: H256,
|
||||
|
||||
/// The genesis hash of the peer's chain.
|
||||
pub genesis: H256,
|
||||
|
||||
/// 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 Status {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let hexed_blockhash = hex::encode(self.blockhash);
|
||||
let hexed_genesis = hex::encode(self.genesis);
|
||||
write!(
|
||||
f,
|
||||
"Status {{ version: {}, chain: {}, total_difficulty: {}, blockhash: {}, genesis: {}, forkid: {:X?} }}",
|
||||
self.version,
|
||||
self.chain,
|
||||
self.total_difficulty,
|
||||
hexed_blockhash,
|
||||
hexed_genesis,
|
||||
self.forkid
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Status {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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\ttotal_difficulty: {:?},\n\tblockhash: {},\n\tgenesis: {},\n\tforkid: {:X?}\n}}",
|
||||
self.version,
|
||||
self.chain,
|
||||
self.total_difficulty,
|
||||
hexed_blockhash,
|
||||
hexed_genesis,
|
||||
self.forkid
|
||||
)
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"Status {{ version: {:?}, chain: {:?}, total_difficulty: {:?}, blockhash: {}, genesis: {}, forkid: {:X?} }}",
|
||||
self.version,
|
||||
self.chain,
|
||||
self.total_difficulty,
|
||||
hexed_blockhash,
|
||||
hexed_genesis,
|
||||
self.forkid
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use ethereum_forkid::{ForkHash, ForkId};
|
||||
use ethers_core::types::Chain as NamedChain;
|
||||
use fastrlp::{Decodable, Encodable};
|
||||
use hex_literal::hex;
|
||||
use reth_primitives::{Chain, H256, U256};
|
||||
|
||||
use crate::{EthVersion, Status};
|
||||
|
||||
#[test]
|
||||
fn encode_eth_status_message() {
|
||||
let expected = hex!("f85643018a07aac59dabcdd74bc567a0feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13da0d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3c684b715077d80");
|
||||
let status = Status {
|
||||
version: EthVersion::Eth67 as u8,
|
||||
chain: Chain::Named(NamedChain::Mainnet),
|
||||
total_difficulty: U256::from(36206751599115524359527u128),
|
||||
blockhash: H256::from_str(
|
||||
"feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13d",
|
||||
)
|
||||
.unwrap(),
|
||||
genesis: H256::from_str(
|
||||
"d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3",
|
||||
)
|
||||
.unwrap(),
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_eth_status_message() {
|
||||
let data = hex!("f85643018a07aac59dabcdd74bc567a0feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13da0d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3c684b715077d80");
|
||||
let expected = Status {
|
||||
version: EthVersion::Eth67 as u8,
|
||||
chain: Chain::Named(NamedChain::Mainnet),
|
||||
total_difficulty: U256::from(36206751599115524359527u128),
|
||||
blockhash: H256::from_str(
|
||||
"feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13d",
|
||||
)
|
||||
.unwrap(),
|
||||
genesis: H256::from_str(
|
||||
"d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3",
|
||||
)
|
||||
.unwrap(),
|
||||
forkid: ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 0 },
|
||||
};
|
||||
let status = Status::decode(&mut &data[..]).unwrap();
|
||||
assert_eq!(status, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_network_status_message() {
|
||||
let expected = hex!("f850423884024190faa0f8514c4680ef27700751b08f37645309ce65a449616a3ea966bf39dd935bb27ba00d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5bc6845d43d2fd80");
|
||||
let status = Status {
|
||||
version: EthVersion::Eth66 as u8,
|
||||
chain: Chain::Named(NamedChain::BinanceSmartChain),
|
||||
total_difficulty: U256::from(37851386u64),
|
||||
blockhash: H256::from_str(
|
||||
"f8514c4680ef27700751b08f37645309ce65a449616a3ea966bf39dd935bb27b",
|
||||
)
|
||||
.unwrap(),
|
||||
genesis: H256::from_str(
|
||||
"0d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5b",
|
||||
)
|
||||
.unwrap(),
|
||||
forkid: ForkId { hash: ForkHash([0x5d, 0x43, 0xd2, 0xfd]), next: 0 },
|
||||
};
|
||||
|
||||
let mut rlp_status = vec![];
|
||||
status.encode(&mut rlp_status);
|
||||
assert_eq!(rlp_status, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_network_status_message() {
|
||||
let data = hex!("f850423884024190faa0f8514c4680ef27700751b08f37645309ce65a449616a3ea966bf39dd935bb27ba00d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5bc6845d43d2fd80");
|
||||
let expected = Status {
|
||||
version: EthVersion::Eth66 as u8,
|
||||
chain: Chain::Named(NamedChain::BinanceSmartChain),
|
||||
total_difficulty: U256::from(37851386u64),
|
||||
blockhash: H256::from_str(
|
||||
"f8514c4680ef27700751b08f37645309ce65a449616a3ea966bf39dd935bb27b",
|
||||
)
|
||||
.unwrap(),
|
||||
genesis: H256::from_str(
|
||||
"0d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5b",
|
||||
)
|
||||
.unwrap(),
|
||||
forkid: ForkId { hash: ForkHash([0x5d, 0x43, 0xd2, 0xfd]), next: 0 },
|
||||
};
|
||||
let status = Status::decode(&mut &data[..]).unwrap();
|
||||
assert_eq!(status, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_another_network_status_message() {
|
||||
let data = hex!("f86142820834936d68fcffffffffffffffffffffffffdeab81b8a0523e8163a6d620a4cc152c547a05f28a03fec91a2a615194cb86df9731372c0ca06499dccdc7c7def3ebb1ce4c6ee27ec6bd02aee570625ca391919faf77ef27bdc6841a67ccd880");
|
||||
let expected = Status {
|
||||
version: EthVersion::Eth66 as u8,
|
||||
chain: Chain::Id(2100),
|
||||
total_difficulty: U256::from_str(
|
||||
"0x000000000000000000000000006d68fcffffffffffffffffffffffffdeab81b8",
|
||||
)
|
||||
.unwrap(),
|
||||
blockhash: H256::from_str(
|
||||
"523e8163a6d620a4cc152c547a05f28a03fec91a2a615194cb86df9731372c0c",
|
||||
)
|
||||
.unwrap(),
|
||||
genesis: H256::from_str(
|
||||
"6499dccdc7c7def3ebb1ce4c6ee27ec6bd02aee570625ca391919faf77ef27bd",
|
||||
)
|
||||
.unwrap(),
|
||||
forkid: ForkId { hash: ForkHash([0x1a, 0x67, 0xcc, 0xd8]), next: 0 },
|
||||
};
|
||||
let status = Status::decode(&mut &data[..]).unwrap();
|
||||
assert_eq!(status, expected);
|
||||
}
|
||||
}
|
||||
107
crates/net/eth-wire/src/version.rs
Normal file
107
crates/net/eth-wire/src/version.rs
Normal file
@ -0,0 +1,107 @@
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||
#[error("Unknown eth protocol version: {0}")]
|
||||
pub struct ParseVersionError(String);
|
||||
|
||||
/// The `eth` protocol version.
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum EthVersion {
|
||||
/// The `eth` protocol version 66.
|
||||
Eth66 = 66,
|
||||
|
||||
/// The `eth` protocol version 67.
|
||||
Eth67 = 67,
|
||||
}
|
||||
|
||||
/// Allow for converting from a `&str` to an `EthVersion`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use reth_eth_wire::EthVersion;
|
||||
///
|
||||
/// let version = EthVersion::try_from("67").unwrap();
|
||||
/// assert_eq!(version, EthVersion::Eth67);
|
||||
/// ```
|
||||
impl TryFrom<&str> for EthVersion {
|
||||
type Error = ParseVersionError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
match s {
|
||||
"66" => Ok(EthVersion::Eth66),
|
||||
"67" => Ok(EthVersion::Eth67),
|
||||
_ => Err(ParseVersionError(s.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow for converting from a u8 to an `EthVersion`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use reth_eth_wire::EthVersion;
|
||||
///
|
||||
/// let version = EthVersion::try_from(67).unwrap();
|
||||
/// assert_eq!(version, EthVersion::Eth67);
|
||||
/// ```
|
||||
impl TryFrom<u8> for EthVersion {
|
||||
type Error = ParseVersionError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(u: u8) -> Result<Self, Self::Error> {
|
||||
match u {
|
||||
66 => Ok(EthVersion::Eth66),
|
||||
67 => Ok(EthVersion::Eth67),
|
||||
_ => Err(ParseVersionError(u.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for EthVersion {
|
||||
type Err = ParseVersionError;
|
||||
|
||||
#[inline]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
EthVersion::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EthVersion> for u8 {
|
||||
#[inline]
|
||||
fn from(v: EthVersion) -> u8 {
|
||||
v as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EthVersion> for &'static str {
|
||||
#[inline]
|
||||
fn from(v: EthVersion) -> &'static str {
|
||||
match v {
|
||||
EthVersion::Eth66 => "66",
|
||||
EthVersion::Eth67 => "67",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{EthVersion, ParseVersionError};
|
||||
use std::{convert::TryFrom, string::ToString};
|
||||
|
||||
#[test]
|
||||
fn test_eth_version_try_from_str() {
|
||||
assert_eq!(EthVersion::Eth66, EthVersion::try_from("66").unwrap());
|
||||
assert_eq!(EthVersion::Eth67, EthVersion::try_from("67").unwrap());
|
||||
assert_eq!(Err(ParseVersionError("68".to_string())), EthVersion::try_from("68"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eth_version_from_str() {
|
||||
assert_eq!(EthVersion::Eth66, "66".parse().unwrap());
|
||||
assert_eq!(EthVersion::Eth67, "67".parse().unwrap());
|
||||
assert_eq!(Err(ParseVersionError("68".to_string())), "68".parse::<EthVersion>());
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,6 @@ readme = "README.md"
|
||||
description = "Commonly used types in reth."
|
||||
|
||||
[dependencies]
|
||||
fastrlp = { version = "0.2.0" }
|
||||
fastrlp = { version = "0.1.3" }
|
||||
ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false }
|
||||
bytes = "1.2"
|
||||
@ -17,6 +17,7 @@ mod transaction;
|
||||
|
||||
pub use account::Account;
|
||||
pub use block::{Block, BlockLocked};
|
||||
pub use chain::Chain;
|
||||
pub use header::{Header, HeaderLocked};
|
||||
pub use log::Log;
|
||||
pub use receipt::Receipt;
|
||||
|
||||
Reference in New Issue
Block a user