mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: add secp256k1 feature in reth-network-peers (#8712)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
committed by
GitHub
parent
ee7a829a12
commit
8717ed757b
@ -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" }
|
||||
|
||||
@ -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"] }
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"] }
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"]
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
@ -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..]);
|
||||
|
||||
Reference in New Issue
Block a user