From 43094180a50ded998250d291caeb1a64d51b53ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 9 Feb 2026 16:01:46 -0300 Subject: [PATCH] fix: parse ip and ip6 as RLP-encoded values --- crates/net/p2p/src/lib.rs | 69 +++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/crates/net/p2p/src/lib.rs b/crates/net/p2p/src/lib.rs index 6d04494..9951e6a 100644 --- a/crates/net/p2p/src/lib.rs +++ b/crates/net/p2p/src/lib.rs @@ -428,9 +428,9 @@ pub(crate) fn schedule_peer_redial(retry_tx: mpsc::UnboundedSender } pub struct Bootnode { - ip: IpAddr, - quic_port: u16, - public_key: PublicKey, + pub(crate) ip: IpAddr, + pub(crate) quic_port: u16, + pub(crate) public_key: PublicKey, } pub fn parse_enrs(enrs: Vec) -> Vec { @@ -464,16 +464,14 @@ pub fn parse_enrs(enrs: Vec) -> Vec { .iter() .find(|(key, _)| key.as_ref() == b"ip") .map(|(_, bytes)| { - let octets: [u8; 4] = bytes.as_ref().try_into().expect("invalid IPv4 address"); - IpAddr::from(Ipv4Addr::from(octets)) + IpAddr::from(Ipv4Addr::decode(bytes.as_ref()).expect("invalid IPv4 address")) }); let ipv6 = record .pairs .iter() .find(|(key, _)| key.as_ref() == b"ip6") .map(|(_, bytes)| { - let octets: [u8; 16] = bytes.as_ref().try_into().expect("invalid IPv6 address"); - IpAddr::from(Ipv6Addr::from(octets)) + IpAddr::from(Ipv6Addr::decode(bytes.as_ref()).expect("invalid IPv6 address")) }); // Prefer IPv4 if both are present @@ -516,3 +514,60 @@ fn compute_message_id(message: &libp2p::gossipsub::Message) -> libp2p::gossipsub let hash = hasher.finalize(); libp2p::gossipsub::MessageId(hash[..20].to_vec()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_enrs_extracts_ip_port_and_public_key() { + // Values taken from a local devnet run with lean-quickstart + let enrs = vec![ + "enr:-IW4QGGifTt9ypyMtChDISUNX3z4z5iPdiEPOmBoILvnDuWIKbWVmKXxZERPnw0piQyaBNCENFEPoIi-vxsnsrBig9MBgmlkgnY0gmlwhH8AAAGEcXVpY4IjKYlzZWNwMjU2azGhAhMMnGF1rmIPQ9tWgqfkNmvsG-aIyc9EJU5JFo3Tegys".to_string(), + "enr:-IW4QPjoNZjNpzdjOqAR2rGguVAWmqpNCUCfbr-pp3rr6Dk6YO2KK5VWARr7BGr8BdmGmG75cBeVC2buzvtQ_nEWLKEBgmlkgnY0gmlwhH8AAAGEcXVpY4IjKolzZWNwMjU2azGhA5_HplOwUZ8wpF4O3g4CBsjRMI6kQYT7ph5LkeKzLgTS".to_string(), + "enr:-IW4QNQN_PFdTfuYLGmdAWNivEJLT2tSZtn5jdBOImvh0QlLAJ1p8wHvvfD7aOa1lH88oJ8ddGK_a_FWqAQT_QY4qdMBgmlkgnY0gmlwhH8AAAGEcXVpY4IjK4lzZWNwMjU2azGhA7NTxgfOmGE2EQa4HhsXxFOeHdTLYIc2MEBczymm9IUN".to_string(), + "enr:-IW4QI9EXVDvUIxTrCV51Gs2RtpmZu71S7ZP7RRg1OoSBVvGFeXkc5WleBffXwTcWX1Qa9F_N6MhH28TsGFhXkMCGvUBgmlkgnY0gmlwhH8AAAGEcXVpY4IjL4lzZWNwMjU2azGhA6Dm1X9PyyCNAm3RUGcZtG5U3imbj_MDPU5CtPnpeaKS".to_string(), + ]; + + let bootnodes = parse_enrs(enrs); + + assert_eq!(bootnodes.len(), 4); + + // All ENRs encode 127.0.0.1 as the IPv4 address + for bootnode in &bootnodes { + assert_eq!(bootnode.ip, IpAddr::from(Ipv4Addr::LOCALHOST)); + } + + // Each ENR encodes a distinct QUIC port + assert_eq!(bootnodes[0].quic_port, 9001); + assert_eq!(bootnodes[1].quic_port, 9002); + assert_eq!(bootnodes[2].quic_port, 9003); + assert_eq!(bootnodes[3].quic_port, 9007); + + // Verify the secp256k1 public keys (33-byte compressed format) + let expected_pubkeys: Vec<[u8; 33]> = vec![ + hex::decode("02130c9c6175ae620f43db5682a7e4366bec1be688c9cf44254e49168dd37a0cac") + .unwrap() + .try_into() + .unwrap(), + hex::decode("039fc7a653b0519f30a45e0ede0e0206c8d1308ea44184fba61e4b91e2b32e04d2") + .unwrap() + .try_into() + .unwrap(), + hex::decode("03b353c607ce9861361106b81e1b17c4539e1dd4cb60873630405ccf29a6f4850d") + .unwrap() + .try_into() + .unwrap(), + hex::decode("03a0e6d57f4fcb208d026dd1506719b46e54de299b8ff3033d4e42b4f9e979a292") + .unwrap() + .try_into() + .unwrap(), + ]; + + for (bootnode, expected) in bootnodes.iter().zip(expected_pubkeys.iter()) { + let secp_key = secp256k1::PublicKey::try_from_bytes(expected).unwrap(); + let expected_key: PublicKey = secp_key.into(); + assert_eq!(bootnode.public_key, expected_key); + } + } +}