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-net-nat = { path = "crates/net/nat" }
|
||||||
reth-network = { path = "crates/net/network" }
|
reth-network = { path = "crates/net/network" }
|
||||||
reth-network-api = { path = "crates/net/network-api" }
|
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-network-p2p = { path = "crates/net/p2p" }
|
||||||
reth-nippy-jar = { path = "crates/storage/nippy-jar" }
|
reth-nippy-jar = { path = "crates/storage/nippy-jar" }
|
||||||
reth-node-api = { path = "crates/node/api" }
|
reth-node-api = { path = "crates/node/api" }
|
||||||
|
|||||||
@ -16,7 +16,7 @@ workspace = true
|
|||||||
reth-primitives.workspace = true
|
reth-primitives.workspace = true
|
||||||
reth-net-common.workspace = true
|
reth-net-common.workspace = true
|
||||||
reth-net-nat.workspace = true
|
reth-net-nat.workspace = true
|
||||||
reth-network-peers.workspace = true
|
reth-network-peers = { workspace = true, features = ["secp256k1"] }
|
||||||
|
|
||||||
# ethereum
|
# ethereum
|
||||||
alloy-rlp = { workspace = true, features = ["derive"] }
|
alloy-rlp = { workspace = true, features = ["derive"] }
|
||||||
|
|||||||
@ -15,7 +15,7 @@ workspace = true
|
|||||||
# reth
|
# reth
|
||||||
reth-primitives.workspace = true
|
reth-primitives.workspace = true
|
||||||
reth-metrics.workspace = true
|
reth-metrics.workspace = true
|
||||||
reth-network-peers.workspace = true
|
reth-network-peers = { workspace = true, features = ["secp256k1"] }
|
||||||
|
|
||||||
# ethereum
|
# ethereum
|
||||||
alloy-rlp.workspace = true
|
alloy-rlp.workspace = true
|
||||||
|
|||||||
@ -15,7 +15,7 @@ workspace = true
|
|||||||
# reth
|
# reth
|
||||||
reth-primitives.workspace = true
|
reth-primitives.workspace = true
|
||||||
reth-net-common.workspace = true
|
reth-net-common.workspace = true
|
||||||
reth-network-peers.workspace = true
|
reth-network-peers = { workspace = true, features = ["secp256k1"] }
|
||||||
|
|
||||||
# ethereum
|
# ethereum
|
||||||
secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recovery", "serde"] }
|
secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recovery", "serde"] }
|
||||||
|
|||||||
@ -13,7 +13,7 @@ workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
reth-primitives.workspace = true
|
reth-primitives.workspace = true
|
||||||
reth-net-common.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"] }
|
alloy-rlp = { workspace = true, features = ["derive"] }
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
|
|||||||
@ -19,8 +19,8 @@ alloy-rlp = { workspace = true, features = ["derive"] }
|
|||||||
enr.workspace = true
|
enr.workspace = true
|
||||||
|
|
||||||
# crypto
|
# crypto
|
||||||
secp256k1.workspace = true
|
|
||||||
|
|
||||||
|
secp256k1 = { workspace = true, optional = true }
|
||||||
# misc
|
# misc
|
||||||
serde_with.workspace = true
|
serde_with.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
@ -32,3 +32,6 @@ alloy-primitives = { workspace = true, features = ["rand"] }
|
|||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
secp256k1 = { workspace = true, features = ["rand"] }
|
secp256k1 = { workspace = true, features = ["rand"] }
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
secp256k1 = ["dep:secp256k1"]
|
||||||
|
|||||||
@ -49,7 +49,7 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||||
|
|
||||||
use alloy_primitives::B512;
|
use alloy_primitives::B512;
|
||||||
use std::{net::IpAddr, str::FromStr};
|
use std::str::FromStr;
|
||||||
|
|
||||||
// Re-export PeerId for ease of use.
|
// Re-export PeerId for ease of use.
|
||||||
pub use enr::Enr;
|
pub use enr::Enr;
|
||||||
@ -69,10 +69,12 @@ pub use trusted_peer::TrustedPeer;
|
|||||||
/// `SECP256K1_TAG_PUBKEY_UNCOMPRESSED` = `0x04`
|
/// `SECP256K1_TAG_PUBKEY_UNCOMPRESSED` = `0x04`
|
||||||
///
|
///
|
||||||
/// See: <https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1.h#L211>
|
/// See: <https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1.h#L211>
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
const SECP256K1_TAG_PUBKEY_UNCOMPRESSED: u8 = 4;
|
const SECP256K1_TAG_PUBKEY_UNCOMPRESSED: u8 = 4;
|
||||||
|
|
||||||
/// Converts a [`secp256k1::PublicKey`] to a [`PeerId`] by stripping the
|
/// 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`].
|
/// `SECP256K1_TAG_PUBKEY_UNCOMPRESSED` tag and storing the rest of the slice in the [`PeerId`].
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pk2id(pk: &secp256k1::PublicKey) -> PeerId {
|
pub fn pk2id(pk: &secp256k1::PublicKey) -> PeerId {
|
||||||
PeerId::from_slice(&pk.serialize_uncompressed()[1..])
|
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
|
/// Converts a [`PeerId`] to a [`secp256k1::PublicKey`] by prepending the [`PeerId`] bytes with the
|
||||||
/// `SECP256K1_TAG_PUBKEY_UNCOMPRESSED` tag.
|
/// `SECP256K1_TAG_PUBKEY_UNCOMPRESSED` tag.
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id2pk(id: PeerId) -> Result<secp256k1::PublicKey, secp256k1::Error> {
|
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
|
// 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 {
|
pub enum AnyNode {
|
||||||
/// An "enode:" peer with full ip
|
/// An "enode:" peer with full ip
|
||||||
NodeRecord(NodeRecord),
|
NodeRecord(NodeRecord),
|
||||||
/// An "enr:"
|
#[cfg(feature = "secp256k1")]
|
||||||
|
/// An "enr:" peer
|
||||||
Enr(Enr<secp256k1::SecretKey>),
|
Enr(Enr<secp256k1::SecretKey>),
|
||||||
/// An incomplete "enode" with only a peer id
|
/// An incomplete "enode" with only a peer id
|
||||||
PeerId(PeerId),
|
PeerId(PeerId),
|
||||||
@ -108,6 +112,7 @@ impl AnyNode {
|
|||||||
pub fn peer_id(&self) -> PeerId {
|
pub fn peer_id(&self) -> PeerId {
|
||||||
match self {
|
match self {
|
||||||
Self::NodeRecord(record) => record.id,
|
Self::NodeRecord(record) => record.id,
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
Self::Enr(enr) => pk2id(&enr.public_key()),
|
Self::Enr(enr) => pk2id(&enr.public_key()),
|
||||||
Self::PeerId(peer_id) => *peer_id,
|
Self::PeerId(peer_id) => *peer_id,
|
||||||
}
|
}
|
||||||
@ -117,9 +122,13 @@ impl AnyNode {
|
|||||||
pub fn node_record(&self) -> Option<NodeRecord> {
|
pub fn node_record(&self) -> Option<NodeRecord> {
|
||||||
match self {
|
match self {
|
||||||
Self::NodeRecord(record) => Some(*record),
|
Self::NodeRecord(record) => Some(*record),
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
Self::Enr(enr) => {
|
Self::Enr(enr) => {
|
||||||
let node_record = NodeRecord {
|
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())?,
|
tcp_port: enr.tcp4().or_else(|| enr.tcp6())?,
|
||||||
udp_port: enr.udp4().or_else(|| enr.udp6())?,
|
udp_port: enr.udp4().or_else(|| enr.udp6())?,
|
||||||
id: pk2id(&enr.public_key()),
|
id: pk2id(&enr.public_key()),
|
||||||
@ -138,6 +147,7 @@ impl From<NodeRecord> for AnyNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
impl From<Enr<secp256k1::SecretKey>> for AnyNode {
|
impl From<Enr<secp256k1::SecretKey>> for AnyNode {
|
||||||
fn from(value: Enr<secp256k1::SecretKey>) -> Self {
|
fn from(value: Enr<secp256k1::SecretKey>) -> Self {
|
||||||
Self::Enr(value)
|
Self::Enr(value)
|
||||||
@ -158,6 +168,7 @@ impl FromStr for AnyNode {
|
|||||||
}
|
}
|
||||||
return Err(format!("invalid public key: {rem}"))
|
return Err(format!("invalid public key: {rem}"))
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
if s.starts_with("enr:") {
|
if s.starts_with("enr:") {
|
||||||
return Enr::from_str(s).map(AnyNode::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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::NodeRecord(record) => write!(f, "{record}"),
|
Self::NodeRecord(record) => write!(f, "{record}"),
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
Self::Enr(enr) => write!(f, "{enr}"),
|
Self::Enr(enr) => write!(f, "{enr}"),
|
||||||
Self::PeerId(peer_id) => {
|
Self::PeerId(peer_id) => {
|
||||||
write!(f, "enode://{}", alloy_primitives::hex::encode(peer_id.as_slice()))
|
write!(f, "enode://{}", alloy_primitives::hex::encode(peer_id.as_slice()))
|
||||||
@ -235,12 +247,13 @@ impl<T> WithPeerId<Option<T>> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_node_record_parse() {
|
fn test_node_record_parse() {
|
||||||
let url = "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@10.3.58.6:30303?discport=30301";
|
let url = "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@10.3.58.6:30303?discport=30301";
|
||||||
let node: AnyNode = url.parse().unwrap();
|
let node: AnyNode = url.parse().unwrap();
|
||||||
assert_eq!(node, AnyNode::NodeRecord(NodeRecord {
|
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,
|
tcp_port: 30303,
|
||||||
udp_port: 30301,
|
udp_port: 30301,
|
||||||
id: "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0".parse().unwrap(),
|
id: "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0".parse().unwrap(),
|
||||||
@ -261,6 +274,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// <https://eips.ethereum.org/EIPS/eip-778>
|
// <https://eips.ethereum.org/EIPS/eip-778>
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_enr_parse() {
|
fn test_enr_parse() {
|
||||||
let url = "enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8";
|
let url = "enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8";
|
||||||
@ -275,6 +289,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
fn pk2id2pk() {
|
fn pk2id2pk() {
|
||||||
let prikey = secp256k1::SecretKey::new(&mut rand::thread_rng());
|
let prikey = secp256k1::SecretKey::new(&mut rand::thread_rng());
|
||||||
let pubkey = secp256k1::PublicKey::from_secret_key(secp256k1::SECP256K1, &prikey);
|
let pubkey = secp256k1::PublicKey::from_secret_key(secp256k1::SECP256K1, &prikey);
|
||||||
|
|||||||
@ -8,11 +8,13 @@ use std::{
|
|||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{pk2id, PeerId};
|
use crate::PeerId;
|
||||||
use alloy_rlp::{RlpDecodable, RlpEncodable};
|
use alloy_rlp::{RlpDecodable, RlpEncodable};
|
||||||
use enr::Enr;
|
|
||||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||||
|
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
|
use enr::Enr;
|
||||||
|
|
||||||
/// Represents a ENR in discovery.
|
/// Represents a ENR in discovery.
|
||||||
///
|
///
|
||||||
/// Note: this is only an excerpt of the [`NodeRecord`] data structure.
|
/// Note: this is only an excerpt of the [`NodeRecord`] data structure.
|
||||||
@ -40,6 +42,7 @@ pub struct NodeRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NodeRecord {
|
impl NodeRecord {
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
/// Derive the [`NodeRecord`] from the secret key and addr
|
/// Derive the [`NodeRecord`] from the secret key and addr
|
||||||
pub fn from_secret_key(addr: SocketAddr, sk: &secp256k1::SecretKey) -> Self {
|
pub fn from_secret_key(addr: SocketAddr, sk: &secp256k1::SecretKey) -> Self {
|
||||||
let pk = secp256k1::PublicKey::from_secret_key(secp256k1::SECP256K1, sk);
|
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 {
|
impl TryFrom<&Enr<secp256k1::SecretKey>> for NodeRecord {
|
||||||
type Error = NodeRecordParseError;
|
type Error = NodeRecordParseError;
|
||||||
|
|
||||||
@ -186,7 +190,7 @@ impl TryFrom<&Enr<secp256k1::SecretKey>> for NodeRecord {
|
|||||||
return Err(NodeRecordParseError::InvalidUrl("tcp port missing".to_string()))
|
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())
|
Ok(Self { address, tcp_port, udp_port, id }.into_ipv4_mapped())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,7 @@ pub struct TrustedPeer {
|
|||||||
|
|
||||||
impl TrustedPeer {
|
impl TrustedPeer {
|
||||||
/// Derive the [`NodeRecord`] from the secret key and addr
|
/// 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 {
|
pub fn from_secret_key(host: Host, port: u16, sk: &secp256k1::SecretKey) -> Self {
|
||||||
let pk = secp256k1::PublicKey::from_secret_key(secp256k1::SECP256K1, sk);
|
let pk = secp256k1::PublicKey::from_secret_key(secp256k1::SECP256K1, sk);
|
||||||
let id = PeerId::from_slice(&pk.serialize_uncompressed()[1..]);
|
let id = PeerId::from_slice(&pk.serialize_uncompressed()[1..]);
|
||||||
|
|||||||
Reference in New Issue
Block a user