feat: add eth-wire (#20)

This commit is contained in:
Dan Cline
2022-10-10 12:46:02 -04:00
committed by GitHub
parent a759201b40
commit d7c8b70cc3
8 changed files with 414 additions and 15 deletions

71
Cargo.lock generated
View File

@ -160,6 +160,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "build_const"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7"
[[package]] [[package]]
name = "byte-slice-cast" name = "byte-slice-cast"
version = "1.2.1" version = "1.2.1"
@ -244,6 +250,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crc"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
dependencies = [
"build_const",
]
[[package]] [[package]]
name = "crunchy" name = "crunchy"
version = "0.2.2" version = "0.2.2"
@ -376,6 +391,19 @@ dependencies = [
"tiny-keccak", "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]] [[package]]
name = "ethereum-types" name = "ethereum-types"
version = "0.13.1" version = "0.13.1"
@ -400,7 +428,7 @@ dependencies = [
"chrono", "chrono",
"elliptic-curve", "elliptic-curve",
"ethabi", "ethabi",
"fastrlp 0.1.3", "fastrlp",
"generic-array", "generic-array",
"hex", "hex",
"k256", "k256",
@ -438,17 +466,6 @@ dependencies = [
"fastrlp-derive", "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]] [[package]]
name = "fastrlp-derive" name = "fastrlp-derive"
version = "0.1.2" version = "0.1.2"
@ -684,6 +701,12 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hex-literal"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0"
[[package]] [[package]]
name = "hmac" name = "hmac"
version = "0.12.1" version = "0.12.1"
@ -1012,6 +1035,12 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]] [[package]]
name = "mdbx-sys" name = "mdbx-sys"
version = "0.12.1-0" version = "0.12.1-0"
@ -1431,6 +1460,20 @@ dependencies = [
"libmdbx", "libmdbx",
] ]
[[package]]
name = "reth-eth-wire"
version = "0.1.0"
dependencies = [
"bytes",
"ethereum-forkid",
"ethers-core",
"fastrlp",
"hex",
"hex-literal",
"reth-primitives",
"thiserror",
]
[[package]] [[package]]
name = "reth-executor" name = "reth-executor"
version = "0.1.0" version = "0.1.0"
@ -1465,7 +1508,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bytes", "bytes",
"ethers-core", "ethers-core",
"fastrlp 0.2.0", "fastrlp",
] ]
[[package]] [[package]]
@ -1493,7 +1536,7 @@ dependencies = [
name = "reth-rpc-types" name = "reth-rpc-types"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"fastrlp 0.1.3", "fastrlp",
"reth-primitives", "reth-primitives",
"serde", "serde",
"serde_json", "serde_json",

View File

@ -10,6 +10,7 @@ members = [
"crates/executor", "crates/executor",
"crates/interfaces", "crates/interfaces",
"crates/net/p2p", "crates/net/p2p",
"crates/net/eth-wire",
"crates/net/rpc", "crates/net/rpc",
"crates/net/rpc-api", "crates/net/rpc-api",
"crates/net/rpc-types", "crates/net/rpc-types",

View 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 }

View 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;

View 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);
}
}

View 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>());
}
}

View File

@ -8,6 +8,6 @@ readme = "README.md"
description = "Commonly used types in reth." description = "Commonly used types in reth."
[dependencies] [dependencies]
fastrlp = { version = "0.2.0" } fastrlp = { version = "0.1.3" }
ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false } ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false }
bytes = "1.2" bytes = "1.2"

View File

@ -17,6 +17,7 @@ mod transaction;
pub use account::Account; pub use account::Account;
pub use block::{Block, BlockLocked}; pub use block::{Block, BlockLocked};
pub use chain::Chain;
pub use header::{Header, HeaderLocked}; pub use header::{Header, HeaderLocked};
pub use log::Log; pub use log::Log;
pub use receipt::Receipt; pub use receipt::Receipt;