diff --git a/Cargo.lock b/Cargo.lock index 855281c0ab..a1bf310e58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3012,6 +3012,7 @@ dependencies = [ "hex", "hex-literal", "jsonwebtoken 9.3.0", + "k256", "rand 0.8.5", "reqwest 0.12.9", "serde", diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index dcabd821d0..e7e4577dab 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -5,7 +5,7 @@ use ethrex_core::types::{Block, Genesis}; use ethrex_net::{ node_id_from_signing_key, peer_table, sync::{SyncManager, SyncMode}, - types::Node, + types::{Node, NodeRecord}, KademliaTable, }; use ethrex_rlp::decode::RLPDecode; @@ -224,6 +224,12 @@ async fn main() { tcp_port: tcp_socket_addr.port(), node_id: local_node_id, }; + let enr_seq = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + let local_node_record = NodeRecord::from_node(local_p2p_node, enr_seq, &signer) + .expect("Node record could not be created from local node"); // Create Kademlia Table here so we can access it from rpc server (for syncing) let peer_table = peer_table(signer.clone()); // Create SyncManager @@ -237,6 +243,7 @@ async fn main() { store.clone(), jwt_secret, local_p2p_node, + local_node_record, syncer, ) .into_future(); diff --git a/crates/networking/rpc/Cargo.toml b/crates/networking/rpc/Cargo.toml index cc9f500291..fa291a2084 100644 --- a/crates/networking/rpc/Cargo.toml +++ b/crates/networking/rpc/Cargo.toml @@ -25,6 +25,7 @@ jsonwebtoken.workspace = true rand.workspace = true tokio-util.workspace = true reqwest.workspace = true +k256 = { version = "0.13.3", features = ["ecdh"] } [dev-dependencies] hex-literal = "0.4.1" diff --git a/crates/networking/rpc/admin/mod.rs b/crates/networking/rpc/admin/mod.rs index 5a8d406d14..deee568005 100644 --- a/crates/networking/rpc/admin/mod.rs +++ b/crates/networking/rpc/admin/mod.rs @@ -1,5 +1,5 @@ use ethrex_core::types::ChainConfig; -use ethrex_net::types::Node; +use ethrex_net::types::{Node, NodeRecord}; use ethrex_storage::Store; use serde::Serialize; use serde_json::Value; @@ -10,6 +10,7 @@ use crate::utils::RpcErr; #[derive(Serialize, Debug)] struct NodeInfo { enode: String, + enr: String, id: String, ip: String, name: String, @@ -29,8 +30,13 @@ enum Protocol { Eth(ChainConfig), } -pub fn node_info(storage: Store, local_node: Node) -> Result { +pub fn node_info( + storage: Store, + local_node: Node, + local_node_record: NodeRecord, +) -> Result { let enode_url = local_node.enode_url(); + let enr_url = local_node_record.enr_url(); let mut protocols = HashMap::new(); let chain_config = storage @@ -40,6 +46,7 @@ pub fn node_info(storage: Store, local_node: Node) -> Result { let node_info = NodeInfo { enode: enode_url, + enr: enr_url, id: hex::encode(local_node.node_id), name: "ethrex/0.1.0/rust1.81".to_string(), ip: local_node.ip.to_string(), diff --git a/crates/networking/rpc/rpc.rs b/crates/networking/rpc/rpc.rs index 38efee747d..3bcd4a8c9b 100644 --- a/crates/networking/rpc/rpc.rs +++ b/crates/networking/rpc/rpc.rs @@ -36,7 +36,7 @@ use eth::{ GetTransactionByHashRequest, GetTransactionReceiptRequest, }, }; -use ethrex_net::sync::SyncManager; +use ethrex_net::{sync::SyncManager, types::NodeRecord}; use serde_json::Value; use std::{ collections::HashMap, @@ -70,6 +70,7 @@ pub struct RpcApiContext { storage: Store, jwt_secret: Bytes, local_p2p_node: Node, + local_node_record: NodeRecord, active_filters: ActiveFilters, syncer: Arc>, } @@ -99,6 +100,7 @@ pub async fn start_api( storage: Store, jwt_secret: Bytes, local_p2p_node: Node, + local_node_record: NodeRecord, syncer: SyncManager, ) { // TODO: Refactor how filters are handled, @@ -108,6 +110,7 @@ pub async fn start_api( storage: storage.clone(), jwt_secret, local_p2p_node, + local_node_record, active_filters: active_filters.clone(), syncer: Arc::new(TokioMutex::new(syncer)), }; @@ -280,7 +283,11 @@ pub fn map_engine_requests(req: &RpcRequest, context: RpcApiContext) -> Result Result { match req.method.as_str() { - "admin_nodeInfo" => admin::node_info(context.storage, context.local_p2p_node), + "admin_nodeInfo" => admin::node_info( + context.storage, + context.local_p2p_node, + context.local_node_record, + ), unknown_admin_method => Err(RpcErr::MethodNotFound(unknown_admin_method.to_owned())), } } @@ -326,7 +333,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::utils::test_utils::example_p2p_node; + use crate::utils::test_utils::{example_local_node_record, example_p2p_node}; use ethrex_core::types::{ChainConfig, Genesis}; use ethrex_storage::EngineType; use std::fs::File; @@ -348,6 +355,7 @@ mod tests { storage.set_chain_config(&example_chain_config()).unwrap(); let context = RpcApiContext { local_p2p_node, + local_node_record: example_local_node_record(), storage, jwt_secret: Default::default(), active_filters: Default::default(), @@ -386,6 +394,7 @@ mod tests { // Process request let context = RpcApiContext { local_p2p_node, + local_node_record: example_local_node_record(), storage, jwt_secret: Default::default(), active_filters: Default::default(), @@ -416,6 +425,7 @@ mod tests { // Process request let context = RpcApiContext { local_p2p_node, + local_node_record: example_local_node_record(), storage, jwt_secret: Default::default(), active_filters: Default::default(), @@ -477,6 +487,7 @@ mod tests { let context = RpcApiContext { storage, local_p2p_node, + local_node_record: example_local_node_record(), jwt_secret: Default::default(), active_filters: Default::default(), syncer: Arc::new(TokioMutex::new(SyncManager::dummy())), diff --git a/crates/networking/rpc/utils.rs b/crates/networking/rpc/utils.rs index 092612c78f..f50e0254fd 100644 --- a/crates/networking/rpc/utils.rs +++ b/crates/networking/rpc/utils.rs @@ -255,8 +255,12 @@ pub mod test_utils { use std::{net::SocketAddr, str::FromStr}; use ethrex_core::H512; - use ethrex_net::{sync::SyncManager, types::Node}; + use ethrex_net::{ + sync::SyncManager, + types::{Node, NodeRecord}, + }; use ethrex_storage::{EngineType, Store}; + use k256::ecdsa::SigningKey; use crate::start_api; @@ -271,6 +275,19 @@ pub mod test_utils { } } + pub fn example_local_node_record() -> NodeRecord { + let node_id_1 = H512::from_str("d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666").unwrap(); + let node = Node { + ip: "127.0.0.1".parse().unwrap(), + udp_port: 30303, + tcp_port: 30303, + node_id: node_id_1, + }; + let signer = SigningKey::random(&mut rand::rngs::OsRng); + + NodeRecord::from_node(node, 0, &signer).unwrap() + } + // Util to start an api for testing on ports 8500 and 8501, // mostly for when hive is missing some endpoints to test // like eth_uninstallFilter. @@ -299,6 +316,7 @@ pub mod test_utils { storage, jwt_secret, local_p2p_node, + example_local_node_record(), SyncManager::dummy(), ) .await;