feat(net): authenticate sessions (#178)

* Switch stream type of ActiveSession to EthStream

 * Start `StatusBuilder` for initializing the `Status` message required
   for the handshake
 * Add `Hardfork` for `Status` default forkid
 * Add `MAINNET_GENESIS` constant

* finish `StatusBuilder`

* initialize eth streams in session

 * add status, hello, and fork filter to session manager
 * fix status builder example
 * add status and hello to network config
   * will probably remove

* removing status and hello from networkconfig

* move forkid to primitives

* change imports for forkid

* add hardfork to primitives

* remove hardfork and forkid from eth-wire

* fix remaining eth-wire forkid references

* put mainnet genesis in constants, remove NodeId

* replace NodeId with PeerId

 * the only NodeId remaining is inherited from enr
 * PeerId still needs to be documented
 * also run cargo fmt

* replace loop with iter().any()

* ignore missing docs for hardforks

* use correct PeerId for Discv4::bind example test

* document PeerId as secp256k1 public key

* cargo fmt

* temporarily allow too_many_arguments

 * the authenticate and start_pending_incoming_session methods have many
   arguments, we can reconsider the lint or fix the methods at a later
   point
This commit is contained in:
Dan Cline
2022-11-14 12:03:05 -05:00
committed by GitHub
parent 5ca2cab97f
commit f1e6639374
31 changed files with 714 additions and 207 deletions

View File

@ -24,7 +24,7 @@ pub struct Discv4Config {
pub find_node_timeout: Duration,
/// The duration we set for neighbours responses
pub neighbours_timeout: Duration,
/// A set of lists that permit or ban IP's or NodeIds from the server. See
/// A set of lists that permit or ban IP's or PeerIds from the server. See
/// `crate::PermitBanList`.
pub permit_ban_list: PermitBanList,
/// Set the default duration for which nodes are banned for. This timeouts are checked every 5
@ -110,7 +110,7 @@ impl Discv4ConfigBuilder {
self
}
/// A set of lists that permit or ban IP's or NodeIds from the server. See
/// A set of lists that permit or ban IP's or PeerIds from the server. See
/// `crate::PermitBanList`.
pub fn permit_ban_list(&mut self, list: PermitBanList) -> &mut Self {
self.config.permit_ban_list = list;

View File

@ -30,7 +30,7 @@ use discv5::{
},
ConnectionDirection, ConnectionState,
};
use reth_primitives::{H256, H512};
use reth_primitives::{PeerId, H256};
use secp256k1::SecretKey;
use std::{
cell::RefCell,
@ -67,9 +67,6 @@ pub mod mock;
/// reexport to get public ip.
pub use public_ip;
/// Identifier for nodes.
pub type NodeId = H512;
/// The default port for discv4 via UDP
///
/// Note: the default TCP port is the same.
@ -140,12 +137,13 @@ impl Discv4 {
/// use std::str::FromStr;
/// use rand::thread_rng;
/// use secp256k1::SECP256K1;
/// use reth_discv4::{Discv4, Discv4Config, NodeId, NodeRecord};
/// use reth_primitives::PeerId;
/// use reth_discv4::{Discv4, Discv4Config, NodeRecord};
/// # async fn t() -> io::Result<()> {
/// // generate a (random) keypair
/// let mut rng = thread_rng();
/// let (secret_key, pk) = SECP256K1.generate_keypair(&mut rng);
/// let id = NodeId::from_slice(&pk.serialize_uncompressed()[1..]);
/// let id = PeerId::from_slice(&pk.serialize_uncompressed()[1..]);
///
/// let socket = SocketAddr::from_str("0.0.0.0:0").unwrap();
/// let local_enr = NodeRecord {
@ -212,11 +210,11 @@ impl Discv4 {
}
/// Looks up the given node id
pub async fn lookup(&self, node_id: NodeId) -> Result<Vec<NodeRecord>, Discv4Error> {
pub async fn lookup(&self, node_id: PeerId) -> Result<Vec<NodeRecord>, Discv4Error> {
self.lookup_node(Some(node_id)).await
}
async fn lookup_node(&self, node_id: Option<NodeId>) -> Result<Vec<NodeRecord>, Discv4Error> {
async fn lookup_node(&self, node_id: Option<PeerId>) -> Result<Vec<NodeRecord>, Discv4Error> {
let (tx, rx) = oneshot::channel();
let cmd = Discv4Command::Lookup { node_id, tx: Some(tx) };
self.to_service.send(cmd).await?;
@ -269,9 +267,9 @@ pub struct Discv4Service {
/// followup `FindNode` requests.... Buffering them effectively prevents high `Ping` peaks.
queued_pings: VecDeque<(NodeRecord, PingReason)>,
/// Currently active pings to specific nodes.
pending_pings: HashMap<NodeId, PingRequest>,
pending_pings: HashMap<PeerId, PingRequest>,
/// Currently active FindNode requests
pending_find_nodes: HashMap<NodeId, FindNodeRequest>,
pending_find_nodes: HashMap<PeerId, FindNodeRequest>,
/// Commands listener
commands_rx: Option<mpsc::Receiver<Discv4Command>>,
/// All subscribers for table updates
@ -377,8 +375,8 @@ impl Discv4Service {
&mut self.local_enr
}
/// Returns true if the given NodeId is currently in the bucket
pub fn contains_node(&self, id: NodeId) -> bool {
/// Returns true if the given PeerId is currently in the bucket
pub fn contains_node(&self, id: PeerId) -> bool {
let key = kad_key(id);
self.kbuckets.get_index(&key).is_some()
}
@ -431,7 +429,7 @@ impl Discv4Service {
//
// To guard against traffic amplification attacks, Neighbors replies should only be sent if the
// sender of FindNode has been verified by the endpoint proof procedure.
pub fn lookup(&mut self, target: NodeId) {
pub fn lookup(&mut self, target: PeerId) {
self.lookup_with(target, None)
}
@ -445,7 +443,7 @@ impl Discv4Service {
/// This takes an optional Sender through which all successfully discovered nodes are sent once
/// the request has finished.
#[instrument(skip_all, fields(?target), target = "net::discv4")]
fn lookup_with(&mut self, target: NodeId, tx: Option<NodeRecordSender>) {
fn lookup_with(&mut self, target: PeerId, tx: Option<NodeRecordSender>) {
trace!("Starting lookup");
let key = kad_key(target);
@ -499,7 +497,7 @@ impl Discv4Service {
///
/// This allows applications, for whatever reason, to remove nodes from the local routing
/// table. Returns `true` if the node was in the table and `false` otherwise.
pub fn remove_node(&mut self, node_id: NodeId) -> bool {
pub fn remove_node(&mut self, node_id: PeerId) -> bool {
let key = kad_key(node_id);
let removed = self.kbuckets.remove(&key);
if removed {
@ -559,7 +557,7 @@ impl Discv4Service {
}
/// Message handler for an incoming `Ping`
fn on_ping(&mut self, ping: Ping, remote_addr: SocketAddr, remote_id: NodeId, hash: H256) {
fn on_ping(&mut self, ping: Ping, remote_addr: SocketAddr, remote_id: PeerId, hash: H256) {
// update the record
let record = NodeRecord {
address: ping.from.address,
@ -611,7 +609,7 @@ impl Discv4Service {
}
/// Message handler for an incoming `Pong`.
fn on_pong(&mut self, pong: Pong, remote_addr: SocketAddr, remote_id: NodeId) {
fn on_pong(&mut self, pong: Pong, remote_addr: SocketAddr, remote_id: PeerId) {
if self.is_expired(pong.expire) {
return
}
@ -654,7 +652,7 @@ impl Discv4Service {
}
/// Handler for incoming `FindNode` message
fn on_find_node(&mut self, msg: FindNode, remote_addr: SocketAddr, node_id: NodeId) {
fn on_find_node(&mut self, msg: FindNode, remote_addr: SocketAddr, node_id: PeerId) {
match self.node_status(node_id, remote_addr) {
NodeEntryStatus::IsLocal => {
// received address from self
@ -675,7 +673,7 @@ impl Discv4Service {
/// Handler for incoming `Neighbours` messages that are handled if they're responses to
/// `FindNode` requests
fn on_neighbours(&mut self, msg: Neighbours, remote_addr: SocketAddr, node_id: NodeId) {
fn on_neighbours(&mut self, msg: Neighbours, remote_addr: SocketAddr, node_id: PeerId) {
// check if this request was expected
let ctx = match self.pending_find_nodes.entry(node_id) {
Entry::Occupied(mut entry) => {
@ -732,7 +730,7 @@ impl Discv4Service {
}
/// Sends a Neighbours packet for `target` to the given addr
fn respond_closest(&mut self, target: NodeId, to: SocketAddr) {
fn respond_closest(&mut self, target: PeerId, to: SocketAddr) {
let key = kad_key(target);
let expire = self.send_neighbours_timeout();
let all_nodes = self.kbuckets.closest_values(&key).collect::<Vec<_>>();
@ -746,7 +744,7 @@ impl Discv4Service {
}
/// Returns the current status of the node
fn node_status(&mut self, node: NodeId, addr: SocketAddr) -> NodeEntryStatus {
fn node_status(&mut self, node: PeerId, addr: SocketAddr) -> NodeEntryStatus {
if node == self.local_enr.id {
debug!(?node, target = "net::disc", "Got an incoming discovery request from self");
return NodeEntryStatus::IsLocal
@ -807,7 +805,7 @@ impl Discv4Service {
}
/// Removes the node from the table
fn expire_node_request(&mut self, node_id: NodeId) {
fn expire_node_request(&mut self, node_id: PeerId) {
let key = kad_key(node_id);
self.kbuckets.remove(&key);
}
@ -976,7 +974,7 @@ pub(crate) async fn send_loop(udp: Arc<UdpSocket>, rx: EgressReceiver) {
}
/// Continuously awaits new incoming messages and sends them back through the channel.
pub(crate) async fn receive_loop(udp: Arc<UdpSocket>, tx: IngressSender, local_id: NodeId) {
pub(crate) async fn receive_loop(udp: Arc<UdpSocket>, tx: IngressSender, local_id: PeerId) {
loop {
let mut buf = [0; MAX_PACKET_SIZE];
let res = udp.recv_from(&mut buf).await;
@ -1010,7 +1008,7 @@ pub(crate) async fn receive_loop(udp: Arc<UdpSocket>, tx: IngressSender, local_i
/// The commands sent from the frontend to the service
enum Discv4Command {
Lookup { node_id: Option<NodeId>, tx: Option<NodeRecordSender> },
Lookup { node_id: Option<PeerId>, tx: Option<NodeRecordSender> },
Updates(OneshotSender<ReceiverStream<TableUpdate>>),
}
@ -1036,7 +1034,7 @@ struct PingRequest {
reason: PingReason,
}
/// Rotates the NodeId that is periodically looked up.
/// Rotates the PeerId that is periodically looked up.
///
/// By selecting different targets, the lookups will be seeded with different ALPHA seed nodes.
#[derive(Debug)]
@ -1066,13 +1064,13 @@ impl Default for LookupTargetRotator {
impl LookupTargetRotator {
/// this will return the next node id to lookup
fn next(&mut self, local: &NodeId) -> NodeId {
fn next(&mut self, local: &PeerId) -> PeerId {
self.counter += 1;
self.counter %= self.interval;
if self.counter == 0 {
return *local
}
NodeId::random()
PeerId::random()
}
}
@ -1087,7 +1085,7 @@ struct LookupContext {
impl LookupContext {
/// Create new context for a recursive lookup
fn new(
target: NodeId,
target: PeerId,
nearest_nodes: impl IntoIterator<Item = (Distance, NodeRecord)>,
listener: Option<NodeRecordSender>,
) -> Self {
@ -1107,7 +1105,7 @@ impl LookupContext {
}
/// Returns the target of this lookup
fn target(&self) -> NodeId {
fn target(&self) -> PeerId {
self.inner.target
}
@ -1132,7 +1130,7 @@ impl LookupContext {
}
/// Marks the node as queried
fn mark_queried(&self, id: NodeId) {
fn mark_queried(&self, id: PeerId) {
if let Some((_, node)) =
self.inner.closest_nodes.borrow_mut().iter_mut().find(|(_, node)| node.record.id == id)
{
@ -1141,7 +1139,7 @@ impl LookupContext {
}
/// Marks the node as responded
fn mark_responded(&self, id: NodeId) {
fn mark_responded(&self, id: PeerId) {
if let Some((_, node)) =
self.inner.closest_nodes.borrow_mut().iter_mut().find(|(_, node)| node.record.id == id)
{
@ -1159,7 +1157,7 @@ impl LookupContext {
unsafe impl Send for LookupContext {}
struct LookupContextInner {
target: NodeId,
target: PeerId,
/// The closest nodes
closest_nodes: RefCell<BTreeMap<Distance, QueryNode>>,
/// A listener for all the nodes retrieved in this lookup
@ -1249,7 +1247,7 @@ enum PingReason {
///
/// Once the expected PONG is received, the endpoint proof is complete and the find node can be
/// answered.
FindNode(NodeId, NodeEntryStatus),
FindNode(PeerId, NodeEntryStatus),
/// Part of a lookup to ensure endpoint is proven.
Lookup(NodeRecord, LookupContext),
}
@ -1260,7 +1258,7 @@ pub enum TableUpdate {
/// A new node was inserted to the table.
Added(NodeRecord),
/// Node that was removed from the table
Removed(NodeId),
Removed(PeerId),
/// A series of updates
Batch(Vec<TableUpdate>),
}
@ -1276,7 +1274,7 @@ mod tests {
#[test]
fn test_local_rotator() {
let id = NodeId::random();
let id = PeerId::random();
let mut rotator = LookupTargetRotator::local_only();
assert_eq!(rotator.next(&id), id);
assert_eq!(rotator.next(&id), id);
@ -1284,7 +1282,7 @@ mod tests {
#[test]
fn test_rotator() {
let id = NodeId::random();
let id = PeerId::random();
let mut rotator = LookupTargetRotator::default();
assert_eq!(rotator.next(&id), id);
assert_ne!(rotator.next(&id), id);
@ -1301,7 +1299,7 @@ mod tests {
let local_addr = service.local_addr();
for idx in 0..MAX_NODES_PING {
let node = NodeRecord::new(local_addr, NodeId::random());
let node = NodeRecord::new(local_addr, PeerId::random());
service.add_node(node);
assert!(service.pending_pings.contains_key(&node.id));
assert_eq!(service.pending_pings.len(), idx + 1);

View File

@ -6,7 +6,7 @@ use crate::{
node::NodeRecord,
proto::{FindNode, Message, Neighbours, NodeEndpoint, Packet, Ping, Pong},
receive_loop, send_loop, Discv4, Discv4Config, Discv4Service, EgressSender, IngressEvent,
IngressReceiver, NodeId, SAFE_MAX_DATAGRAM_NEIGHBOUR_RECORDS,
IngressReceiver, PeerId, SAFE_MAX_DATAGRAM_NEIGHBOUR_RECORDS,
};
use rand::{thread_rng, Rng, RngCore};
use reth_primitives::H256;
@ -40,8 +40,8 @@ pub struct MockDiscovery {
ingress: IngressReceiver,
/// Sender for sending outgoing messages
egress: EgressSender,
pending_pongs: HashSet<NodeId>,
pending_neighbours: HashMap<NodeId, Vec<NodeRecord>>,
pending_pongs: HashSet<PeerId>,
pending_neighbours: HashMap<PeerId, Vec<NodeRecord>>,
command_rx: mpsc::Receiver<MockCommand>,
}
@ -51,7 +51,7 @@ impl MockDiscovery {
let mut rng = thread_rng();
let socket = SocketAddr::from_str("0.0.0.0:0").unwrap();
let (secret_key, pk) = SECP256K1.generate_keypair(&mut rng);
let id = NodeId::from_slice(&pk.serialize_uncompressed()[1..]);
let id = PeerId::from_slice(&pk.serialize_uncompressed()[1..]);
let socket = Arc::new(UdpSocket::bind(socket).await?);
let local_addr = socket.local_addr()?;
let local_enr = NodeRecord {
@ -95,12 +95,12 @@ impl MockDiscovery {
}
/// Queue a pending pong.
pub fn queue_pong(&mut self, from: NodeId) {
pub fn queue_pong(&mut self, from: PeerId) {
self.pending_pongs.insert(from);
}
/// Queue a pending Neighbours response.
pub fn queue_neighbours(&mut self, target: NodeId, nodes: Vec<NodeRecord>) {
pub fn queue_neighbours(&mut self, target: PeerId, nodes: Vec<NodeRecord>) {
self.pending_neighbours.insert(target, nodes);
}
@ -195,8 +195,8 @@ pub enum MockEvent {
/// Command for interacting with the `MockDiscovery` service
pub enum MockCommand {
MockPong { node_id: NodeId },
MockNeighbours { target: NodeId, nodes: Vec<NodeRecord> },
MockPong { node_id: PeerId },
MockNeighbours { target: PeerId, nodes: Vec<NodeRecord> },
}
/// Creates a new testing instance for [`Discv4`] and its service
@ -209,7 +209,7 @@ pub async fn create_discv4_with_config(config: Discv4Config) -> (Discv4, Discv4S
let mut rng = thread_rng();
let socket = SocketAddr::from_str("0.0.0.0:0").unwrap();
let (secret_key, pk) = SECP256K1.generate_keypair(&mut rng);
let id = NodeId::from_slice(&pk.serialize_uncompressed()[1..]);
let id = PeerId::from_slice(&pk.serialize_uncompressed()[1..]);
let external_addr = public_ip::addr().await.unwrap_or_else(|| socket.ip());
let local_enr =
NodeRecord { address: external_addr, tcp_port: socket.port(), udp_port: socket.port(), id };
@ -231,21 +231,21 @@ pub fn rng_endpoint(rng: &mut impl Rng) -> NodeEndpoint {
pub fn rng_record(rng: &mut impl RngCore) -> NodeRecord {
let NodeEndpoint { address, udp_port, tcp_port } = rng_endpoint(rng);
NodeRecord { address, tcp_port, udp_port, id: NodeId::random() }
NodeRecord { address, tcp_port, udp_port, id: PeerId::random() }
}
pub fn rng_ipv6_record(rng: &mut impl RngCore) -> NodeRecord {
let mut ip = [0u8; 16];
rng.fill_bytes(&mut ip);
let address = IpAddr::V6(ip.into());
NodeRecord { address, tcp_port: rng.gen(), udp_port: rng.gen(), id: NodeId::random() }
NodeRecord { address, tcp_port: rng.gen(), udp_port: rng.gen(), id: PeerId::random() }
}
pub fn rng_ipv4_record(rng: &mut impl RngCore) -> NodeRecord {
let mut ip = [0u8; 4];
rng.fill_bytes(&mut ip);
let address = IpAddr::V4(ip.into());
NodeRecord { address, tcp_port: rng.gen(), udp_port: rng.gen(), id: NodeId::random() }
NodeRecord { address, tcp_port: rng.gen(), udp_port: rng.gen(), id: PeerId::random() }
}
pub fn rng_message(rng: &mut impl RngCore) -> Message {
@ -256,7 +256,7 @@ pub fn rng_message(rng: &mut impl RngCore) -> Message {
expire: rng.gen(),
}),
2 => Message::Pong(Pong { to: rng_endpoint(rng), echo: H256::random(), expire: rng.gen() }),
3 => Message::FindNode(FindNode { id: NodeId::random(), expire: rng.gen() }),
3 => Message::FindNode(FindNode { id: PeerId::random(), expire: rng.gen() }),
4 => {
let num: usize = rng.gen_range(1..=SAFE_MAX_DATAGRAM_NEIGHBOUR_RECORDS);
Message::Neighbours(Neighbours {

View File

@ -1,4 +1,4 @@
use crate::{proto::Octets, NodeId};
use crate::{proto::Octets, PeerId};
use bytes::{Buf, BufMut};
use generic_array::GenericArray;
use reth_primitives::keccak256;
@ -13,10 +13,10 @@ use url::{Host, Url};
/// The key type for the table.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) struct NodeKey(pub(crate) NodeId);
pub(crate) struct NodeKey(pub(crate) PeerId);
impl From<NodeId> for NodeKey {
fn from(value: NodeId) -> Self {
impl From<PeerId> for NodeKey {
fn from(value: PeerId) -> Self {
NodeKey(value)
}
}
@ -29,9 +29,9 @@ impl From<NodeKey> for discv5::Key<NodeKey> {
}
}
/// Converts a `NodeId` into the required `Key` type for the table
/// Converts a `PeerId` into the required `Key` type for the table
#[inline]
pub(crate) fn kad_key(node: NodeId) -> discv5::Key<NodeKey> {
pub(crate) fn kad_key(node: PeerId) -> discv5::Key<NodeKey> {
discv5::kbucket::Key::from(NodeKey::from(node))
}
@ -45,20 +45,20 @@ pub struct NodeRecord {
/// UDP discovery port.
pub udp_port: u16,
/// Public key of the discovery service
pub id: NodeId,
pub id: PeerId,
}
impl NodeRecord {
/// Derive the [`NodeRecord`] from the secret key and addr
pub fn from_secret_key(addr: SocketAddr, sk: &SecretKey) -> Self {
let pk = secp256k1::PublicKey::from_secret_key(SECP256K1, sk);
let id = NodeId::from_slice(&pk.serialize_uncompressed()[1..]);
let id = PeerId::from_slice(&pk.serialize_uncompressed()[1..]);
Self::new(addr, id)
}
/// Creates a new record
#[allow(unused)]
pub(crate) fn new(addr: SocketAddr, id: NodeId) -> Self {
pub(crate) fn new(addr: SocketAddr, id: PeerId) -> Self {
Self { address: addr.ip(), tcp_port: addr.port(), udp_port: addr.port(), id }
}
@ -112,7 +112,7 @@ impl FromStr for NodeRecord {
let id = url
.username()
.parse::<NodeId>()
.parse::<PeerId>()
.map_err(|e| NodeRecordParseError::InvalidId(e.to_string()))?;
Ok(Self { address, id, tcp_port: port, udp_port: port })
@ -126,7 +126,7 @@ impl Encodable for NodeRecord {
octets: Octets,
udp_port: u16,
tcp_port: u16,
id: NodeId,
id: PeerId,
}
let octets = match self.address {
@ -185,7 +185,7 @@ mod tests {
address: IpAddr::V4(ip.into()),
tcp_port: rng.gen(),
udp_port: rng.gen(),
id: NodeId::random(),
id: PeerId::random(),
};
let mut buf = BytesMut::new();
@ -206,7 +206,7 @@ mod tests {
address: IpAddr::V6(ip.into()),
tcp_port: rng.gen(),
udp_port: rng.gen(),
id: NodeId::random(),
id: PeerId::random(),
};
let mut buf = BytesMut::new();

View File

@ -1,6 +1,6 @@
#![allow(missing_docs)]
use crate::{error::DecodePacketError, node::NodeRecord, NodeId, MAX_PACKET_SIZE, MIN_PACKET_SIZE};
use crate::{error::DecodePacketError, node::NodeRecord, PeerId, MAX_PACKET_SIZE, MIN_PACKET_SIZE};
use bytes::{Buf, BufMut, Bytes, BytesMut};
use reth_primitives::{keccak256, H256};
use reth_rlp::{Decodable, DecodeError, Encodable, Header};
@ -136,7 +136,7 @@ impl Message {
let msg = secp256k1::Message::from_slice(keccak256(&packet[97..]).as_bytes())?;
let pk = SECP256K1.recover_ecdsa(&msg, &recoverable_sig)?;
let node_id = NodeId::from_slice(&pk.serialize_uncompressed()[1..]);
let node_id = PeerId::from_slice(&pk.serialize_uncompressed()[1..]);
let msg_type = packet[97];
let payload = &mut &packet[98..];
@ -156,7 +156,7 @@ impl Message {
#[derive(Debug)]
pub struct Packet {
pub msg: Message,
pub node_id: NodeId,
pub node_id: PeerId,
pub hash: H256,
}
@ -223,7 +223,7 @@ impl From<NodeRecord> for NodeEndpoint {
/// A [FindNode packet](https://github.com/ethereum/devp2p/blob/master/discv4.md#findnode-packet-0x03).).
#[derive(Clone, Copy, Debug, Eq, PartialEq, RlpEncodable, RlpDecodable)]
pub struct FindNode {
pub id: NodeId,
pub id: PeerId,
pub expire: u64,
}
@ -499,7 +499,7 @@ mod tests {
for _ in 0..100 {
let msg = rng_message(&mut rng);
let (secret_key, pk) = SECP256K1.generate_keypair(&mut rng);
let sender_id = NodeId::from_slice(&pk.serialize_uncompressed()[1..]);
let sender_id = PeerId::from_slice(&pk.serialize_uncompressed()[1..]);
let (buf, _) = msg.encode(&secret_key);

View File

@ -17,6 +17,9 @@ reth-ecies = { path = "../ecies" }
reth-primitives = { path = "../../primitives" }
reth-rlp = { path = "../../common/rlp", features = ["alloc", "derive", "std", "ethereum-types", "smol_str"] }
# used for Chain and builders
ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false }
#used for forkid
crc = "1"
maplit = "1"

View File

@ -0,0 +1,145 @@
//! Builder structs for [`Status`](crate::types::Status) and [`Hello`](crate::types::Hello)
//! messages.
use crate::{
capability::Capability,
p2pstream::{HelloMessage, ProtocolVersion},
EthVersion, Status,
};
use reth_primitives::{Chain, ForkId, PeerId, H256, U256};
/// Builder for [`Status`](crate::types::Status) messages.
///
/// # Example
/// ```
/// use reth_eth_wire::EthVersion;
/// use reth_primitives::{Chain, U256, H256, MAINNET_GENESIS, Hardfork};
/// use reth_eth_wire::types::Status;
///
/// // this is just an example status message!
/// let status = Status::builder()
/// .version(EthVersion::Eth66.into())
/// .chain(Chain::Named(ethers_core::types::Chain::Mainnet))
/// .total_difficulty(U256::from(100))
/// .blockhash(H256::from(MAINNET_GENESIS))
/// .genesis(H256::from(MAINNET_GENESIS))
/// .forkid(Hardfork::Latest.fork_id())
/// .build();
///
/// assert_eq!(
/// status,
/// Status {
/// version: EthVersion::Eth66.into(),
/// chain: Chain::Named(ethers_core::types::Chain::Mainnet),
/// total_difficulty: U256::from(100),
/// blockhash: H256::from(MAINNET_GENESIS),
/// genesis: H256::from(MAINNET_GENESIS),
/// forkid: Hardfork::Latest.fork_id(),
/// }
/// );
/// ```
#[derive(Debug, Default)]
pub struct StatusBuilder {
status: Status,
}
impl StatusBuilder {
/// Consumes the type and creates the actual [`Status`](crate::types::Status) message.
pub fn build(self) -> Status {
self.status
}
/// Sets the protocol version.
pub fn version(mut self, version: u8) -> Self {
self.status.version = version;
self
}
/// Sets the chain id.
pub fn chain(mut self, chain: Chain) -> Self {
self.status.chain = chain;
self
}
/// Sets the total difficulty.
pub fn total_difficulty(mut self, total_difficulty: U256) -> Self {
self.status.total_difficulty = total_difficulty;
self
}
/// Sets the block hash.
pub fn blockhash(mut self, blockhash: H256) -> Self {
self.status.blockhash = blockhash;
self
}
/// Sets the genesis hash.
pub fn genesis(mut self, genesis: H256) -> Self {
self.status.genesis = genesis;
self
}
/// Sets the fork id.
pub fn forkid(mut self, forkid: ForkId) -> Self {
self.status.forkid = forkid;
self
}
}
/// Builder for [`Hello`](crate::types::Hello) messages.
pub struct HelloBuilder {
hello: HelloMessage,
}
impl HelloBuilder {
/// Creates a new [`HelloBuilder`](crate::builder::HelloBuilder) with default [`Hello`] values,
/// and a `PeerId` corresponding to the given pubkey.
pub fn new(pubkey: PeerId) -> Self {
Self {
hello: HelloMessage {
protocol_version: ProtocolVersion::V5,
// TODO: proper client versioning
client_version: "Ethereum/1.0.0".to_string(),
capabilities: vec![EthVersion::Eth67.into()],
// TODO: default port config
port: 30303,
id: pubkey,
},
}
}
/// Consumes the type and creates the actual [`Hello`](crate::types::Hello) message.
pub fn build(self) -> HelloMessage {
self.hello
}
/// Sets the protocol version.
pub fn protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
self.hello.protocol_version = protocol_version;
self
}
/// Sets the client version.
pub fn client_version(mut self, client_version: String) -> Self {
self.hello.client_version = client_version;
self
}
/// Sets the capabilities.
pub fn capabilities(mut self, capabilities: Vec<Capability>) -> Self {
self.hello.capabilities = capabilities;
self
}
/// Sets the port.
pub fn port(mut self, port: u16) -> Self {
self.hello.port = port;
self
}
/// Sets the node id.
pub fn id(mut self, id: PeerId) -> Self {
self.hello.id = id;
self
}
}

View File

@ -91,6 +91,16 @@ impl Capabilities {
}
}
impl From<Vec<Capability>> for Capabilities {
fn from(value: Vec<Capability>) -> Self {
Self {
eth_66: value.iter().any(Capability::is_eth_v66),
eth_67: value.iter().any(Capability::is_eth_v67),
inner: value,
}
}
}
impl Encodable for Capabilities {
fn encode(&self, out: &mut dyn BufMut) {
self.inner.encode(out)

View File

@ -1,9 +1,9 @@
//! Error cases when handling a [`crate::EthStream`]
use std::io;
use reth_primitives::{Chain, H256};
use reth_primitives::{Chain, ValidationError, H256};
use crate::{capability::SharedCapabilityError, types::forkid::ValidationError};
use crate::capability::SharedCapabilityError;
/// Errors when sending/receiving messages
#[derive(thiserror::Error, Debug)]

View File

@ -1,10 +1,11 @@
use crate::{
error::{EthStreamError, HandshakeError},
types::{forkid::ForkFilter, EthMessage, ProtocolMessage, Status},
types::{EthMessage, ProtocolMessage, Status},
};
use bytes::{Bytes, BytesMut};
use futures::{ready, Sink, SinkExt, StreamExt};
use pin_project::pin_project;
use reth_primitives::ForkFilter;
use reth_rlp::{Decodable, Encodable};
use std::{
pin::Pin,
@ -117,6 +118,7 @@ where
/// An `EthStream` wraps over any `Stream` that yields bytes and makes it
/// compatible with eth-networking protocol messages, which get RLP encoded/decoded.
#[pin_project]
#[derive(Debug)]
pub struct EthStream<S> {
#[pin]
inner: S,
@ -203,7 +205,7 @@ where
mod tests {
use crate::{
p2pstream::{HelloMessage, ProtocolVersion, UnauthedP2PStream},
types::{broadcast::BlockHashNumber, forkid::ForkFilter, EthMessage, Status},
types::{broadcast::BlockHashNumber, EthMessage, Status},
EthStream, PassthroughCodec,
};
use futures::{SinkExt, StreamExt};
@ -214,7 +216,7 @@ mod tests {
use crate::{capability::Capability, types::EthVersion};
use ethers_core::types::Chain;
use reth_primitives::{H256, U256};
use reth_primitives::{ForkFilter, H256, U256};
use super::UnauthedEthStream;

View File

@ -11,12 +11,17 @@
pub use tokio_util::codec::{
LengthDelimitedCodec as PassthroughCodec, LengthDelimitedCodecError as PassthroughCodecError,
};
pub mod builder;
pub mod capability;
pub mod error;
mod ethstream;
mod p2pstream;
mod pinger;
pub use builder::*;
pub mod types;
pub use types::*;
pub use ethstream::{EthStream, UnauthedEthStream};
pub use crate::{
ethstream::{EthStream, UnauthedEthStream},
p2pstream::{HelloMessage, P2PStream, UnauthedP2PStream},
};

View File

@ -138,6 +138,7 @@ where
/// A P2PStream wraps over any `Stream` that yields bytes and makes it compatible with `p2p`
/// protocol messages.
#[pin_project]
#[derive(Debug)]
pub struct P2PStream<S> {
#[pin]
inner: S,

View File

@ -6,8 +6,6 @@ pub use status::Status;
pub mod version;
pub use version::EthVersion;
pub mod forkid;
pub mod message;
pub use message::{EthMessage, EthMessageID, ProtocolMessage};

View File

@ -1,5 +1,6 @@
use super::forkid::ForkId;
use reth_primitives::{Chain, H256, U256};
use crate::{EthVersion, StatusBuilder};
use reth_primitives::{Chain, ForkId, Hardfork, H256, MAINNET_GENESIS, U256};
use reth_rlp::{RlpDecodable, RlpEncodable};
use std::fmt::{Debug, Display};
@ -37,6 +38,13 @@ pub struct Status {
pub forkid: ForkId,
}
impl Status {
/// Helper for returning a builder for the status message.
pub fn builder() -> StatusBuilder {
Default::default()
}
}
impl Display for Status {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let hexed_blockhash = hex::encode(self.blockhash);
@ -84,18 +92,28 @@ impl Debug for Status {
}
}
impl Default for Status {
fn default() -> Self {
Status {
version: EthVersion::Eth67 as u8,
chain: Chain::Named(ethers_core::types::Chain::Mainnet),
total_difficulty: U256::zero(),
blockhash: MAINNET_GENESIS,
genesis: MAINNET_GENESIS,
forkid: Hardfork::Homestead.fork_id(),
}
}
}
#[cfg(test)]
mod tests {
use ethers_core::types::Chain as NamedChain;
use hex_literal::hex;
use reth_primitives::{Chain, H256, U256};
use reth_primitives::{Chain, ForkHash, ForkId, H256, U256};
use reth_rlp::{Decodable, Encodable};
use std::str::FromStr;
use crate::types::{
forkid::{ForkHash, ForkId},
EthVersion, Status,
};
use crate::types::{EthVersion, Status};
#[test]
fn encode_eth_status_message() {

View File

@ -1,7 +1,6 @@
use crate::{peers::PeersConfig, session::SessionsConfig};
use reth_discv4::{Discv4Config, Discv4ConfigBuilder, DEFAULT_DISCOVERY_PORT};
use reth_eth_wire::forkid::ForkId;
use reth_primitives::{Chain, H256};
use reth_primitives::{Chain, ForkId, H256};
use secp256k1::SecretKey;
use std::{
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
@ -76,8 +75,12 @@ pub struct NetworkConfigBuilder<C> {
peers_config: Option<PeersConfig>,
/// How to configure the sessions manager
sessions_config: Option<SessionsConfig>,
/// A fork identifier as defined by EIP-2124.
/// Serves as the chain compatibility identifier.
fork_id: Option<ForkId>,
/// The network's chain id
chain: Chain,
/// Network genesis hash
genesis_hash: H256,
}

View File

@ -1,8 +1,9 @@
//! Discovery support for the network.
use crate::{error::NetworkError, NodeId};
use crate::error::NetworkError;
use futures::StreamExt;
use reth_discv4::{Discv4, Discv4Config, NodeRecord, TableUpdate};
use reth_primitives::PeerId;
use secp256k1::SecretKey;
use std::{
collections::{hash_map::Entry, HashMap, VecDeque},
@ -19,7 +20,7 @@ pub struct Discovery {
/// All nodes discovered via discovery protocol.
///
/// These nodes can be ephemeral and are updated via the discovery protocol.
discovered_nodes: HashMap<NodeId, SocketAddr>,
discovered_nodes: HashMap<PeerId, SocketAddr>,
/// Local ENR of the discovery service.
local_enr: NodeRecord,
/// Handler to interact with the Discovery v4 service
@ -66,12 +67,12 @@ impl Discovery {
}
/// Returns the id with which the local identifies itself in the network
pub(crate) fn local_id(&self) -> NodeId {
pub(crate) fn local_id(&self) -> PeerId {
self.local_enr.id
}
/// Manually adds an address to the set.
pub(crate) fn add_known_address(&mut self, node_id: NodeId, addr: SocketAddr) {
pub(crate) fn add_known_address(&mut self, node_id: PeerId, addr: SocketAddr) {
self.on_discv4_update(TableUpdate::Added(NodeRecord {
address: addr.ip(),
tcp_port: addr.port(),
@ -81,7 +82,7 @@ impl Discovery {
}
/// Returns all nodes we know exist in the network.
pub fn known_nodes(&mut self) -> &HashMap<NodeId, SocketAddr> {
pub fn known_nodes(&mut self) -> &HashMap<PeerId, SocketAddr> {
&self.discovered_nodes
}
@ -131,7 +132,7 @@ impl Discovery {
/// Events produced by the [`Discovery`] manager.
pub enum DiscoveryEvent {
/// A new node was discovered
Discovered(NodeId, SocketAddr),
Discovered(PeerId, SocketAddr),
}
#[cfg(test)]

View File

@ -1,10 +1,10 @@
//! Fetch data from the network.
use crate::{message::BlockRequest, NodeId};
use crate::message::BlockRequest;
use futures::StreamExt;
use reth_eth_wire::{BlockBody, EthMessage};
use reth_interfaces::p2p::{error::RequestResult, headers::client::HeadersRequest};
use reth_primitives::{Header, H256, U256};
use reth_primitives::{Header, PeerId, H256, U256};
use std::{
collections::{HashMap, VecDeque},
task::{Context, Poll},
@ -19,9 +19,9 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
/// peers and sends the response once ready.
pub struct StateFetcher {
/// Currently active [`GetBlockHeaders`] requests
inflight_headers_requests: HashMap<NodeId, Request<HeadersRequest, RequestResult<Vec<Header>>>>,
inflight_headers_requests: HashMap<PeerId, Request<HeadersRequest, RequestResult<Vec<Header>>>>,
/// The list of available peers for requests.
peers: HashMap<NodeId, Peer>,
peers: HashMap<PeerId, Peer>,
/// Requests queued for processing
queued_requests: VecDeque<DownloadRequest>,
/// Receiver for new incoming download requests
@ -34,13 +34,13 @@ pub struct StateFetcher {
impl StateFetcher {
/// Invoked when connected to a new peer.
pub(crate) fn new_connected_peer(&mut self, _node_id: NodeId, _best_hash: H256) {}
pub(crate) fn new_connected_peer(&mut self, _node_id: PeerId, _best_hash: H256) {}
/// Invoked when an active session was closed.
pub(crate) fn on_session_closed(&mut self, _peer: &NodeId) {}
pub(crate) fn on_session_closed(&mut self, _peer: &PeerId) {}
/// Invoked when an active session is about to be disconnected.
pub(crate) fn on_pending_disconnect(&mut self, _peer: &NodeId) {}
pub(crate) fn on_pending_disconnect(&mut self, _peer: &PeerId) {}
/// Returns the next action to return
fn poll_action(&mut self) -> Option<FetchAction> {
@ -94,7 +94,7 @@ impl StateFetcher {
/// Called on a `GetBlockHeaders` response from a peer
pub(crate) fn on_block_headers_response(
&mut self,
_peer: NodeId,
_peer: PeerId,
_res: RequestResult<Vec<Header>>,
) -> Option<BlockResponseOutcome> {
None
@ -103,7 +103,7 @@ impl StateFetcher {
/// Called on a `GetBlockBodies` response from a peer
pub(crate) fn on_block_bodies_response(
&mut self,
_peer: NodeId,
_peer: PeerId,
_res: RequestResult<Vec<BlockBody>>,
) -> Option<BlockResponseOutcome> {
None
@ -189,7 +189,7 @@ enum DownloadRequest {
pub(crate) enum FetchAction {
/// Dispatch an eth request to the given peer.
EthRequest {
node_id: NodeId,
node_id: PeerId,
/// The request to send
request: EthMessage,
},
@ -201,8 +201,8 @@ pub(crate) enum FetchAction {
#[derive(Debug)]
pub(crate) enum BlockResponseOutcome {
/// Continue with another request to the peer.
Request(NodeId, BlockRequest),
Request(PeerId, BlockRequest),
/// How to handle a bad response
// TODO this should include some form of reputation change
BadResponse(NodeId),
BadResponse(PeerId),
}

View File

@ -5,7 +5,7 @@
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]
// TODO remove later
#![allow(dead_code)]
#![allow(dead_code, clippy::too_many_arguments)]
//! reth P2P networking.
//!
@ -29,9 +29,6 @@ mod state;
mod swarm;
mod transactions;
/// Identifier for a unique node
pub type NodeId = reth_discv4::NodeId;
pub use config::NetworkConfig;
pub use manager::NetworkManager;
pub use network::NetworkHandle;

View File

@ -25,7 +25,6 @@ use crate::{
session::SessionManager,
state::NetworkState,
swarm::{Swarm, SwarmEvent},
NodeId,
};
use futures::{Future, StreamExt};
use parking_lot::Mutex;
@ -34,6 +33,7 @@ use reth_eth_wire::{
EthMessage,
};
use reth_interfaces::provider::BlockProvider;
use reth_primitives::PeerId;
use std::{
net::SocketAddr,
pin::Pin,
@ -88,8 +88,8 @@ pub struct NetworkManager<C> {
/// This is updated via internal events and shared via `Arc` with the [`NetworkHandle`]
/// Updated by the `NetworkWorker` and loaded by the `NetworkService`.
num_active_peers: Arc<AtomicUsize>,
/// Local copy of the `NodeId` of the local node.
local_node_id: NodeId,
/// Local copy of the `PeerId` of the local node.
local_node_id: PeerId,
}
// === impl NetworkManager ===
@ -163,7 +163,7 @@ where
/// Event hook for an unexpected message from the peer.
fn on_invalid_message(
&self,
node_id: NodeId,
node_id: PeerId,
_capabilities: Arc<Capabilities>,
_message: CapabilityMessage,
) {
@ -172,7 +172,7 @@ where
}
/// Handles a received [`CapabilityMessage`] from the peer.
fn on_capability_message(&mut self, _node_id: NodeId, msg: CapabilityMessage) {
fn on_capability_message(&mut self, _node_id: PeerId, msg: CapabilityMessage) {
match msg {
CapabilityMessage::Eth(eth) => {
match eth {
@ -299,7 +299,7 @@ where
/// Events emitted by the network that are of interest for subscribers.
#[derive(Debug, Clone)]
pub enum NetworkEvent {
EthMessage { node_id: NodeId, message: EthMessage },
EthMessage { node_id: PeerId, message: EthMessage },
}
/// Bundles all listeners for [`NetworkEvent`]s.

View File

@ -11,10 +11,9 @@ use reth_eth_wire::{
};
use std::task::{ready, Context, Poll};
use crate::NodeId;
use reth_eth_wire::capability::CapabilityMessage;
use reth_interfaces::p2p::error::RequestResult;
use reth_primitives::{Header, Receipt, TransactionSigned};
use reth_primitives::{Header, PeerId, Receipt, TransactionSigned};
use tokio::sync::{mpsc, mpsc::error::TrySendError, oneshot};
/// Represents all messages that can be sent to a peer session
@ -180,7 +179,7 @@ impl PeerResponseResult {
#[derive(Debug, Clone)]
pub struct PeerRequestSender {
/// id of the remote node.
pub(crate) peer: NodeId,
pub(crate) peer: PeerId,
/// The Sender half connected to a session.
pub(crate) to_session_tx: mpsc::Sender<PeerRequest>,
}

View File

@ -1,6 +1,6 @@
use crate::{manager::NetworkEvent, peers::PeersHandle, NodeId};
use crate::{manager::NetworkEvent, peers::PeersHandle};
use parking_lot::Mutex;
use reth_primitives::{H256, U256};
use reth_primitives::{PeerId, H256, U256};
use std::{
net::SocketAddr,
sync::{atomic::AtomicUsize, Arc},
@ -24,7 +24,7 @@ impl NetworkHandle {
num_active_peers: Arc<AtomicUsize>,
listener_address: Arc<Mutex<SocketAddr>>,
to_manager_tx: UnboundedSender<NetworkHandleMessage>,
local_node_id: NodeId,
local_node_id: PeerId,
peers: PeersHandle,
) -> Self {
let inner = NetworkInner {
@ -57,7 +57,7 @@ struct NetworkInner {
/// The local address that accepts incoming connections.
listener_address: Arc<Mutex<SocketAddr>>,
/// The identifier used by this node.
local_node_id: NodeId,
local_node_id: PeerId,
/// Access to the all the nodes
peers: PeersHandle, // TODO need something to access
}

View File

@ -1,5 +1,5 @@
use futures::StreamExt;
use reth_discv4::NodeId;
use reth_primitives::PeerId;
use std::{
collections::{hash_map::Entry, HashMap, VecDeque},
net::SocketAddr,
@ -32,7 +32,7 @@ pub struct PeersHandle {
/// The [`PeersManager`] will be notified on peer related changes
pub(crate) struct PeersManager {
/// All peers known to the network
peers: HashMap<NodeId, Peer>,
peers: HashMap<PeerId, Peer>,
/// Copy of the receiver half, so new [`PeersHandle`] can be created on demand.
manager_tx: mpsc::UnboundedSender<PeerCommand>,
/// Receiver half of the command channel.
@ -74,7 +74,7 @@ impl PeersManager {
///
/// If the reputation of the peer is below the `BANNED_REPUTATION` threshold, a disconnect will
/// be scheduled.
pub(crate) fn on_active_session(&mut self, peer_id: NodeId, addr: SocketAddr) {
pub(crate) fn on_active_session(&mut self, peer_id: PeerId, addr: SocketAddr) {
match self.peers.entry(peer_id) {
Entry::Occupied(mut entry) => {
let value = entry.get_mut();
@ -96,7 +96,7 @@ impl PeersManager {
/// Called when a session to a peer was disconnected.
///
/// Accepts an additional [`ReputationChange`] value to apply to the peer.
pub(crate) fn on_disconnected(&mut self, peer: NodeId, reputation_change: ReputationChange) {
pub(crate) fn on_disconnected(&mut self, peer: PeerId, reputation_change: ReputationChange) {
if let Some(mut peer) = self.peers.get_mut(&peer) {
self.connection_info.decr_state(peer.state);
peer.state = PeerConnectionState::Idle;
@ -108,7 +108,7 @@ impl PeersManager {
///
/// If the peer already exists, then the address will e updated. If the addresses differ, the
/// old address is returned
pub(crate) fn add_discovered_node(&mut self, peer_id: NodeId, addr: SocketAddr) {
pub(crate) fn add_discovered_node(&mut self, peer_id: PeerId, addr: SocketAddr) {
match self.peers.entry(peer_id) {
Entry::Occupied(mut entry) => {
let node = entry.get_mut();
@ -121,7 +121,7 @@ impl PeersManager {
}
/// Removes the tracked node from the set.
pub(crate) fn remove_discovered_node(&mut self, peer_id: NodeId) {
pub(crate) fn remove_discovered_node(&mut self, peer_id: PeerId) {
if let Some(entry) = self.peers.remove(&peer_id) {
if entry.state.is_connected() {
self.connection_info.decr_state(entry.state);
@ -133,11 +133,11 @@ impl PeersManager {
/// Returns the idle peer with the highest reputation.
///
/// Returns `None` if no peer is available.
fn best_unconnected(&mut self) -> Option<(NodeId, &mut Peer)> {
fn best_unconnected(&mut self) -> Option<(PeerId, &mut Peer)> {
self.peers
.iter_mut()
.filter(|(_, peer)| peer.state.is_unconnected())
.fold(None::<(&NodeId, &mut Peer)>, |mut best_peer, candidate| {
.fold(None::<(&PeerId, &mut Peer)>, |mut best_peer, candidate| {
if let Some(best_peer) = best_peer.take() {
if best_peer.1.reputation >= candidate.1.reputation {
return Some(best_peer)
@ -331,14 +331,14 @@ pub(crate) enum PeerCommand {
/// Command for manually add
Add {
/// Identifier of the peer.
peer_id: NodeId,
peer_id: PeerId,
/// The address of the peer
addr: SocketAddr,
},
/// Remove a peer from the set
///
/// If currently connected this will disconnect the sessin
Remove(NodeId),
Remove(PeerId),
}
/// Actions the peer manager can trigger.
@ -347,17 +347,17 @@ pub enum PeerAction {
/// Start a new connection to a peer.
Connect {
/// The peer to connect to.
peer_id: NodeId,
peer_id: PeerId,
/// Where to reach the node
remote_addr: SocketAddr,
},
/// Disconnect an existing connection.
Disconnect { peer_id: NodeId },
Disconnect { peer_id: PeerId },
/// Disconnect an existing incoming connection, because the peers reputation is below the
/// banned threshold.
DisconnectBannedIncoming {
/// Peer id of the established connection.
peer_id: NodeId,
peer_id: PeerId,
},
}

View File

@ -6,13 +6,16 @@ use crate::{
handle::{ActiveSessionMessage, SessionCommand},
SessionId,
},
NodeId,
};
use fnv::FnvHashMap;
use futures::{stream::Fuse, Sink, Stream};
use pin_project::pin_project;
use reth_ecies::stream::ECIESStream;
use reth_eth_wire::capability::{Capabilities, CapabilityMessage};
use reth_eth_wire::{
capability::{Capabilities, CapabilityMessage},
EthStream, P2PStream,
};
use reth_primitives::PeerId;
use std::{
collections::VecDeque,
future::Future,
@ -31,9 +34,9 @@ pub(crate) struct ActiveSession {
pub(crate) next_id: usize,
/// The underlying connection.
#[pin]
pub(crate) conn: ECIESStream<TcpStream>,
pub(crate) conn: EthStream<P2PStream<ECIESStream<TcpStream>>>,
/// Identifier of the node we're connected to.
pub(crate) remote_node_id: NodeId,
pub(crate) remote_node_id: PeerId,
/// All capabilities the peer announced
pub(crate) remote_capabilities: Arc<Capabilities>,
/// Internal identifier of this session

View File

@ -1,13 +1,12 @@
//! Session handles
use crate::{
session::{Direction, SessionId},
NodeId,
};
use crate::session::{Direction, SessionId};
use reth_ecies::{stream::ECIESStream, ECIESError};
use reth_eth_wire::{
capability::{Capabilities, CapabilityMessage},
Status,
error::EthStreamError,
EthStream, P2PStream, Status,
};
use reth_primitives::PeerId;
use std::{io, net::SocketAddr, sync::Arc, time::Instant};
use tokio::{
net::TcpStream,
@ -33,7 +32,7 @@ pub(crate) struct ActiveSessionHandle {
/// The assigned id for this session
pub(crate) session_id: SessionId,
/// The identifier of the remote peer
pub(crate) remote_id: NodeId,
pub(crate) remote_id: PeerId,
/// The timestamp when the session has been established.
pub(crate) established: Instant,
/// Announced capabilities of the peer.
@ -65,23 +64,24 @@ pub(crate) enum PendingSessionEvent {
Established {
session_id: SessionId,
remote_addr: SocketAddr,
node_id: NodeId,
/// The remote node's public key
node_id: PeerId,
capabilities: Arc<Capabilities>,
status: Status,
conn: ECIESStream<TcpStream>,
conn: EthStream<P2PStream<ECIESStream<TcpStream>>>,
},
/// Handshake unsuccessful, session was disconnected.
Disconnected {
remote_addr: SocketAddr,
session_id: SessionId,
direction: Direction,
error: Option<ECIESError>,
error: Option<EthStreamError>,
},
/// Thrown when unable to establish a [`TcpStream`].
OutgoingConnectionError {
remote_addr: SocketAddr,
session_id: SessionId,
node_id: NodeId,
node_id: PeerId,
error: io::Error,
},
/// Thrown when authentication via Ecies failed.
@ -101,18 +101,18 @@ pub(crate) enum SessionCommand {
#[derive(Debug)]
pub(crate) enum ActiveSessionMessage {
/// Session disconnected.
Closed { node_id: NodeId, remote_addr: SocketAddr },
Closed { node_id: PeerId, remote_addr: SocketAddr },
/// A session received a valid message via RLPx.
ValidMessage {
/// Identifier of the remote peer.
node_id: NodeId,
node_id: PeerId,
/// Message received from the peer.
message: CapabilityMessage,
},
/// Received a message that does not match the announced capabilities of the peer.
InvalidMessage {
/// Identifier of the remote peer.
node_id: NodeId,
node_id: PeerId,
/// Announced capabilities of the remote peer.
capabilities: Arc<Capabilities>,
/// Message received from the peer.

View File

@ -1,21 +1,20 @@
//! Support for handling peer sessions.
pub use crate::message::PeerRequestSender;
use crate::{
session::{
use crate::session::{
active::ActiveSession,
handle::{
ActiveSessionHandle, ActiveSessionMessage, PendingSessionEvent, PendingSessionHandle,
},
},
NodeId,
};
use fnv::FnvHashMap;
use futures::{future::Either, io, FutureExt, StreamExt};
use reth_ecies::{stream::ECIESStream, ECIESError};
use reth_ecies::stream::ECIESStream;
use reth_eth_wire::{
capability::{Capabilities, CapabilityMessage},
Status, UnauthedEthStream,
error::EthStreamError,
HelloBuilder, HelloMessage, Status, StatusBuilder, UnauthedEthStream, UnauthedP2PStream,
};
use reth_primitives::{ForkFilter, Hardfork, PeerId};
use secp256k1::{SecretKey, SECP256K1};
use std::{
collections::HashMap,
@ -48,7 +47,13 @@ pub(crate) struct SessionManager {
/// The secret key used for authenticating sessions.
secret_key: SecretKey,
/// The node id of node
node_id: NodeId,
node_id: PeerId,
/// The `Status` message to send to peers.
status: Status,
/// THe `Hello` message to send to peers.
hello: HelloMessage,
/// The [`ForkFilter`] used to validate the peer's `Status` message.
fork_filter: ForkFilter,
/// Size of the command buffer per session.
session_command_buffer: usize,
/// All spawned session tasks.
@ -61,7 +66,7 @@ pub(crate) struct SessionManager {
/// session is authenticated, it can be moved to the `active_session` set.
pending_sessions: FnvHashMap<SessionId, PendingSessionHandle>,
/// All active sessions that are ready to exchange messages.
active_sessions: HashMap<NodeId, ActiveSessionHandle>,
active_sessions: HashMap<PeerId, ActiveSessionHandle>,
/// The original Sender half of the [`PendingSessionEvent`] channel.
///
/// When a new (pending) session is created, the corresponding [`PendingSessionHandle`] will
@ -87,12 +92,21 @@ impl SessionManager {
let (active_session_tx, active_session_rx) = mpsc::channel(config.session_event_buffer);
let pk = secret_key.public_key(SECP256K1);
let node_id = NodeId::from_slice(&pk.serialize_uncompressed()[1..]);
let node_id = PeerId::from_slice(&pk.serialize_uncompressed()[1..]);
// TODO: make sure this is the right place to put these builders - maybe per-Network rather
// than per-Session?
let hello = HelloBuilder::new(node_id).build();
let status = StatusBuilder::default().build();
let fork_filter = Hardfork::Frontier.fork_filter();
Self {
next_id: 0,
secret_key,
node_id,
status,
hello,
fork_filter,
session_command_buffer: config.session_command_buffer,
spawned_tasks: Default::default(),
pending_sessions: Default::default(),
@ -139,6 +153,9 @@ impl SessionManager {
pending_events,
remote_addr,
self.secret_key,
self.hello.clone(),
self.status,
self.fork_filter.clone(),
));
let handle = PendingSessionHandle { disconnect_tx };
@ -147,7 +164,7 @@ impl SessionManager {
}
/// Starts a new pending session from the local node to the given remote node.
pub(crate) fn dial_outbound(&mut self, remote_addr: SocketAddr, remote_node_id: NodeId) {
pub(crate) fn dial_outbound(&mut self, remote_addr: SocketAddr, remote_node_id: PeerId) {
let session_id = self.next_id();
let (disconnect_tx, disconnect_rx) = oneshot::channel();
let pending_events = self.pending_sessions_tx.clone();
@ -158,6 +175,9 @@ impl SessionManager {
remote_addr,
remote_node_id,
self.secret_key,
self.hello.clone(),
self.status,
self.fork_filter.clone(),
));
let handle = PendingSessionHandle { disconnect_tx };
@ -168,7 +188,7 @@ impl SessionManager {
///
/// This will trigger the disconnect on the session task to gracefully terminate. The result
/// will be picked up by the receiver.
pub(crate) fn disconnect(&self, node: NodeId) {
pub(crate) fn disconnect(&self, node: PeerId) {
if let Some(session) = self.active_sessions.get(&node) {
session.disconnect();
}
@ -376,7 +396,7 @@ pub(crate) enum SessionEvent {
///
/// This session is now able to exchange data.
SessionEstablished {
node_id: NodeId,
node_id: PeerId,
remote_addr: SocketAddr,
capabilities: Arc<Capabilities>,
status: Status,
@ -384,30 +404,30 @@ pub(crate) enum SessionEvent {
},
/// A session received a valid message via RLPx.
ValidMessage {
node_id: NodeId,
node_id: PeerId,
/// Message received from the peer.
message: CapabilityMessage,
},
/// Received a message that does not match the announced capabilities of the peer.
InvalidMessage {
node_id: NodeId,
node_id: PeerId,
/// Announced capabilities of the remote peer.
capabilities: Arc<Capabilities>,
/// Message received from the peer.
message: CapabilityMessage,
},
/// Closed an incoming pending session during authentication.
IncomingPendingSessionClosed { remote_addr: SocketAddr, error: Option<ECIESError> },
IncomingPendingSessionClosed { remote_addr: SocketAddr, error: Option<EthStreamError> },
/// Closed an outgoing pending session during authentication.
OutgoingPendingSessionClosed {
remote_addr: SocketAddr,
node_id: NodeId,
error: Option<ECIESError>,
node_id: PeerId,
error: Option<EthStreamError>,
},
/// Failed to establish a tcp stream
OutgoingConnectionError { remote_addr: SocketAddr, node_id: NodeId, error: io::Error },
OutgoingConnectionError { remote_addr: SocketAddr, node_id: PeerId, error: io::Error },
/// Active session was disconnected.
Disconnected { node_id: NodeId, remote_addr: SocketAddr },
Disconnected { node_id: PeerId, remote_addr: SocketAddr },
}
/// The error thrown when the max configured limit has been reached and no more connections are
@ -426,6 +446,9 @@ async fn start_pending_incoming_session(
events: mpsc::Sender<PendingSessionEvent>,
remote_addr: SocketAddr,
secret_key: SecretKey,
hello: HelloMessage,
status: Status,
fork_filter: ForkFilter,
) {
authenticate(
disconnect_rx,
@ -435,6 +458,9 @@ async fn start_pending_incoming_session(
remote_addr,
secret_key,
Direction::Incoming,
hello,
status,
fork_filter,
)
.await
}
@ -446,8 +472,11 @@ async fn start_pending_outbound_session(
events: mpsc::Sender<PendingSessionEvent>,
session_id: SessionId,
remote_addr: SocketAddr,
remote_node_id: NodeId,
remote_node_id: PeerId,
secret_key: SecretKey,
hello: HelloMessage,
status: Status,
fork_filter: ForkFilter,
) {
let stream = match TcpStream::connect(remote_addr).await {
Ok(stream) => stream,
@ -471,6 +500,9 @@ async fn start_pending_outbound_session(
remote_addr,
secret_key,
Direction::Outgoing(remote_node_id),
hello,
status,
fork_filter,
)
.await
}
@ -481,7 +513,7 @@ pub(crate) enum Direction {
/// Incoming connection.
Incoming,
/// Outgoing connection to a specific node.
Outgoing(NodeId),
Outgoing(PeerId),
}
async fn authenticate(
@ -492,6 +524,9 @@ async fn authenticate(
remote_addr: SocketAddr,
secret_key: SecretKey,
direction: Direction,
hello: HelloMessage,
status: Status,
fork_filter: ForkFilter,
) {
let stream = match direction {
Direction::Incoming => match ECIESStream::incoming(stream, secret_key).await {
@ -520,8 +555,17 @@ async fn authenticate(
}
};
let unauthed = UnauthedEthStream::new(stream);
let auth = authenticate_stream(unauthed, session_id, remote_addr, direction).boxed();
let unauthed = UnauthedP2PStream::new(stream);
let auth = authenticate_stream(
unauthed,
session_id,
remote_addr,
direction,
hello,
status,
fork_filter,
)
.boxed();
match futures::future::select(disconnect_rx, auth).await {
Either::Left((_, _)) => {
@ -544,10 +588,47 @@ async fn authenticate(
///
/// On Success return the authenticated stream as [`PendingSessionEvent`]
async fn authenticate_stream(
_stream: UnauthedEthStream<ECIESStream<TcpStream>>,
_session_id: SessionId,
_remote_addr: SocketAddr,
_direction: Direction,
stream: UnauthedP2PStream<ECIESStream<TcpStream>>,
session_id: SessionId,
remote_addr: SocketAddr,
direction: Direction,
hello: HelloMessage,
status: Status,
fork_filter: ForkFilter,
) -> PendingSessionEvent {
todo!()
// conduct the p2p handshake and return the authenticated stream
let (p2p_stream, their_hello) = match stream.handshake(hello).await {
Ok(stream_res) => stream_res,
Err(err) => {
return PendingSessionEvent::Disconnected {
remote_addr,
session_id,
direction,
error: Some(err.into()),
}
}
};
// if the hello handshake was successful we can try status handshake
let eth_unauthed = UnauthedEthStream::new(p2p_stream);
let (eth_stream, their_status) = match eth_unauthed.handshake(status, fork_filter).await {
Ok(stream_res) => stream_res,
Err(err) => {
return PendingSessionEvent::Disconnected {
remote_addr,
session_id,
direction,
error: Some(err),
}
}
};
PendingSessionEvent::Established {
session_id,
remote_addr,
node_id: their_hello.id,
capabilities: Arc::new(Capabilities::from(their_hello.capabilities)),
status: their_status,
conn: eth_stream,
}
}

View File

@ -5,12 +5,11 @@ use crate::{
fetch::StateFetcher,
message::{PeerRequestSender, PeerResponse},
peers::{PeerAction, PeersManager},
NodeId,
};
use reth_eth_wire::{capability::Capabilities, Status};
use reth_interfaces::provider::BlockProvider;
use reth_primitives::H256;
use reth_primitives::{PeerId, H256};
use std::{
collections::{HashMap, VecDeque},
net::SocketAddr,
@ -37,7 +36,7 @@ use tracing::trace;
/// This type is also responsible for responding for received request.
pub struct NetworkState<C> {
/// All connected peers and their state.
connected_peers: HashMap<NodeId, ConnectedPeer>,
connected_peers: HashMap<PeerId, ConnectedPeer>,
/// Manages connections to peers.
peers_manager: PeersManager,
/// Buffered messages until polled.
@ -83,7 +82,7 @@ where
/// should be rejected.
pub(crate) fn on_session_activated(
&mut self,
peer: NodeId,
peer: PeerId,
capabilities: Arc<Capabilities>,
status: Status,
request_tx: PeerRequestSender,
@ -107,7 +106,7 @@ where
}
/// Event hook for a disconnected session for the peer.
pub(crate) fn on_session_closed(&mut self, peer: NodeId) {
pub(crate) fn on_session_closed(&mut self, peer: PeerId) {
self.connected_peers.remove(&peer);
self.state_fetcher.on_session_closed(&peer);
}
@ -149,7 +148,7 @@ where
}
/// Disconnect the session
fn on_session_disconnected(&mut self, peer: NodeId) {
fn on_session_disconnected(&mut self, peer: PeerId) {
self.connected_peers.remove(&peer);
}
@ -157,7 +156,7 @@ where
///
/// Caution: this will replace an already pending response. It's the responsibility of the
/// caller to select the peer.
fn handle_block_request(&mut self, peer: NodeId, request: BlockRequest) {
fn handle_block_request(&mut self, peer: PeerId, request: BlockRequest) {
if let Some(ref mut peer) = self.connected_peers.get_mut(&peer) {
let (request, response) = match request {
BlockRequest::GetBlockHeaders(request) => {
@ -192,7 +191,7 @@ where
}
/// Invoked when received a response from a connected peer.
fn on_eth_response(&mut self, peer: NodeId, resp: PeerResponseResult) -> Option<StateAction> {
fn on_eth_response(&mut self, peer: PeerId, resp: PeerResponseResult) -> Option<StateAction> {
match resp {
PeerResponseResult::BlockHeaders(res) => {
let outcome = self.state_fetcher.on_block_headers_response(peer, res)?;
@ -283,9 +282,9 @@ pub struct ConnectedPeer {
/// Message variants triggered by the [`State`]
pub enum StateAction {
/// Create a new connection to the given node.
Connect { remote_addr: SocketAddr, node_id: NodeId },
Connect { remote_addr: SocketAddr, node_id: PeerId },
/// Disconnect an existing connection
Disconnect { node_id: NodeId },
Disconnect { node_id: PeerId },
}
#[derive(Debug, thiserror::Error)]
@ -293,6 +292,6 @@ pub enum AddSessionError {
#[error("No capacity for new sessions")]
AtCapacity {
/// The peer of the session
peer: NodeId,
peer: PeerId,
},
}

View File

@ -2,12 +2,14 @@ use crate::{
listener::{ConnectionListener, ListenerEvent},
session::{SessionEvent, SessionId, SessionManager},
state::{AddSessionError, NetworkState, StateAction},
NodeId,
};
use futures::Stream;
use reth_ecies::ECIESError;
use reth_eth_wire::capability::{Capabilities, CapabilityMessage};
use reth_eth_wire::{
capability::{Capabilities, CapabilityMessage},
error::EthStreamError,
};
use reth_interfaces::provider::BlockProvider;
use reth_primitives::PeerId;
use std::{
io,
net::SocketAddr,
@ -55,7 +57,7 @@ where
}
/// Triggers a new outgoing connection to the given node
pub(crate) fn dial_outbound(&mut self, remote_addr: SocketAddr, remote_id: NodeId) {
pub(crate) fn dial_outbound(&mut self, remote_addr: SocketAddr, remote_id: PeerId) {
self.sessions.dial_outbound(remote_addr, remote_id)
}
@ -191,13 +193,13 @@ pub enum SwarmEvent {
/// Events related to the actual network protocol.
CapabilityMessage {
/// The peer that sent the message
node_id: NodeId,
node_id: PeerId,
/// Message received from the peer
message: CapabilityMessage,
},
/// Received a message that does not match the announced capabilities of the peer.
InvalidCapabilityMessage {
node_id: NodeId,
node_id: PeerId,
/// Announced capabilities of the remote peer.
capabilities: Arc<Capabilities>,
/// Message received from the peer.
@ -226,28 +228,28 @@ pub enum SwarmEvent {
remote_addr: SocketAddr,
},
SessionEstablished {
node_id: NodeId,
node_id: PeerId,
remote_addr: SocketAddr,
},
SessionClosed {
node_id: NodeId,
node_id: PeerId,
remote_addr: SocketAddr,
},
/// Closed an incoming pending session during authentication.
IncomingPendingSessionClosed {
remote_addr: SocketAddr,
error: Option<ECIESError>,
error: Option<EthStreamError>,
},
/// Closed an outgoing pending session during authentication.
OutgoingPendingSessionClosed {
remote_addr: SocketAddr,
node_id: NodeId,
error: Option<ECIESError>,
node_id: PeerId,
error: Option<EthStreamError>,
},
/// Failed to establish a tcp stream to the given address/node
OutgoingConnectionError {
remote_addr: SocketAddr,
node_id: NodeId,
node_id: PeerId,
error: io::Error,
},
}

View File

@ -0,0 +1,5 @@
use crate::H256;
/// The Ethereum mainnet genesis hash.
pub const MAINNET_GENESIS: H256 =
H256(hex_literal::hex!("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"));

View File

@ -3,9 +3,9 @@
#![deny(missing_docs)]
#![allow(clippy::redundant_else, clippy::too_many_lines)]
use crate::{BlockNumber, H256};
use crc::crc32;
use maplit::btreemap;
use reth_primitives::{BlockNumber, H256};
use reth_rlp::*;
use std::{
collections::{BTreeMap, BTreeSet},

View File

@ -0,0 +1,225 @@
use crate::{BlockNumber, ForkFilter, ForkHash, ForkId, MAINNET_GENESIS};
use std::str::FromStr;
/// Ethereum mainnet hardforks
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Hardfork {
Frontier,
Homestead,
Dao,
Tangerine,
SpuriousDragon,
Byzantium,
Constantinople,
Petersburg,
Istanbul,
Muirglacier,
Berlin,
London,
ArrowGlacier,
GrayGlacier,
Latest,
}
impl Hardfork {
/// Get the first block number of the hardfork.
pub fn fork_block(&self) -> u64 {
match *self {
Hardfork::Frontier => 0,
Hardfork::Homestead => 1150000,
Hardfork::Dao => 1920000,
Hardfork::Tangerine => 2463000,
Hardfork::SpuriousDragon => 2675000,
Hardfork::Byzantium => 4370000,
Hardfork::Constantinople | Hardfork::Petersburg => 7280000,
Hardfork::Istanbul => 9069000,
Hardfork::Muirglacier => 9200000,
Hardfork::Berlin => 12244000,
Hardfork::London => 12965000,
Hardfork::ArrowGlacier => 13773000,
Hardfork::GrayGlacier | Hardfork::Latest => 15050000,
}
}
/// Get the EIP-2124 fork id for a given hardfork
///
/// The [`ForkId`](ethereum_forkid::ForkId) includes a CRC32 checksum of the all fork block
/// numbers from genesis, and the next upcoming fork block number.
/// If the next fork block number is not yet known, it is set to 0.
pub fn fork_id(&self) -> ForkId {
match *self {
Hardfork::Frontier => {
ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 }
}
Hardfork::Homestead => {
ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 }
}
Hardfork::Dao => ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
Hardfork::Tangerine => {
ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 }
}
Hardfork::SpuriousDragon => {
ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 }
}
Hardfork::Byzantium => {
ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 }
}
Hardfork::Constantinople | Hardfork::Petersburg => {
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 }
}
Hardfork::Istanbul => {
ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 }
}
Hardfork::Muirglacier => {
ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 }
}
Hardfork::Berlin => ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
Hardfork::London => ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
Hardfork::ArrowGlacier => {
ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 }
}
Hardfork::Latest | Hardfork::GrayGlacier => {
// update `next` when another fork block num is known
ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 0 }
}
}
}
/// This returns all known hardforks in order.
pub fn all_forks() -> Vec<Self> {
vec![
Hardfork::Homestead,
Hardfork::Dao,
Hardfork::Tangerine,
Hardfork::SpuriousDragon,
Hardfork::Byzantium,
Hardfork::Constantinople, /* petersburg is skipped because it's the same block num
* as constantinople */
Hardfork::Istanbul,
Hardfork::Muirglacier,
Hardfork::Berlin,
Hardfork::London,
Hardfork::ArrowGlacier,
Hardfork::GrayGlacier,
]
}
/// This returns all known hardfork block numbers as a vector.
pub fn all_fork_blocks() -> Vec<BlockNumber> {
Hardfork::all_forks().iter().map(|f| f.fork_block()).collect()
}
/// Creates a [`ForkFilter`](crate::ForkFilter) for the given hardfork.
/// This assumes the current hardfork's block number is the current head and uses all known
/// future hardforks to initialize the filter.
pub fn fork_filter(&self) -> ForkFilter {
let all_forks = Hardfork::all_forks();
let future_forks: Vec<BlockNumber> = all_forks
.iter()
.filter(|f| f.fork_block() > self.fork_block())
.map(|f| f.fork_block())
.collect();
// this data structure is not chain-agnostic, so we can pass in the constant mainnet
// genesis
ForkFilter::new(self.fork_block(), MAINNET_GENESIS, future_forks)
}
}
impl FromStr for Hardfork {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
let hardfork = match s.as_str() {
"frontier" | "1" => Hardfork::Frontier,
"homestead" | "2" => Hardfork::Homestead,
"dao" | "3" => Hardfork::Dao,
"tangerine" | "4" => Hardfork::Tangerine,
"spuriousdragon" | "5" => Hardfork::SpuriousDragon,
"byzantium" | "6" => Hardfork::Byzantium,
"constantinople" | "7" => Hardfork::Constantinople,
"petersburg" | "8" => Hardfork::Petersburg,
"istanbul" | "9" => Hardfork::Istanbul,
"muirglacier" | "10" => Hardfork::Muirglacier,
"berlin" | "11" => Hardfork::Berlin,
"london" | "12" => Hardfork::London,
"arrowglacier" | "13" => Hardfork::ArrowGlacier,
"grayglacier" => Hardfork::GrayGlacier,
"latest" | "14" => Hardfork::Latest,
_ => return Err(format!("Unknown hardfork {s}")),
};
Ok(hardfork)
}
}
impl Default for Hardfork {
fn default() -> Self {
Hardfork::Latest
}
}
impl From<BlockNumber> for Hardfork {
fn from(num: BlockNumber) -> Hardfork {
match num {
_i if num < 1_150_000 => Hardfork::Frontier,
_i if num < 1_920_000 => Hardfork::Dao,
_i if num < 2_463_000 => Hardfork::Homestead,
_i if num < 2_675_000 => Hardfork::Tangerine,
_i if num < 4_370_000 => Hardfork::SpuriousDragon,
_i if num < 7_280_000 => Hardfork::Byzantium,
_i if num < 9_069_000 => Hardfork::Constantinople,
_i if num < 9_200_000 => Hardfork::Istanbul,
_i if num < 12_244_000 => Hardfork::Muirglacier,
_i if num < 12_965_000 => Hardfork::Berlin,
_i if num < 13_773_000 => Hardfork::London,
_i if num < 15_050_000 => Hardfork::ArrowGlacier,
_ => Hardfork::Latest,
}
}
}
#[cfg(test)]
mod tests {
use crate::{forkid::ForkHash, hardfork::Hardfork};
use crc::crc32;
#[test]
fn test_hardfork_blocks() {
let hf: Hardfork = 12_965_000u64.into();
assert_eq!(hf, Hardfork::London);
let hf: Hardfork = 4370000u64.into();
assert_eq!(hf, Hardfork::Byzantium);
let hf: Hardfork = 12244000u64.into();
assert_eq!(hf, Hardfork::Berlin);
}
#[test]
// this test checks that the fork hash assigned to forks accurately map to the fork_id method
fn test_forkhash_from_fork_blocks() {
// set the genesis hash
let genesis =
hex::decode("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
.unwrap();
// set the frontier forkhash
let mut curr_forkhash = ForkHash(crc32::checksum_ieee(&genesis[..]).to_be_bytes());
// now we go through enum members
let frontier_forkid = Hardfork::Frontier.fork_id();
assert_eq!(curr_forkhash, frontier_forkid.hash);
// list of the above hardforks
let hardforks = Hardfork::all_forks();
// check that the curr_forkhash we compute matches the output of each fork_id returned
for hardfork in hardforks {
curr_forkhash += hardfork.fork_block();
assert_eq!(curr_forkhash, hardfork.fork_id().hash);
}
}
}

View File

@ -10,7 +10,10 @@
mod account;
mod block;
mod chain;
mod constants;
mod error;
mod forkid;
mod hardfork;
mod header;
mod hex_bytes;
mod integer_list;
@ -23,6 +26,9 @@ mod transaction;
pub use account::Account;
pub use block::{Block, BlockLocked};
pub use chain::Chain;
pub use constants::MAINNET_GENESIS;
pub use forkid::{ForkFilter, ForkHash, ForkId, ValidationError};
pub use hardfork::Hardfork;
pub use header::{Header, SealedHeader};
pub use hex_bytes::Bytes;
pub use integer_list::IntegerList;
@ -56,6 +62,12 @@ pub type StorageKey = H256;
/// Storage value
pub type StorageValue = U256;
// TODO: should we use `PublicKey` for this? Even when dealing with public keys we should try to
// prevent misuse
/// This represents an uncompressed secp256k1 public key.
/// This encodes the concatenation of the x and y components of the affine point in bytes.
pub type PeerId = H512;
pub use ethers_core::{
types as rpc,
types::{BigEndianHash, Bloom, H128, H160, H256, H512, H64, U128, U256, U64},