Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 9 additions & 1 deletion crates/networking/p2p/discv4/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::{
};
use bytes::BytesMut;
use ethrex_common::{H256, H512};
use ethrex_storage::Store;
use futures::StreamExt;
use rand::rngs::OsRng;
use secp256k1::SecretKey;
Expand Down Expand Up @@ -90,6 +91,7 @@ pub struct DiscoveryServer {

impl DiscoveryServer {
pub async fn spawn(
storage: Store,
local_node: Node,
signer: SecretKey,
udp_socket: Arc<UdpSocket>,
Expand All @@ -98,8 +100,14 @@ impl DiscoveryServer {
) -> Result<(), DiscoveryServerError> {
info!("Starting Discovery Server");

let local_node_record = NodeRecord::from_node(&local_node, 1, &signer)
let mut local_node_record = NodeRecord::from_node(&local_node, 1, &signer)
.expect("Failed to create local node record");
if let Ok(fork_id) = storage.get_fork_id().await {
local_node_record
.set_fork_id(fork_id, &signer)
.expect("Failed to set fork_id on local node record");
}

let mut discovery_server = Self {
local_node: local_node.clone(),
local_node_record,
Expand Down
1 change: 1 addition & 0 deletions crates/networking/p2p/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ pub async fn start_network(context: P2PContext, bootnodes: Vec<Node>) -> Result<
);

DiscoveryServer::spawn(
context.storage.clone(),
context.local_node.clone(),
context.signer,
udp_socket.clone(),
Expand Down
61 changes: 61 additions & 0 deletions crates/networking/p2p/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ pub struct NodeRecord {
pub seq: u64,
// holds optional values in (key, value) format
// value represents the rlp encoded bytes
// The key/value pairs must be sorted by key and must be unique
pub pairs: Vec<(Bytes, Bytes)>,
}

Expand Down Expand Up @@ -377,6 +378,23 @@ impl NodeRecord {
Ok(record)
}

pub fn set_fork_id(&mut self, fork_id: ForkId, signer: &SecretKey) -> Result<(), NodeError> {
// Without the Vec wrapper, RLP encoding fork_id directly would produce:
// [forkHash, forkNext]
// But the spec requires nested lists:
// [[forkHash, forkNext]]
let eth = vec![fork_id];
self.pairs.push(("eth".into(), eth.encode_to_vec().into()));

//Pairs need to be sorted by their key.
//The keys are Bytes which implements Ord, so they can be compared directly. The sorting
//will be lexicographic (alphabetical for string keys like "eth", "id", "ip", etc.).
self.pairs.sort_by(|a, b| a.0.cmp(&b.0));

self.signature = self.sign_record(signer)?;
Ok(())
}

fn sign_record(&self, signer: &SecretKey) -> Result<H512, NodeError> {
let digest = &self.get_signature_digest();
let msg = secp256k1::Message::from_digest_slice(digest)
Expand All @@ -402,6 +420,11 @@ impl From<NodeRecordPairs> for Vec<(Bytes, Bytes)> {
fn from(value: NodeRecordPairs) -> Self {
let mut pairs = vec![];
if let Some(eth) = value.eth {
// Without the Vec wrapper, RLP encoding fork_id directly would produce:
// [forkHash, forkNext]
// But the spec requires nested lists:
// [[forkHash, forkNext]]
let eth = vec![eth];
pairs.push(("eth".into(), eth.encode_to_vec().into()));
}
if let Some(id) = value.id {
Expand Down Expand Up @@ -500,6 +523,7 @@ mod tests {
utils::public_key_from_signing_key,
};
use ethrex_common::H512;
use ethrex_rlp::decode::RLPDecode;
use ethrex_storage::{EngineType, Store};
use secp256k1::SecretKey;
use std::{net::SocketAddr, str::FromStr};
Expand Down Expand Up @@ -590,4 +614,41 @@ mod tests {

assert_eq!(record.enr_url().unwrap(), expected_enr_string);
}

#[tokio::test]
async fn encode_decode_node_record_with_forkid() {
let signer = SecretKey::from_slice(&[
16, 125, 177, 238, 167, 212, 168, 215, 239, 165, 77, 224, 199, 143, 55, 205, 9, 194,
87, 139, 92, 46, 30, 191, 74, 37, 68, 242, 38, 225, 104, 246,
])
.unwrap();
let addr = std::net::SocketAddr::from_str("127.0.0.1:30303").unwrap();

let mut storage =
Store::new("", EngineType::InMemory).expect("Failed to create in-memory storage");
storage
.add_initial_state(serde_json::from_str(TEST_GENESIS).unwrap())
.await
.expect("Failed to build test genesis");

let node = Node::new(
addr.ip(),
addr.port(),
addr.port(),
public_key_from_signing_key(&signer),
);
let fork_id = storage.get_fork_id().await.unwrap();

let mut record = NodeRecord::from_node(&node, 1, &signer).unwrap();
record.set_fork_id(fork_id.clone(), &signer).unwrap();

record.sign_record(&signer).unwrap();

let enr_url = record.enr_url().unwrap();
let base64_decoded = ethrex_common::base64::decode(&enr_url.as_bytes()[4..]);
let parsed_record = NodeRecord::decode(&base64_decoded).unwrap();
let pairs = parsed_record.decode_pairs();

assert_eq!(pairs.eth, Some(fork_id));
}
}