feat: add secp256k1 feature in reth-network-peers (#8712)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Panagiotis Ganelis
2024-06-10 13:46:22 +02:00
committed by GitHub
parent ee7a829a12
commit 8717ed757b
9 changed files with 36 additions and 13 deletions

View File

@ -281,7 +281,7 @@ reth-net-common = { path = "crates/net/common" }
reth-net-nat = { path = "crates/net/nat" }
reth-network = { path = "crates/net/network" }
reth-network-api = { path = "crates/net/network-api" }
reth-network-peers = { path = "crates/net/peers" }
reth-network-peers = { path = "crates/net/peers", default-features = false }
reth-network-p2p = { path = "crates/net/p2p" }
reth-nippy-jar = { path = "crates/storage/nippy-jar" }
reth-node-api = { path = "crates/node/api" }

View File

@ -16,7 +16,7 @@ workspace = true
reth-primitives.workspace = true
reth-net-common.workspace = true
reth-net-nat.workspace = true
reth-network-peers.workspace = true
reth-network-peers = { workspace = true, features = ["secp256k1"] }
# ethereum
alloy-rlp = { workspace = true, features = ["derive"] }

View File

@ -15,7 +15,7 @@ workspace = true
# reth
reth-primitives.workspace = true
reth-metrics.workspace = true
reth-network-peers.workspace = true
reth-network-peers = { workspace = true, features = ["secp256k1"] }
# ethereum
alloy-rlp.workspace = true

View File

@ -15,7 +15,7 @@ workspace = true
# reth
reth-primitives.workspace = true
reth-net-common.workspace = true
reth-network-peers.workspace = true
reth-network-peers = { workspace = true, features = ["secp256k1"] }
# ethereum
secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recovery", "serde"] }

View File

@ -13,7 +13,7 @@ workspace = true
[dependencies]
reth-primitives.workspace = true
reth-net-common.workspace = true
reth-network-peers.workspace = true
reth-network-peers = { workspace = true, features = ["secp256k1"] }
alloy-rlp = { workspace = true, features = ["derive"] }
futures.workspace = true

View File

@ -19,8 +19,8 @@ alloy-rlp = { workspace = true, features = ["derive"] }
enr.workspace = true
# crypto
secp256k1.workspace = true
secp256k1 = { workspace = true, optional = true }
# misc
serde_with.workspace = true
thiserror.workspace = true
@ -32,3 +32,6 @@ alloy-primitives = { workspace = true, features = ["rand"] }
rand.workspace = true
secp256k1 = { workspace = true, features = ["rand"] }
serde_json.workspace = true
[features]
secp256k1 = ["dep:secp256k1"]

View File

@ -49,7 +49,7 @@
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
use alloy_primitives::B512;
use std::{net::IpAddr, str::FromStr};
use std::str::FromStr;
// Re-export PeerId for ease of use.
pub use enr::Enr;
@ -69,10 +69,12 @@ pub use trusted_peer::TrustedPeer;
/// `SECP256K1_TAG_PUBKEY_UNCOMPRESSED` = `0x04`
///
/// See: <https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1.h#L211>
#[cfg(feature = "secp256k1")]
const SECP256K1_TAG_PUBKEY_UNCOMPRESSED: u8 = 4;
/// Converts a [`secp256k1::PublicKey`] to a [`PeerId`] by stripping the
/// `SECP256K1_TAG_PUBKEY_UNCOMPRESSED` tag and storing the rest of the slice in the [`PeerId`].
#[cfg(feature = "secp256k1")]
#[inline]
pub fn pk2id(pk: &secp256k1::PublicKey) -> PeerId {
PeerId::from_slice(&pk.serialize_uncompressed()[1..])
@ -80,6 +82,7 @@ pub fn pk2id(pk: &secp256k1::PublicKey) -> PeerId {
/// Converts a [`PeerId`] to a [`secp256k1::PublicKey`] by prepending the [`PeerId`] bytes with the
/// `SECP256K1_TAG_PUBKEY_UNCOMPRESSED` tag.
#[cfg(feature = "secp256k1")]
#[inline]
pub fn id2pk(id: PeerId) -> Result<secp256k1::PublicKey, secp256k1::Error> {
// NOTE: B512 is used as a PeerId because 512 bits is enough to represent an uncompressed
@ -97,7 +100,8 @@ pub fn id2pk(id: PeerId) -> Result<secp256k1::PublicKey, secp256k1::Error> {
pub enum AnyNode {
/// An "enode:" peer with full ip
NodeRecord(NodeRecord),
/// An "enr:"
#[cfg(feature = "secp256k1")]
/// An "enr:" peer
Enr(Enr<secp256k1::SecretKey>),
/// An incomplete "enode" with only a peer id
PeerId(PeerId),
@ -108,6 +112,7 @@ impl AnyNode {
pub fn peer_id(&self) -> PeerId {
match self {
Self::NodeRecord(record) => record.id,
#[cfg(feature = "secp256k1")]
Self::Enr(enr) => pk2id(&enr.public_key()),
Self::PeerId(peer_id) => *peer_id,
}
@ -117,9 +122,13 @@ impl AnyNode {
pub fn node_record(&self) -> Option<NodeRecord> {
match self {
Self::NodeRecord(record) => Some(*record),
#[cfg(feature = "secp256k1")]
Self::Enr(enr) => {
let node_record = NodeRecord {
address: enr.ip4().map(IpAddr::from).or_else(|| enr.ip6().map(IpAddr::from))?,
address: enr
.ip4()
.map(std::net::IpAddr::from)
.or_else(|| enr.ip6().map(std::net::IpAddr::from))?,
tcp_port: enr.tcp4().or_else(|| enr.tcp6())?,
udp_port: enr.udp4().or_else(|| enr.udp6())?,
id: pk2id(&enr.public_key()),
@ -138,6 +147,7 @@ impl From<NodeRecord> for AnyNode {
}
}
#[cfg(feature = "secp256k1")]
impl From<Enr<secp256k1::SecretKey>> for AnyNode {
fn from(value: Enr<secp256k1::SecretKey>) -> Self {
Self::Enr(value)
@ -158,6 +168,7 @@ impl FromStr for AnyNode {
}
return Err(format!("invalid public key: {rem}"))
}
#[cfg(feature = "secp256k1")]
if s.starts_with("enr:") {
return Enr::from_str(s).map(AnyNode::Enr)
}
@ -169,6 +180,7 @@ impl std::fmt::Display for AnyNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NodeRecord(record) => write!(f, "{record}"),
#[cfg(feature = "secp256k1")]
Self::Enr(enr) => write!(f, "{enr}"),
Self::PeerId(peer_id) => {
write!(f, "enode://{}", alloy_primitives::hex::encode(peer_id.as_slice()))
@ -235,12 +247,13 @@ impl<T> WithPeerId<Option<T>> {
mod tests {
use super::*;
#[cfg(feature = "secp256k1")]
#[test]
fn test_node_record_parse() {
let url = "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@10.3.58.6:30303?discport=30301";
let node: AnyNode = url.parse().unwrap();
assert_eq!(node, AnyNode::NodeRecord(NodeRecord {
address: IpAddr::V4([10,3,58,6].into()),
address: std::net::IpAddr::V4([10,3,58,6].into()),
tcp_port: 30303,
udp_port: 30301,
id: "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0".parse().unwrap(),
@ -261,6 +274,7 @@ mod tests {
}
// <https://eips.ethereum.org/EIPS/eip-778>
#[cfg(feature = "secp256k1")]
#[test]
fn test_enr_parse() {
let url = "enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8";
@ -275,6 +289,7 @@ mod tests {
}
#[test]
#[cfg(feature = "secp256k1")]
fn pk2id2pk() {
let prikey = secp256k1::SecretKey::new(&mut rand::thread_rng());
let pubkey = secp256k1::PublicKey::from_secret_key(secp256k1::SECP256K1, &prikey);

View File

@ -8,11 +8,13 @@ use std::{
str::FromStr,
};
use crate::{pk2id, PeerId};
use crate::PeerId;
use alloy_rlp::{RlpDecodable, RlpEncodable};
use enr::Enr;
use serde_with::{DeserializeFromStr, SerializeDisplay};
#[cfg(feature = "secp256k1")]
use enr::Enr;
/// Represents a ENR in discovery.
///
/// Note: this is only an excerpt of the [`NodeRecord`] data structure.
@ -40,6 +42,7 @@ pub struct NodeRecord {
}
impl NodeRecord {
#[cfg(feature = "secp256k1")]
/// Derive the [`NodeRecord`] from the secret key and addr
pub fn from_secret_key(addr: SocketAddr, sk: &secp256k1::SecretKey) -> Self {
let pk = secp256k1::PublicKey::from_secret_key(secp256k1::SECP256K1, sk);
@ -169,6 +172,7 @@ impl FromStr for NodeRecord {
}
}
#[cfg(feature = "secp256k1")]
impl TryFrom<&Enr<secp256k1::SecretKey>> for NodeRecord {
type Error = NodeRecordParseError;
@ -186,7 +190,7 @@ impl TryFrom<&Enr<secp256k1::SecretKey>> for NodeRecord {
return Err(NodeRecordParseError::InvalidUrl("tcp port missing".to_string()))
};
let id = pk2id(&enr.public_key());
let id = crate::pk2id(&enr.public_key());
Ok(Self { address, tcp_port, udp_port, id }.into_ipv4_mapped())
}

View File

@ -33,6 +33,7 @@ pub struct TrustedPeer {
impl TrustedPeer {
/// Derive the [`NodeRecord`] from the secret key and addr
#[cfg(feature = "secp256k1")]
pub fn from_secret_key(host: Host, port: u16, sk: &secp256k1::SecretKey) -> Self {
let pk = secp256k1::PublicKey::from_secret_key(secp256k1::SECP256K1, sk);
let id = PeerId::from_slice(&pk.serialize_uncompressed()[1..]);