Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2a09cdb
implement signedPeerRecord
drHuangMHT Dec 31, 2024
b5d7aec
apply suggestions
drHuangMHT Jan 1, 2025
85d7496
Merge branch 'master' into identify-peer-record
drHuangMHT Jan 3, 2025
4d3d692
add test
drHuangMHT Jan 3, 2025
59f80f2
reduce diff
drHuangMHT Jan 4, 2025
ef89b5b
Merge branch 'master' into identify-peer-record
drHuangMHT Jan 4, 2025
12b9a29
rename symbols
drHuangMHT Jan 10, 2025
d3d8ae6
prefer addresses in signedPeerRecord
drHuangMHT Jan 10, 2025
59151b6
rename symbols
drHuangMHT Jan 11, 2025
9b6a139
lint and fmt
drHuangMHT Jan 11, 2025
cf30682
Merge branch 'master' into identify-peer-record
drHuangMHT Feb 5, 2025
e0e4d9c
rename identifiers
drHuangMHT Feb 13, 2025
d26a692
Merge branch 'master' into identify-peer-record
drHuangMHT Feb 13, 2025
92a49a5
reduce enum variant size with Box
drHuangMHT Feb 16, 2025
b35cbd3
simplify envelope deserialization
drHuangMHT Feb 16, 2025
f29c58d
document try_deserialize_signed_envelope
drHuangMHT Feb 16, 2025
ec8f31b
CI lint
drHuangMHT Feb 16, 2025
c73d37a
dedup constructor, reference count KeyType
drHuangMHT Feb 20, 2025
7cbb3f2
doc and fmt lint
drHuangMHT Feb 21, 2025
c013293
opaque type on new_with_key
drHuangMHT Feb 21, 2025
54aed27
changelog and manifest
drHuangMHT Feb 21, 2025
cd70cb9
workspace manifest
drHuangMHT Feb 21, 2025
8df2279
Merge branch 'master' into identify-peer-record
drHuangMHT Feb 21, 2025
9ec20a4
Merge branch 'master' into identify-peer-record
drHuangMHT Feb 25, 2025
c5db873
fix definition link, simplify record parsing
drHuangMHT Feb 28, 2025
b882ad5
revert core version bump
drHuangMHT Feb 28, 2025
0d27658
Merge branch 'master' into identify-peer-record
drHuangMHT Feb 28, 2025
ec67ce6
Merge branch 'master' into identify-peer-record
mergify[bot] Mar 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ libp2p-dcutr = { version = "0.13.0", path = "protocols/dcutr" }
libp2p-dns = { version = "0.43.0", path = "transports/dns" }
libp2p-floodsub = { version = "0.46.1", path = "protocols/floodsub" }
libp2p-gossipsub = { version = "0.48.1", path = "protocols/gossipsub" }
libp2p-identify = { version = "0.46.0", path = "protocols/identify" }
libp2p-identify = { version = "0.47.0", path = "protocols/identify" }
libp2p-identity = { version = "0.2.10" }
libp2p-kad = { version = "0.47.0", path = "protocols/kad" }
libp2p-mdns = { version = "0.47.0", path = "protocols/mdns" }
Expand Down
5 changes: 5 additions & 0 deletions protocols/identify/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.47.0

- Implement optional `signedPeerRecord` support for identify messages.
See [PR 5785](https://github.com/libp2p/rust-libp2p/pull/5785)

## 0.46.0

- Add `hide_listen_addrs` option to prevent leaking (local) listen addresses.
Expand Down
2 changes: 1 addition & 1 deletion protocols/identify/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "libp2p-identify"
edition = "2021"
rust-version = { workspace = true }
description = "Nodes identification protocol for libp2p"
version = "0.46.0"
version = "0.47.0"
authors = ["Parity Technologies <[email protected]>"]
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
Expand Down
68 changes: 59 additions & 9 deletions protocols/identify/src/behaviour.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,17 @@
use std::{
collections::{hash_map::Entry, HashMap, HashSet, VecDeque},
num::NonZeroUsize,
sync::Arc,
task::{Context, Poll},
time::Duration,
};

use libp2p_core::{
multiaddr, multiaddr::Protocol, transport::PortUse, ConnectedPoint, Endpoint, Multiaddr,
multiaddr::{self, Protocol},
transport::PortUse,
ConnectedPoint, Endpoint, Multiaddr,
};
use libp2p_identity::{PeerId, PublicKey};
use libp2p_identity::{Keypair, PeerId, PublicKey};
use libp2p_swarm::{
behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm},
ConnectionDenied, ConnectionId, DialError, ExternalAddresses, ListenAddresses,
Expand Down Expand Up @@ -117,8 +120,10 @@ pub struct Config {
/// Application-specific version of the protocol family used by the peer,
/// e.g. `ipfs/1.0.0` or `polkadot/1.0.0`.
protocol_version: String,
/// The public key of the local node. To report on the wire.
local_public_key: PublicKey,
/// The key of the local node. Only the public key will be report on the wire.
/// The behaviour will send signed [`PeerRecord`](libp2p_core::PeerRecord) in
/// its identify message only when supplied with a keypair.
local_key: Arc<KeyType>,
/// Name and version of the local peer implementation, similar to the
/// `User-Agent` header in the HTTP protocol.
///
Expand Down Expand Up @@ -156,12 +161,26 @@ pub struct Config {

impl Config {
/// Creates a new configuration for the identify [`Behaviour`] that
/// advertises the given protocol version and public key.
/// advertises the given protocol version and public key.
/// Use [`new_with_signed_peer_record`](Config::new_with_signed_peer_record) for
/// `signedPeerRecord` support.
pub fn new(protocol_version: String, local_public_key: PublicKey) -> Self {
Self::new_with_key(protocol_version, local_public_key)
}

/// Creates a new configuration for the identify [`Behaviour`] that
/// advertises the given protocol version and public key.
/// The private key will be used to sign [`PeerRecord`](libp2p_core::PeerRecord)
/// for verifiable address advertisement.
pub fn new_with_signed_peer_record(protocol_version: String, local_keypair: &Keypair) -> Self {
Self::new_with_key(protocol_version, local_keypair)
}

fn new_with_key(protocol_version: String, key: impl Into<KeyType>) -> Self {
Self {
protocol_version,
agent_version: format!("rust-libp2p/{}", env!("CARGO_PKG_VERSION")),
local_public_key,
local_key: Arc::new(key.into()),
interval: Duration::from_secs(5 * 60),
push_listen_addr_updates: false,
cache_size: 100,
Expand Down Expand Up @@ -209,7 +228,7 @@ impl Config {

/// Get the local public key of the Config.
pub fn local_public_key(&self) -> &PublicKey {
&self.local_public_key
self.local_key.public_key()
}

/// Get the agent version of the Config.
Expand Down Expand Up @@ -380,7 +399,7 @@ impl NetworkBehaviour for Behaviour {
Ok(Handler::new(
self.config.interval,
peer,
self.config.local_public_key.clone(),
self.config.local_key.clone(),
self.config.protocol_version.clone(),
self.config.agent_version.clone(),
remote_addr.clone(),
Expand Down Expand Up @@ -413,7 +432,7 @@ impl NetworkBehaviour for Behaviour {
Ok(Handler::new(
self.config.interval,
peer,
self.config.local_public_key.clone(),
self.config.local_key.clone(),
self.config.protocol_version.clone(),
self.config.agent_version.clone(),
// TODO: This is weird? That is the public address we dialed,
Expand Down Expand Up @@ -670,6 +689,37 @@ impl PeerCache {
}
}

#[derive(Debug, Clone)]
#[allow(clippy::large_enum_variant)]
pub(crate) enum KeyType {
PublicKey(PublicKey),
Keypair {
keypair: Keypair,
public_key: PublicKey,
},
}
impl From<PublicKey> for KeyType {
fn from(value: PublicKey) -> Self {
Self::PublicKey(value)
}
}
impl From<&Keypair> for KeyType {
fn from(value: &Keypair) -> Self {
Self::Keypair {
public_key: value.public(),
keypair: value.clone(),
}
}
}
impl KeyType {
pub(crate) fn public_key(&self) -> &PublicKey {
match &self {
KeyType::PublicKey(pubkey) => pubkey,
KeyType::Keypair { public_key, .. } => public_key,
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
8 changes: 8 additions & 0 deletions protocols/identify/src/generated/structs.proto
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,12 @@ message Identify {
optional bytes observedAddr = 4;

repeated string protocols = 3;

// signedPeerRecord contains a serialized SignedEnvelope containing a PeerRecord,
// signed by the sending node. It contains the same addresses as the listenAddrs field, but
// in a form that lets us share authenticated addrs with other peers.
// see https://github.com/libp2p/rust-libp2p/blob/8ac5b5aac5f5c25a85f1065e292deeaf58290189/core/src/generated/envelope.proto#L12
// and https://github.com/libp2p/rust-libp2p/blob/8ac5b5aac5f5c25a85f1065e292deeaf58290189/core/src/generated/peer_record.proto#L11
// for message definitions.
optional bytes signedPeerRecord = 8;
}
4 changes: 4 additions & 0 deletions protocols/identify/src/generated/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct Identify {
pub listenAddrs: Vec<Vec<u8>>,
pub observedAddr: Option<Vec<u8>>,
pub protocols: Vec<String>,
pub signedPeerRecord: Option<Vec<u8>>,
}

impl<'a> MessageRead<'a> for Identify {
Expand All @@ -35,6 +36,7 @@ impl<'a> MessageRead<'a> for Identify {
Ok(18) => msg.listenAddrs.push(r.read_bytes(bytes)?.to_owned()),
Ok(34) => msg.observedAddr = Some(r.read_bytes(bytes)?.to_owned()),
Ok(26) => msg.protocols.push(r.read_string(bytes)?.to_owned()),
Ok(66) => msg.signedPeerRecord = Some(r.read_bytes(bytes)?.to_owned()),
Ok(t) => { r.read_unknown(bytes, t)?; }
Err(e) => return Err(e),
}
Expand All @@ -52,6 +54,7 @@ impl MessageWrite for Identify {
+ self.listenAddrs.iter().map(|s| 1 + sizeof_len((s).len())).sum::<usize>()
+ self.observedAddr.as_ref().map_or(0, |m| 1 + sizeof_len((m).len()))
+ self.protocols.iter().map(|s| 1 + sizeof_len((s).len())).sum::<usize>()
+ self.signedPeerRecord.as_ref().map_or(0, |m| 1 + sizeof_len((m).len()))
}

fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
Expand All @@ -61,6 +64,7 @@ impl MessageWrite for Identify {
for s in &self.listenAddrs { w.write_with_tag(18, |w| w.write_bytes(&**s))?; }
if let Some(ref s) = self.observedAddr { w.write_with_tag(34, |w| w.write_bytes(&**s))?; }
for s in &self.protocols { w.write_with_tag(26, |w| w.write_string(&**s))?; }
if let Some(ref s) = self.signedPeerRecord { w.write_with_tag(66, |w| w.write_bytes(&**s))?; }
Ok(())
}
}
Expand Down
29 changes: 20 additions & 9 deletions protocols/identify/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

use std::{
collections::HashSet,
sync::Arc,
task::{Context, Poll},
time::Duration,
};
Expand All @@ -32,7 +33,7 @@ use libp2p_core::{
upgrade::{ReadyUpgrade, SelectUpgrade},
Multiaddr,
};
use libp2p_identity::{PeerId, PublicKey};
use libp2p_identity::PeerId;
use libp2p_swarm::{
handler::{
ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound,
Expand All @@ -45,8 +46,8 @@ use smallvec::SmallVec;
use tracing::Level;

use crate::{
protocol,
protocol::{Info, PushInfo, UpgradeError},
behaviour::KeyType,
protocol::{self, Info, PushInfo, UpgradeError},
PROTOCOL_NAME, PUSH_PROTOCOL_NAME,
};

Expand Down Expand Up @@ -80,8 +81,8 @@ pub struct Handler {
/// The interval of `trigger_next_identify`, i.e. the recurrent delay.
interval: Duration,

/// The public key of the local peer.
public_key: PublicKey,
/// The key of the local peer.
local_key: Arc<KeyType>,

/// Application-specific version of the protocol family used by the peer,
/// e.g. `ipfs/1.0.0` or `polkadot/1.0.0`.
Expand Down Expand Up @@ -125,10 +126,10 @@ pub enum Event {

impl Handler {
/// Creates a new `Handler`.
pub fn new(
pub(crate) fn new(
interval: Duration,
remote_peer_id: PeerId,
public_key: PublicKey,
local_key: Arc<KeyType>,
protocol_version: String,
agent_version: String,
observed_addr: Multiaddr,
Expand All @@ -144,7 +145,7 @@ impl Handler {
trigger_next_identify: Delay::new(Duration::ZERO),
exchanged_one_periodic_identify: false,
interval,
public_key,
local_key,
protocol_version,
agent_version,
observed_addr,
Expand Down Expand Up @@ -226,13 +227,23 @@ impl Handler {
}

fn build_info(&mut self) -> Info {
let signed_envelope = match self.local_key.as_ref() {
KeyType::PublicKey(_) => None,
KeyType::Keypair { keypair, .. } => libp2p_core::PeerRecord::new(
keypair,
Vec::from_iter(self.external_addresses.iter().cloned()),
)
.ok()
.map(|r| r.into_signed_envelope()),
};
Info {
public_key: self.public_key.clone(),
public_key: self.local_key.public_key().clone(),
protocol_version: self.protocol_version.clone(),
agent_version: self.agent_version.clone(),
listen_addrs: Vec::from_iter(self.external_addresses.iter().cloned()),
protocols: Vec::from_iter(self.local_supported_protocols.iter().cloned()),
observed_addr: self.observed_addr.clone(),
signed_peer_record: signed_envelope,
}
}

Expand Down
Loading
Loading