Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1f3744c
Merge branch 'main' of github.com:lambdaclass/ethereum_rust
juanbono Jun 10, 2024
d883432
Merge branch 'main' of github.com:lambdaclass/ethereum_rust
juanbono Jun 24, 2024
7b8eff3
rename ethereum_rust -> ethrex
juanbono Jun 24, 2024
bb67116
Merge branch 'main' of github.com:lambdaclass/ethereum_rust
juanbono Jun 24, 2024
6ce865f
Merge branch 'main' of github.com:lambdaclass/ethereum_rust
juanbono Jun 24, 2024
0434d3a
Merge branch 'main' of github.com:lambdaclass/ethereum_rust
juanbono Jun 28, 2024
5b4d1a6
Merge branch 'main' of github.com:lambdaclass/ethereum_rust
juanbono Jun 29, 2024
344413b
add empty modules
juanbono Jul 1, 2024
b70b287
Merge branch 'main' into implement_rlpx_handshake
MegaRedHand Jul 2, 2024
d6d055a
move `types/mod.rs` -> `types.rs`
MegaRedHand Jul 2, 2024
0fc4e02
chore: add RLPx cryptography crates
MegaRedHand Jul 2, 2024
992af95
feat: add example handshake (WIP)
MegaRedHand Jul 2, 2024
0334828
comment
MegaRedHand Jul 3, 2024
020693b
Merge branch 'main' into implement_rlpx_handshake
MegaRedHand Jul 3, 2024
ca6946a
Add auth decoding example
MegaRedHand Jul 4, 2024
0316f03
Finish auth message decoding
MegaRedHand Jul 4, 2024
2e262e8
feat: add `AuthMessage` struct
MegaRedHand Jul 5, 2024
c48f908
feat: implement auth message creation
MegaRedHand Jul 5, 2024
38aa8ab
chore: move auth message construction to a function
MegaRedHand Jul 8, 2024
a3d557b
Merge branch 'main' into implement_rlpx_handshake
MegaRedHand Jul 8, 2024
e53b0ea
fix: was using wrong address for ecdh
MegaRedHand Jul 8, 2024
9fa601f
feat: decode ack message
MegaRedHand Jul 8, 2024
84dd0d8
Clean up a bit
MegaRedHand Jul 8, 2024
e84398d
Merge branch 'main' into implement_rlpx_handshake
MegaRedHand Jul 8, 2024
81d22fb
chore: remove unused module
MegaRedHand Jul 8, 2024
1b03fb0
Fix
MegaRedHand Jul 8, 2024
1d8f733
fix clippy
MegaRedHand Jul 10, 2024
7890c87
Remove comment
MegaRedHand Jul 10, 2024
692b0c8
feat: derive connection secrets
MegaRedHand Jul 10, 2024
e8162a2
Update tests
MegaRedHand Jul 10, 2024
6ec328b
fix clippy
MegaRedHand Jul 10, 2024
0bd533a
Merge branch 'main' into implement_rlpx_handshake
MegaRedHand Jul 10, 2024
26f0fc9
refactor a bit
MegaRedHand Jul 10, 2024
682be09
add comment
MegaRedHand Jul 10, 2024
04749f2
Merge branch 'main' into implement_rlpx_handshake
MegaRedHand Jul 12, 2024
754eba1
Merge imports
MegaRedHand Jul 12, 2024
1c0cab2
Improve documentation in encode_auth_message
MegaRedHand Jul 12, 2024
4d56b2a
Add inline comments on RLPx handshake functions
MegaRedHand Jul 12, 2024
2046725
Fix tests
MegaRedHand Jul 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions crates/core/src/rlp/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ use tinyvec::ArrayVec;

use super::constants::RLP_NULL;

/// Function for encoding a value to RLP.
/// For encoding the value into a buffer directly, use [`RLPEncode::encode`].
pub fn encode<T: RLPEncode>(value: T) -> Vec<u8> {
let mut buf = Vec::new();
value.encode(&mut buf);
buf
}

pub trait RLPEncode {
fn encode(&self, buf: &mut dyn BufMut);

Expand Down
13 changes: 12 additions & 1 deletion crates/net/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,16 @@ ethereum_rust-core.workspace = true
tracing.workspace = true
tokio.workspace = true
bytes.workspace = true
k256 = "0.13.3"

k256 = { version = "0.13.3", features = ["ecdh"] }
keccak-hash = "0.10.0"

# RLPx
concat-kdf = "0.1.0"
hmac = "0.12.1"
aes = "0.8.4"
ctr = "0.9.2"
rand = "0.8.5"

[dev-dependencies]
hex-literal = "0.4.1"
27 changes: 17 additions & 10 deletions crates/net/src/discv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ use ethereum_rust_core::rlp::{
};
use ethereum_rust_core::{H256, H512, H520};
use k256::ecdsa::SigningKey;
use std::net::IpAddr;
use std::net::{IpAddr, SocketAddr};

#[allow(unused)]
#[derive(Debug)]
pub struct Packet {
hash: H256,
signature: H520,
Expand Down Expand Up @@ -145,6 +146,12 @@ pub(crate) struct Endpoint {
pub tcp_port: u16,
}

impl Endpoint {
pub fn tcp_address(&self) -> Option<SocketAddr> {
(self.tcp_port != 0).then_some(SocketAddr::new(self.ip, self.tcp_port))
}
}

impl RLPEncode for Endpoint {
fn encode(&self, buf: &mut dyn BufMut) {
structs::Encoder::new(buf)
Expand Down Expand Up @@ -178,12 +185,12 @@ pub(crate) struct PingMessage {
/// The endpoint of the sender.
pub from: Endpoint,
/// The endpoint of the receiver.
to: Endpoint,
pub to: Endpoint,
/// The expiration time of the message. If the message is older than this time,
/// it shouldn't be responded to.
expiration: u64,
pub expiration: u64,
/// The ENR sequence number of the sender. This field is optional.
enr_seq: Option<u64>,
pub enr_seq: Option<u64>,
}

impl PingMessage {
Expand Down Expand Up @@ -222,10 +229,10 @@ impl RLPEncode for PingMessage {
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct FindNodeMessage {
/// The target is a 64-byte secp256k1 public key.
target: H512,
pub target: H512,
/// The expiration time of the message. If the message is older than this time,
/// it shouldn't be responded to.
expiration: u64,
pub expiration: u64,
}

impl FindNodeMessage {
Expand Down Expand Up @@ -280,14 +287,14 @@ impl RLPDecode for PingMessage {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct PongMessage {
/// The endpoint of the receiver.
to: Endpoint,
pub to: Endpoint,
/// The hash of the corresponding ping packet.
ping_hash: H256,
pub ping_hash: H256,
/// The expiration time of the message. If the message is older than this time,
/// it shouldn't be responded to.
expiration: u64,
pub expiration: u64,
/// The ENR sequence number of the sender. This field is optional.
enr_seq: Option<u64>,
pub enr_seq: Option<u64>,
}

impl PongMessage {
Expand Down
108 changes: 89 additions & 19 deletions crates/net/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,46 @@
pub mod bootnode;
pub(crate) mod discv4;
pub(crate) mod kademlia;
use discv4::{Endpoint, FindNodeMessage, Message, Packet, PingMessage, PongMessage};
use ethereum_rust_core::H512;
use k256::elliptic_curve::sec1::ToEncodedPoint;
use k256::elliptic_curve::PublicKey;
use k256::{ecdsa::SigningKey, elliptic_curve::rand_core::OsRng};
use keccak_hash::H256;

use bootnode::BootNode;
use kademlia::{KademliaTable, PeerData};
use std::vec;
use std::{
net::SocketAddr,
time::{Duration, SystemTime, UNIX_EPOCH},
};

use bootnode::BootNode;
use discv4::{Endpoint, FindNodeMessage, Message, Packet, PingMessage, PongMessage};
use ethereum_rust_core::H512;
use k256::{
ecdsa::{RecoveryId, Signature, SigningKey, VerifyingKey},
elliptic_curve::{rand_core::OsRng, sec1::ToEncodedPoint, PublicKey},
SecretKey,
};
use kademlia::{KademliaTable, PeerData};
use keccak_hash::H256;
use rlpx::ecies::RLPxConnection;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{TcpSocket, UdpSocket},
try_join,
};
use tracing::info;
use tracing::{info, warn};

pub mod bootnode;
pub(crate) mod discv4;
pub(crate) mod kademlia;
pub mod rlpx;

const MAX_DISC_PACKET_SIZE: usize = 1280;

pub async fn start_network(udp_addr: SocketAddr, tcp_addr: SocketAddr, bootnodes: Vec<BootNode>) {
info!("Starting discovery service at {udp_addr}");
info!("Listening for requests at {tcp_addr}");
let signer = SigningKey::random(&mut OsRng);

let discovery_handle = tokio::spawn(discover_peers(udp_addr, bootnodes));
let server_handle = tokio::spawn(serve_requests(tcp_addr));
let discovery_handle = tokio::spawn(discover_peers(udp_addr, signer.clone(), bootnodes));
let server_handle = tokio::spawn(serve_requests(tcp_addr, signer));
try_join!(discovery_handle, server_handle).unwrap();
}

async fn discover_peers(udp_addr: SocketAddr, bootnodes: Vec<BootNode>) {
async fn discover_peers(udp_addr: SocketAddr, signer: SigningKey, bootnodes: Vec<BootNode>) {
let udp_socket = UdpSocket::bind(udp_addr).await.unwrap();
let signer = SigningKey::random(&mut OsRng);

let public_key = PublicKey::from(signer.verifying_key());
let encoded = public_key.to_encoded_point(false);
let local_node_id = H512::from_slice(&encoded.as_bytes()[1..]);
Expand Down Expand Up @@ -147,9 +153,73 @@ async fn pong(socket: &UdpSocket, to_addr: SocketAddr, ping_hash: H256, signer:
socket.send_to(&buf, to_addr).await.unwrap();
}

async fn serve_requests(tcp_addr: SocketAddr) {
async fn serve_requests(tcp_addr: SocketAddr, signer: SigningKey) {
let secret_key: SecretKey = signer.clone().into();
let tcp_socket = TcpSocket::new_v4().unwrap();
tcp_socket.bind(tcp_addr).unwrap();

let mut udp_addr = tcp_addr;
udp_addr.set_port(tcp_addr.port() + 1);
let udp_socket = UdpSocket::bind(udp_addr).await.unwrap();

// Try contacting a known peer
// TODO: this is just an example, and we should do this dynamically
let str_udp_addr = "127.0.0.1:51311";

let udp_addr: SocketAddr = str_udp_addr.parse().unwrap();

let mut buf = vec![0; MAX_DISC_PACKET_SIZE];

let (mut msg, sig_bytes, endpoint) = loop {
ping(&udp_socket, tcp_addr, udp_addr, &signer).await;

let (read, from) = udp_socket.recv_from(&mut buf).await.unwrap();
info!("Received {read} bytes from {from}");
let packet = Packet::decode(&buf[..read]).unwrap();
info!("Message: {:?}", packet);

match packet.get_message() {
Message::Pong(pong) => {
break (&buf[32 + 65..read], &buf[32..32 + 65], pong.to);
}
Message::Ping(ping) => {
break (&buf[32 + 65..read], &buf[32..32 + 65], ping.from);
}
_ => {
warn!("Unexpected message type");
}
};
};

let digest = keccak_hash::keccak_buffer(&mut msg).unwrap();
let signature = &Signature::from_bytes(sig_bytes[..64].into()).unwrap();
let rid = RecoveryId::from_byte(sig_bytes[64]).unwrap();

let peer_pk = VerifyingKey::recover_from_prehash(&digest.0, signature, rid).unwrap();

let mut conn = RLPxConnection::random();
let mut auth_message = vec![];
conn.encode_auth_message(&secret_key, &peer_pk.into(), &mut auth_message);

let tcp_addr = "127.0.0.1:58617";
// NOTE: for some reason kurtosis peers don't publish their active TCP port
let tcp_addr = endpoint.tcp_address().unwrap_or(tcp_addr.parse().unwrap());

let mut stream = TcpSocket::new_v4()
.unwrap()
.connect(tcp_addr)
.await
.unwrap();

stream.write_all(&auth_message).await.unwrap();
info!("Sent auth message correctly!");
stream.read_exact(&mut buf[..2]).await.unwrap();
let auth_data = buf[..2].try_into().unwrap();
let msg_size = u16::from_be_bytes(auth_data) as usize;
let msg = &mut buf[..msg_size];
stream.read_exact(msg).await.unwrap();
conn.decode_ack_message(&secret_key, msg, auth_data);
info!("Completed handshake!");
}

#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions crates/net/src/rlpx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod ecies;
Loading