mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-03 05:01:24 +00:00
fix(identify): handle partial push
messages
According to the [spec], all fields within push messages are optional. To handle missing fields, we deserialize push messages into a different struct, patch the previously received, full identify message with it and emit this as the new info.
Previously, we failed to parse the message which causes incompatibility with js-libp2p nodes as they don't send the public key in push messages. See 88c47f51f9/packages/libp2p/src/identify/identify.ts (L205-L210)
.
[spec]: https://github.com/libp2p/specs/tree/master/identify#identifypush
Pull-Request: #4495.
This commit is contained in:
parent
95890b550b
commit
ad45d23d94
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2610,7 +2610,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libp2p-identify"
|
name = "libp2p-identify"
|
||||||
version = "0.43.0"
|
version = "0.43.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"asynchronous-codec",
|
"asynchronous-codec",
|
||||||
|
@ -81,7 +81,7 @@ libp2p-deflate = { version = "0.40.0", path = "transports/deflate" }
|
|||||||
libp2p-dns = { version = "0.40.1", path = "transports/dns" }
|
libp2p-dns = { version = "0.40.1", path = "transports/dns" }
|
||||||
libp2p-floodsub = { version = "0.43.0", path = "protocols/floodsub" }
|
libp2p-floodsub = { version = "0.43.0", path = "protocols/floodsub" }
|
||||||
libp2p-gossipsub = { version = "0.45.1", path = "protocols/gossipsub" }
|
libp2p-gossipsub = { version = "0.45.1", path = "protocols/gossipsub" }
|
||||||
libp2p-identify = { version = "0.43.0", path = "protocols/identify" }
|
libp2p-identify = { version = "0.43.1", path = "protocols/identify" }
|
||||||
libp2p-identity = { version = "0.2.3" }
|
libp2p-identity = { version = "0.2.3" }
|
||||||
libp2p-kad = { version = "0.44.5", path = "protocols/kad" }
|
libp2p-kad = { version = "0.44.5", path = "protocols/kad" }
|
||||||
libp2p-mdns = { version = "0.44.0", path = "protocols/mdns" }
|
libp2p-mdns = { version = "0.44.0", path = "protocols/mdns" }
|
||||||
|
@ -1,4 +1,12 @@
|
|||||||
## 0.43.0
|
## 0.43.1 - unreleased
|
||||||
|
|
||||||
|
- Handle partial push messages.
|
||||||
|
Previously, push messages with partial information were ignored.
|
||||||
|
See [PR 4495].
|
||||||
|
|
||||||
|
[PR 4495]: https://github.com/libp2p/rust-libp2p/pull/4495
|
||||||
|
|
||||||
|
## 0.43.0
|
||||||
|
|
||||||
- Observed addresses (aka. external address candidates) of the local node, reported by a remote node via `libp2p-identify`, are no longer automatically considered confirmed external addresses, in other words they are no longer trusted by default.
|
- Observed addresses (aka. external address candidates) of the local node, reported by a remote node via `libp2p-identify`, are no longer automatically considered confirmed external addresses, in other words they are no longer trusted by default.
|
||||||
Instead users need to confirm the reported observed address either manually, or by using `libp2p-autonat`.
|
Instead users need to confirm the reported observed address either manually, or by using `libp2p-autonat`.
|
||||||
|
@ -3,7 +3,7 @@ name = "libp2p-identify"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
description = "Nodes identifcation protocol for libp2p"
|
description = "Nodes identifcation protocol for libp2p"
|
||||||
version = "0.43.0"
|
version = "0.43.1"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/libp2p/rust-libp2p"
|
repository = "https://github.com/libp2p/rust-libp2p"
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
// DEALINGS IN THE SOFTWARE.
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
use crate::protocol::{Identify, InboundPush, Info, OutboundPush, Push, UpgradeError};
|
use crate::protocol::{Identify, InboundPush, OutboundPush, Push, UpgradeError};
|
||||||
|
use crate::protocol::{Info, PushInfo};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use futures::future::BoxFuture;
|
use futures::future::BoxFuture;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
@ -48,7 +49,7 @@ use std::{io, task::Context, task::Poll, time::Duration};
|
|||||||
/// permitting the underlying connection to be closed.
|
/// permitting the underlying connection to be closed.
|
||||||
pub struct Handler {
|
pub struct Handler {
|
||||||
remote_peer_id: PeerId,
|
remote_peer_id: PeerId,
|
||||||
inbound_identify_push: Option<BoxFuture<'static, Result<Info, UpgradeError>>>,
|
inbound_identify_push: Option<BoxFuture<'static, Result<PushInfo, UpgradeError>>>,
|
||||||
/// Pending events to yield.
|
/// Pending events to yield.
|
||||||
events: SmallVec<
|
events: SmallVec<
|
||||||
[ConnectionHandlerEvent<Either<Identify, Push<OutboundPush>>, (), Event, io::Error>; 4],
|
[ConnectionHandlerEvent<Either<Identify, Push<OutboundPush>>, (), Event, io::Error>; 4],
|
||||||
@ -80,6 +81,9 @@ pub struct Handler {
|
|||||||
/// Address observed by or for the remote.
|
/// Address observed by or for the remote.
|
||||||
observed_addr: Multiaddr,
|
observed_addr: Multiaddr,
|
||||||
|
|
||||||
|
/// Identify information about the remote peer.
|
||||||
|
remote_info: Option<Info>,
|
||||||
|
|
||||||
local_supported_protocols: SupportedProtocols,
|
local_supported_protocols: SupportedProtocols,
|
||||||
remote_supported_protocols: HashSet<StreamProtocol>,
|
remote_supported_protocols: HashSet<StreamProtocol>,
|
||||||
external_addresses: HashSet<Multiaddr>,
|
external_addresses: HashSet<Multiaddr>,
|
||||||
@ -133,6 +137,7 @@ impl Handler {
|
|||||||
observed_addr,
|
observed_addr,
|
||||||
local_supported_protocols: SupportedProtocols::default(),
|
local_supported_protocols: SupportedProtocols::default(),
|
||||||
remote_supported_protocols: HashSet::default(),
|
remote_supported_protocols: HashSet::default(),
|
||||||
|
remote_info: Default::default(),
|
||||||
external_addresses,
|
external_addresses,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,7 +181,8 @@ impl Handler {
|
|||||||
) {
|
) {
|
||||||
match output {
|
match output {
|
||||||
future::Either::Left(remote_info) => {
|
future::Either::Left(remote_info) => {
|
||||||
self.update_supported_protocols_for_remote(&remote_info);
|
self.handle_incoming_info(&remote_info);
|
||||||
|
|
||||||
self.events
|
self.events
|
||||||
.push(ConnectionHandlerEvent::NotifyBehaviour(Event::Identified(
|
.push(ConnectionHandlerEvent::NotifyBehaviour(Event::Identified(
|
||||||
remote_info,
|
remote_info,
|
||||||
@ -213,6 +219,12 @@ impl Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_incoming_info(&mut self, info: &Info) {
|
||||||
|
self.remote_info.replace(info.clone());
|
||||||
|
|
||||||
|
self.update_supported_protocols_for_remote(info);
|
||||||
|
}
|
||||||
|
|
||||||
fn update_supported_protocols_for_remote(&mut self, remote_info: &Info) {
|
fn update_supported_protocols_for_remote(&mut self, remote_info: &Info) {
|
||||||
let new_remote_protocols = HashSet::from_iter(remote_info.protocols.clone());
|
let new_remote_protocols = HashSet::from_iter(remote_info.protocols.clone());
|
||||||
|
|
||||||
@ -318,11 +330,15 @@ impl ConnectionHandler for Handler {
|
|||||||
{
|
{
|
||||||
self.inbound_identify_push.take();
|
self.inbound_identify_push.take();
|
||||||
|
|
||||||
if let Ok(info) = res {
|
if let Ok(remote_push_info) = res {
|
||||||
self.update_supported_protocols_for_remote(&info);
|
if let Some(mut info) = self.remote_info.clone() {
|
||||||
return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Event::Identified(
|
info.merge(remote_push_info);
|
||||||
info,
|
self.handle_incoming_info(&info);
|
||||||
)));
|
|
||||||
|
return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(
|
||||||
|
Event::Identified(info),
|
||||||
|
));
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ impl Push<OutboundPush> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information of a peer sent in protocol messages.
|
/// Identify information of a peer sent in protocol messages.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Info {
|
pub struct Info {
|
||||||
/// The public key of the local peer.
|
/// The public key of the local peer.
|
||||||
@ -82,6 +82,41 @@ pub struct Info {
|
|||||||
pub observed_addr: Multiaddr,
|
pub observed_addr: Multiaddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Info {
|
||||||
|
pub fn merge(&mut self, info: PushInfo) {
|
||||||
|
if let Some(public_key) = info.public_key {
|
||||||
|
self.public_key = public_key;
|
||||||
|
}
|
||||||
|
if let Some(protocol_version) = info.protocol_version {
|
||||||
|
self.protocol_version = protocol_version;
|
||||||
|
}
|
||||||
|
if let Some(agent_version) = info.agent_version {
|
||||||
|
self.agent_version = agent_version;
|
||||||
|
}
|
||||||
|
if !info.listen_addrs.is_empty() {
|
||||||
|
self.listen_addrs = info.listen_addrs;
|
||||||
|
}
|
||||||
|
if !info.protocols.is_empty() {
|
||||||
|
self.protocols = info.protocols;
|
||||||
|
}
|
||||||
|
if let Some(observed_addr) = info.observed_addr {
|
||||||
|
self.observed_addr = observed_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Identify push information of a peer sent in protocol messages.
|
||||||
|
/// Note that missing fields should be ignored, as peers may choose to send partial updates containing only the fields whose values have changed.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PushInfo {
|
||||||
|
pub public_key: Option<PublicKey>,
|
||||||
|
pub protocol_version: Option<String>,
|
||||||
|
pub agent_version: Option<String>,
|
||||||
|
pub listen_addrs: Vec<Multiaddr>,
|
||||||
|
pub protocols: Vec<StreamProtocol>,
|
||||||
|
pub observed_addr: Option<Multiaddr>,
|
||||||
|
}
|
||||||
|
|
||||||
impl UpgradeInfo for Identify {
|
impl UpgradeInfo for Identify {
|
||||||
type Info = StreamProtocol;
|
type Info = StreamProtocol;
|
||||||
type InfoIter = iter::Once<Self::Info>;
|
type InfoIter = iter::Once<Self::Info>;
|
||||||
@ -110,7 +145,7 @@ where
|
|||||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
|
type Future = Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send>>;
|
||||||
|
|
||||||
fn upgrade_outbound(self, socket: C, _: Self::Info) -> Self::Future {
|
fn upgrade_outbound(self, socket: C, _: Self::Info) -> Self::Future {
|
||||||
recv(socket).boxed()
|
recv_identify(socket).boxed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,13 +162,13 @@ impl<C> InboundUpgrade<C> for Push<InboundPush>
|
|||||||
where
|
where
|
||||||
C: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
C: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||||
{
|
{
|
||||||
type Output = BoxFuture<'static, Result<Info, UpgradeError>>;
|
type Output = BoxFuture<'static, Result<PushInfo, UpgradeError>>;
|
||||||
type Error = Void;
|
type Error = Void;
|
||||||
type Future = future::Ready<Result<Self::Output, Self::Error>>;
|
type Future = future::Ready<Result<Self::Output, Self::Error>>;
|
||||||
|
|
||||||
fn upgrade_inbound(self, socket: C, _: Self::Info) -> Self::Future {
|
fn upgrade_inbound(self, socket: C, _: Self::Info) -> Self::Future {
|
||||||
// Lazily upgrade stream, thus allowing upgrade to happen within identify's handler.
|
// Lazily upgrade stream, thus allowing upgrade to happen within identify's handler.
|
||||||
future::ok(recv(socket).boxed())
|
future::ok(recv_push(socket).boxed())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +219,29 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn recv<T>(socket: T) -> Result<Info, UpgradeError>
|
async fn recv_push<T>(socket: T) -> Result<PushInfo, UpgradeError>
|
||||||
|
where
|
||||||
|
T: AsyncRead + AsyncWrite + Unpin,
|
||||||
|
{
|
||||||
|
let info = recv(socket).await?.try_into()?;
|
||||||
|
|
||||||
|
trace!("Received {:?}", info);
|
||||||
|
|
||||||
|
Ok(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn recv_identify<T>(socket: T) -> Result<Info, UpgradeError>
|
||||||
|
where
|
||||||
|
T: AsyncRead + AsyncWrite + Unpin,
|
||||||
|
{
|
||||||
|
let info = recv(socket).await?.try_into()?;
|
||||||
|
|
||||||
|
trace!("Received {:?}", info);
|
||||||
|
|
||||||
|
Ok(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn recv<T>(socket: T) -> Result<proto::Identify, UpgradeError>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin,
|
T: AsyncRead + AsyncWrite + Unpin,
|
||||||
{
|
{
|
||||||
@ -199,61 +256,93 @@ where
|
|||||||
)
|
)
|
||||||
.next()
|
.next()
|
||||||
.await
|
.await
|
||||||
.ok_or(UpgradeError::StreamClosed)??
|
.ok_or(UpgradeError::StreamClosed)??;
|
||||||
.try_into()?;
|
|
||||||
|
|
||||||
trace!("Received: {:?}", info);
|
|
||||||
|
|
||||||
Ok(info)
|
Ok(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_listen_addrs(listen_addrs: Vec<Vec<u8>>) -> Vec<Multiaddr> {
|
||||||
|
listen_addrs
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|bytes| match Multiaddr::try_from(bytes) {
|
||||||
|
Ok(a) => Some(a),
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Unable to parse multiaddr: {e:?}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_protocols(protocols: Vec<String>) -> Vec<StreamProtocol> {
|
||||||
|
protocols
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|p| match StreamProtocol::try_from_owned(p) {
|
||||||
|
Ok(p) => Some(p),
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Received invalid protocol from peer: {e}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_public_key(public_key: Option<Vec<u8>>) -> Option<PublicKey> {
|
||||||
|
public_key.and_then(|key| match PublicKey::try_decode_protobuf(&key) {
|
||||||
|
Ok(k) => Some(k),
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Unable to decode public key: {e:?}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_observed_addr(observed_addr: Option<Vec<u8>>) -> Option<Multiaddr> {
|
||||||
|
observed_addr.and_then(|bytes| match Multiaddr::try_from(bytes) {
|
||||||
|
Ok(a) => Some(a),
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Unable to parse observed multiaddr: {e:?}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<proto::Identify> for Info {
|
impl TryFrom<proto::Identify> for Info {
|
||||||
type Error = UpgradeError;
|
type Error = UpgradeError;
|
||||||
|
|
||||||
fn try_from(msg: proto::Identify) -> Result<Self, Self::Error> {
|
fn try_from(msg: proto::Identify) -> Result<Self, Self::Error> {
|
||||||
fn parse_multiaddr(bytes: Vec<u8>) -> Result<Multiaddr, multiaddr::Error> {
|
let public_key = {
|
||||||
Multiaddr::try_from(bytes)
|
match parse_public_key(msg.publicKey) {
|
||||||
}
|
Some(key) => key,
|
||||||
|
// This will always produce a DecodingError if the public key is missing.
|
||||||
let listen_addrs = {
|
None => PublicKey::try_decode_protobuf(Default::default())?,
|
||||||
let mut addrs = Vec::new();
|
|
||||||
for addr in msg.listenAddrs.into_iter() {
|
|
||||||
match parse_multiaddr(addr) {
|
|
||||||
Ok(a) => addrs.push(a),
|
|
||||||
Err(e) => {
|
|
||||||
debug!("Unable to parse multiaddr: {e:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addrs
|
|
||||||
};
|
|
||||||
|
|
||||||
let public_key = PublicKey::try_decode_protobuf(&msg.publicKey.unwrap_or_default())?;
|
|
||||||
|
|
||||||
let observed_addr = match parse_multiaddr(msg.observedAddr.unwrap_or_default()) {
|
|
||||||
Ok(a) => a,
|
|
||||||
Err(e) => {
|
|
||||||
debug!("Unable to parse multiaddr: {e:?}");
|
|
||||||
Multiaddr::empty()
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let info = Info {
|
let info = Info {
|
||||||
public_key,
|
public_key,
|
||||||
protocol_version: msg.protocolVersion.unwrap_or_default(),
|
protocol_version: msg.protocolVersion.unwrap_or_default(),
|
||||||
agent_version: msg.agentVersion.unwrap_or_default(),
|
agent_version: msg.agentVersion.unwrap_or_default(),
|
||||||
listen_addrs,
|
listen_addrs: parse_listen_addrs(msg.listenAddrs),
|
||||||
protocols: msg
|
protocols: parse_protocols(msg.protocols),
|
||||||
.protocols
|
observed_addr: parse_observed_addr(msg.observedAddr).unwrap_or(Multiaddr::empty()),
|
||||||
.into_iter()
|
};
|
||||||
.filter_map(|p| match StreamProtocol::try_from_owned(p) {
|
|
||||||
Ok(p) => Some(p),
|
Ok(info)
|
||||||
Err(e) => {
|
}
|
||||||
debug!("Received invalid protocol from peer: {e}");
|
}
|
||||||
None
|
|
||||||
}
|
impl TryFrom<proto::Identify> for PushInfo {
|
||||||
})
|
type Error = UpgradeError;
|
||||||
.collect(),
|
|
||||||
observed_addr,
|
fn try_from(msg: proto::Identify) -> Result<Self, Self::Error> {
|
||||||
|
let info = PushInfo {
|
||||||
|
public_key: parse_public_key(msg.publicKey),
|
||||||
|
protocol_version: msg.protocolVersion,
|
||||||
|
agent_version: msg.agentVersion,
|
||||||
|
listen_addrs: parse_listen_addrs(msg.listenAddrs),
|
||||||
|
protocols: parse_protocols(msg.protocols),
|
||||||
|
observed_addr: parse_observed_addr(msg.observedAddr),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(info)
|
Ok(info)
|
||||||
@ -303,7 +392,7 @@ mod tests {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let info = Info::try_from(payload).expect("not to fail");
|
let info = PushInfo::try_from(payload).expect("not to fail");
|
||||||
|
|
||||||
assert_eq!(info.listen_addrs, vec![valid_multiaddr])
|
assert_eq!(info.listen_addrs, vec![valid_multiaddr])
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user