Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 42 additions & 0 deletions core/src/peer_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,24 @@ impl PeerRecord {
Self::from_signed_envelope_impl(envelope, STANDARD_DOMAIN_SEP, STANDARD_PAYLOAD_TYPE)
}

/// Attempt to re-construct a [`PeerRecord`] from a [`SignedEnvelope`] using either legacy or
/// standard interop format.
///
/// Tries the legacy format first and falls back to the standard interop format when the payload
/// type does not match the legacy encoding. This preserves legacy validation semantics while
/// allowing protocol consumers to accept peer records produced during migration.
pub fn from_signed_envelope_legacy_or_interop(
envelope: SignedEnvelope,
) -> Result<Self, FromEnvelopeError> {
match Self::from_signed_envelope(envelope.clone()) {
Ok(record) => Ok(record),
Err(FromEnvelopeError::BadPayload(
signed_envelope::ReadPayloadError::UnexpectedPayloadType { .. },
)) => Self::from_signed_envelope_interop(envelope),
Err(err) => Err(err),
}
}

fn from_signed_envelope_impl(
envelope: SignedEnvelope,
domain: &str,
Expand Down Expand Up @@ -250,6 +268,30 @@ mod tests {
assert_eq!(reconstructed, record)
}

#[test]
fn roundtrip_envelope_legacy_or_interop_legacy() {
let key = Keypair::generate_ed25519();

let record = PeerRecord::new(&key, vec![HOME.parse().unwrap()]).unwrap();

let envelope = record.to_signed_envelope();
let reconstructed = PeerRecord::from_signed_envelope_legacy_or_interop(envelope).unwrap();

assert_eq!(reconstructed, record)
}

#[test]
fn roundtrip_envelope_legacy_or_interop_interop() {
let key = Keypair::generate_ed25519();

let record = PeerRecord::new_interop(&key, vec![HOME.parse().unwrap()]).unwrap();

let envelope = record.to_signed_envelope();
let reconstructed = PeerRecord::from_signed_envelope_legacy_or_interop(envelope).unwrap();

assert_eq!(reconstructed, record)
}

#[test]
fn mismatched_signature_legacy() {
use quick_protobuf::MessageWrite;
Expand Down
27 changes: 26 additions & 1 deletion protocols/identify/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ impl TryFrom<proto::Identify> for Info {
.signedPeerRecord
.and_then(|b| {
let envelope = SignedEnvelope::from_protobuf_encoding(b.as_ref()).ok()?;
let peer_record = PeerRecord::from_signed_envelope(envelope).ok()?;
let peer_record =
PeerRecord::from_signed_envelope_legacy_or_interop(envelope).ok()?;
(peer_record.peer_id() == identify_public_key.to_peer_id()).then_some((
peer_record.addresses().to_vec(),
Some(peer_record.into_signed_envelope()),
Expand Down Expand Up @@ -402,4 +403,28 @@ mod tests {
.expect("read to succeed");
assert_eq!(message, parsed_message)
}

#[test]
fn uses_interop_signed_peer_record_addresses() {
let identity = identity::Keypair::generate_ed25519();
let listen_addr: Multiaddr = "/ip4/192.0.2.1/tcp/1234".parse().unwrap();
let ignored_addr: Multiaddr = "/ip4/127.0.0.1/tcp/4321".parse().unwrap();
let signed_peer_record =
PeerRecord::new_interop(&identity, vec![listen_addr.clone()]).unwrap();
let signed_envelope = signed_peer_record.into_signed_envelope();

let info = Info::try_from(proto::Identify {
agentVersion: Some("agent".into()),
listenAddrs: vec![ignored_addr.to_vec()],
observedAddr: None,
protocolVersion: Some("proto".into()),
protocols: vec![],
publicKey: Some(identity.public().encode_protobuf()),
signedPeerRecord: Some(signed_envelope.clone().into_protobuf_encoding()),
})
.expect("interop signed peer records to be accepted");

assert_eq!(info.listen_addrs, vec![listen_addr]);
assert_eq!(info.signed_peer_record, Some(signed_envelope));
}
}
66 changes: 62 additions & 4 deletions protocols/rendezvous/src/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,9 +451,9 @@ impl TryFrom<proto::Message> for Message {
.transpose()?
.ok_or(ConversionError::MissingNamespace)?,
ttl,
record: PeerRecord::from_signed_envelope(SignedEnvelope::from_protobuf_encoding(
&signed_peer_record,
)?)?,
record: PeerRecord::from_signed_envelope_legacy_or_interop(
SignedEnvelope::from_protobuf_encoding(&signed_peer_record)?,
)?,
}),
proto::Message {
type_pb: Some(proto::MessageType::REGISTER_RESPONSE),
Expand Down Expand Up @@ -494,7 +494,7 @@ impl TryFrom<proto::Message> for Message {
.map(Namespace::new)
.transpose()?
.ok_or(ConversionError::MissingNamespace)?,
record: PeerRecord::from_signed_envelope(
record: PeerRecord::from_signed_envelope_legacy_or_interop(
SignedEnvelope::from_protobuf_encoding(
&reggo
.signedPeerRecord
Expand Down Expand Up @@ -642,6 +642,9 @@ mod proto {

#[cfg(test)]
mod tests {
use libp2p_core::PeerRecord;
use libp2p_identity::Keypair;

use super::*;

#[test]
Expand All @@ -662,4 +665,59 @@ mod tests {

assert_eq!(bytes.len(), 8 + 3)
}

#[test]
fn parses_interop_register_records() {
let namespace = Namespace::from_static("foo");
let record = interop_peer_record();
let expected_record = record.clone();

let message = Message::try_from(proto::Message::from(Message::Register(
NewRegistration::new(namespace.clone(), record, Some(42)),
)))
.expect("interop register record to parse");

assert_eq!(
message,
Message::Register(NewRegistration::new(namespace, expected_record, Some(42)))
);
}

#[test]
fn parses_interop_discover_response_records() {
let namespace = Namespace::from_static("foo");
let record = interop_peer_record();
let expected_record = record.clone();
let cookie = Cookie::for_namespace(namespace.clone());

let message = Message::try_from(proto::Message::from(Message::DiscoverResponse(Ok((
vec![Registration {
namespace: namespace.clone(),
record,
ttl: 42,
}],
cookie.clone(),
)))))
.expect("interop discover response record to parse");

assert_eq!(
message,
Message::DiscoverResponse(Ok((
vec![Registration {
namespace,
record: expected_record,
ttl: 42,
}],
cookie,
)))
);
}

fn interop_peer_record() -> PeerRecord {
PeerRecord::new_interop(
&Keypair::generate_ed25519(),
vec!["/ip4/127.0.0.1/tcp/1234".parse().unwrap()],
)
.unwrap()
}
}