Skip to content

Commit 2d335d4

Browse files
committed
Integrated Signed-peer-record transfer with identify/identify-push
1 parent 8b8b051 commit 2d335d4

File tree

9 files changed

+117
-58
lines changed

9 files changed

+117
-58
lines changed

examples/identify/identify.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
identify_handler_for,
1515
parse_identify_response,
1616
)
17+
from libp2p.identity.identify.pb.identify_pb2 import Identify
18+
from libp2p.peer.envelope import debug_dump_envelope, unmarshal_envelope
1719
from libp2p.peer.peerinfo import (
1820
info_from_p2p_addr,
1921
)
@@ -32,10 +34,11 @@ def decode_multiaddrs(raw_addrs):
3234
return decoded_addrs
3335

3436

35-
def print_identify_response(identify_response):
37+
def print_identify_response(identify_response: Identify):
3638
"""Pretty-print Identify response."""
3739
public_key_b64 = base64.b64encode(identify_response.public_key).decode("utf-8")
3840
listen_addrs = decode_multiaddrs(identify_response.listen_addrs)
41+
signed_peer_record = unmarshal_envelope(identify_response.signedPeerRecord)
3942
try:
4043
observed_addr_decoded = decode_multiaddrs([identify_response.observed_addr])
4144
except Exception:
@@ -51,6 +54,8 @@ def print_identify_response(identify_response):
5154
f" Agent Version: {identify_response.agent_version}"
5255
)
5356

57+
debug_dump_envelope(signed_peer_record)
58+
5459

5560
async def run(port: int, destination: str, use_varint_format: bool = True) -> None:
5661
localhost_ip = "0.0.0.0"
@@ -61,6 +66,7 @@ async def run(port: int, destination: str, use_varint_format: bool = True) -> No
6166
host_a = new_host()
6267

6368
# Set up identify handler with specified format
69+
# Set use_varint_format = False, if want to checkout the Signed-PeerRecord
6470
identify_handler = identify_handler_for(
6571
host_a, use_varint_format=use_varint_format
6672
)
@@ -238,9 +244,9 @@ def main() -> None:
238244

239245
args = parser.parse_args()
240246

241-
# Determine format: raw format if --raw-format is specified, otherwise
242-
# length-prefixed
243-
use_varint_format = not args.raw_format
247+
# Determine format: use varint (length-prefixed) if --raw-format is specified,
248+
# otherwise use raw protobuf format (old format)
249+
use_varint_format = args.raw_format
244250

245251
try:
246252
if args.destination:

libp2p/identity/identify/identify.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
from libp2p.network.stream.exceptions import (
1616
StreamClosed,
1717
)
18+
from libp2p.peer.envelope import seal_record
19+
from libp2p.peer.peer_record import PeerRecord
1820
from libp2p.utils import (
1921
decode_varint_with_size,
2022
get_agent_version,
@@ -63,6 +65,11 @@ def _mk_identify_protobuf(
6365
laddrs = host.get_addrs()
6466
protocols = tuple(str(p) for p in host.get_mux().get_protocols() if p is not None)
6567

68+
# Create a signed peer-record for the remote peer
69+
record = PeerRecord(host.get_id(), host.get_addrs())
70+
envelope = seal_record(record, host.get_private_key())
71+
protobuf = envelope.marshal_envelope()
72+
6673
observed_addr = observed_multiaddr.to_bytes() if observed_multiaddr else b""
6774
return Identify(
6875
protocol_version=PROTOCOL_VERSION,
@@ -71,6 +78,7 @@ def _mk_identify_protobuf(
7178
listen_addrs=map(_multiaddr_to_bytes, laddrs),
7279
observed_addr=observed_addr,
7380
protocols=protocols,
81+
signedPeerRecord=protobuf,
7482
)
7583

7684

libp2p/identity/identify/pb/identify.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ message Identify {
99
repeated bytes listen_addrs = 2;
1010
optional bytes observed_addr = 4;
1111
repeated string protocols = 3;
12+
optional bytes signedPeerRecord = 8;
1213
}

libp2p/identity/identify/pb/identify_pb2.py

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 22 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,24 @@
1-
"""
2-
@generated by mypy-protobuf. Do not edit manually!
3-
isort:skip_file
4-
"""
1+
from google.protobuf.internal import containers as _containers
2+
from google.protobuf import descriptor as _descriptor
3+
from google.protobuf import message as _message
4+
from typing import ClassVar as _ClassVar, Iterable as _Iterable, Optional as _Optional
55

6-
import builtins
7-
import collections.abc
8-
import google.protobuf.descriptor
9-
import google.protobuf.internal.containers
10-
import google.protobuf.message
11-
import typing
6+
DESCRIPTOR: _descriptor.FileDescriptor
127

13-
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
14-
15-
@typing.final
16-
class Identify(google.protobuf.message.Message):
17-
DESCRIPTOR: google.protobuf.descriptor.Descriptor
18-
19-
PROTOCOL_VERSION_FIELD_NUMBER: builtins.int
20-
AGENT_VERSION_FIELD_NUMBER: builtins.int
21-
PUBLIC_KEY_FIELD_NUMBER: builtins.int
22-
LISTEN_ADDRS_FIELD_NUMBER: builtins.int
23-
OBSERVED_ADDR_FIELD_NUMBER: builtins.int
24-
PROTOCOLS_FIELD_NUMBER: builtins.int
25-
protocol_version: builtins.str
26-
agent_version: builtins.str
27-
public_key: builtins.bytes
28-
observed_addr: builtins.bytes
29-
@property
30-
def listen_addrs(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.bytes]: ...
31-
@property
32-
def protocols(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ...
33-
def __init__(
34-
self,
35-
*,
36-
protocol_version: builtins.str | None = ...,
37-
agent_version: builtins.str | None = ...,
38-
public_key: builtins.bytes | None = ...,
39-
listen_addrs: collections.abc.Iterable[builtins.bytes] | None = ...,
40-
observed_addr: builtins.bytes | None = ...,
41-
protocols: collections.abc.Iterable[builtins.str] | None = ...,
42-
) -> None: ...
43-
def HasField(self, field_name: typing.Literal["agent_version", b"agent_version", "observed_addr", b"observed_addr", "protocol_version", b"protocol_version", "public_key", b"public_key"]) -> builtins.bool: ...
44-
def ClearField(self, field_name: typing.Literal["agent_version", b"agent_version", "listen_addrs", b"listen_addrs", "observed_addr", b"observed_addr", "protocol_version", b"protocol_version", "protocols", b"protocols", "public_key", b"public_key"]) -> None: ...
45-
46-
global___Identify = Identify
8+
class Identify(_message.Message):
9+
__slots__ = ("protocol_version", "agent_version", "public_key", "listen_addrs", "observed_addr", "protocols", "signedPeerRecord")
10+
PROTOCOL_VERSION_FIELD_NUMBER: _ClassVar[int]
11+
AGENT_VERSION_FIELD_NUMBER: _ClassVar[int]
12+
PUBLIC_KEY_FIELD_NUMBER: _ClassVar[int]
13+
LISTEN_ADDRS_FIELD_NUMBER: _ClassVar[int]
14+
OBSERVED_ADDR_FIELD_NUMBER: _ClassVar[int]
15+
PROTOCOLS_FIELD_NUMBER: _ClassVar[int]
16+
SIGNEDPEERRECORD_FIELD_NUMBER: _ClassVar[int]
17+
protocol_version: str
18+
agent_version: str
19+
public_key: bytes
20+
listen_addrs: _containers.RepeatedScalarFieldContainer[bytes]
21+
observed_addr: bytes
22+
protocols: _containers.RepeatedScalarFieldContainer[str]
23+
signedPeerRecord: bytes
24+
def __init__(self, protocol_version: _Optional[str] = ..., agent_version: _Optional[str] = ..., public_key: _Optional[bytes] = ..., listen_addrs: _Optional[_Iterable[bytes]] = ..., observed_addr: _Optional[bytes] = ..., protocols: _Optional[_Iterable[str]] = ..., signedPeerRecord: _Optional[bytes] = ...) -> None: ...

libp2p/identity/identify_push/identify_push.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from libp2p.network.stream.exceptions import (
2121
StreamClosed,
2222
)
23+
from libp2p.peer.envelope import consume_envelope
2324
from libp2p.peer.id import (
2425
ID,
2526
)
@@ -150,6 +151,19 @@ async def _update_peerstore_from_identify(
150151
peerstore.add_addr(peer_id, observed_addr, 7200)
151152
except Exception as e:
152153
logger.error("Error updating observed address for peer %s: %s", peer_id, e)
154+
if identify_msg.HasField("signedPeerRecord"):
155+
try:
156+
# Convert the signed-peer-record(Envelope) from prtobuf bytes
157+
envelope, _ = consume_envelope(
158+
identify_msg.signedPeerRecord, "libp2p-peer-record"
159+
)
160+
# Use a default TTL of 2 hours (7200 seconds)
161+
if not peerstore.consume_peer_record(envelope, 7200):
162+
logger.error("Updating Certified-Addr-Book was unsuccessful")
163+
except Exception as e:
164+
logger.error(
165+
"Error updating the certified addr book for peer %s: %s", peer_id, e
166+
)
153167

154168

155169
async def push_identify_to_peer(

libp2p/peer/envelope.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
import libp2p.peer.pb.crypto_pb2 as cryto_pb
88
import libp2p.peer.pb.envelope_pb2 as pb
99
import libp2p.peer.pb.peer_record_pb2 as record_pb
10-
from libp2p.peer.peer_record import PeerRecord, peer_record_from_protobuf
10+
from libp2p.peer.peer_record import (
11+
PeerRecord,
12+
peer_record_from_protobuf,
13+
unmarshal_record,
14+
)
1115
from libp2p.utils.varint import encode_uvarint
1216

1317
ENVELOPE_DOMAIN = "libp2p-peer-record"
@@ -251,3 +255,17 @@ def make_unsigned(domain: str, payload_type: bytes, payload: bytes) -> bytes:
251255
buf.extend(field)
252256

253257
return bytes(buf)
258+
259+
260+
def debug_dump_envelope(env: Envelope) -> None:
261+
print("\n=== Envelope ===")
262+
print(f"Payload Type: {env.payload_type!r}")
263+
print(f"Signature: {env.signature.hex()} ({len(env.signature)} bytes)")
264+
print(f"Raw Payload: {env.raw_payload.hex()} ({len(env.raw_payload)} bytes)")
265+
266+
try:
267+
peer_record = unmarshal_record(env.raw_payload)
268+
print("\n=== Parsed PeerRecord ===")
269+
print(peer_record)
270+
except Exception as e:
271+
print("Failed to parse PeerRecord:", e)

libp2p/peer/peer_record.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ def __init__(
5858
else:
5959
self.seq = timestamp_seq()
6060

61+
def __repr__(self) -> str:
62+
return (
63+
f"PeerRecord(\n"
64+
f" peer_id={self.peer_id},\n"
65+
f" multiaddrs={[str(m) for m in self.addrs]},\n"
66+
f" seq={self.seq}\n"
67+
f")"
68+
)
69+
6170
def domain(self) -> str:
6271
"""
6372
Return the domain string associated with this PeerRecord.

tests/core/identity/identify/test_identify.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
_multiaddr_to_bytes,
1414
parse_identify_response,
1515
)
16+
from libp2p.peer.envelope import Envelope, consume_envelope, unmarshal_envelope
17+
from libp2p.peer.peer_record import unmarshal_record
1618
from tests.utils.factories import (
1719
host_pair_factory,
1820
)
@@ -40,6 +42,19 @@ async def test_identify_protocol(security_protocol):
4042
# Parse the response (handles both old and new formats)
4143
identify_response = parse_identify_response(response)
4244

45+
# Validate the recieved envelope and then store it in the certified-addr-book
46+
envelope, record = consume_envelope(
47+
identify_response.signedPeerRecord, "libp2p-peer-record"
48+
)
49+
assert host_b.peerstore.consume_peer_record(envelope, ttl=7200)
50+
51+
# Check if the peer_id in the record is same as of host_a
52+
assert record.peer_id == host_a.get_id()
53+
54+
# Check if the peer-record is correctly consumed
55+
assert host_a.get_addrs() == host_b.peerstore.addrs(host_a.get_id())
56+
assert isinstance(host_b.peerstore.get_peer_record(host_a.get_id()), Envelope)
57+
4358
logger.debug("host_a: %s", host_a.get_addrs())
4459
logger.debug("host_b: %s", host_b.get_addrs())
4560

@@ -71,5 +86,14 @@ async def test_identify_protocol(security_protocol):
7186
# Check protocols
7287
assert set(identify_response.protocols) == set(host_a.get_mux().get_protocols())
7388

74-
# sanity check
75-
assert identify_response == _mk_identify_protobuf(host_a, cleaned_addr)
89+
# sanity check if the peer_id of the identify msg are same
90+
assert (
91+
unmarshal_record(
92+
unmarshal_envelope(identify_response.signedPeerRecord).raw_payload
93+
).peer_id
94+
== unmarshal_record(
95+
unmarshal_envelope(
96+
_mk_identify_protobuf(host_a, cleaned_addr).signedPeerRecord
97+
).raw_payload
98+
).peer_id
99+
)

0 commit comments

Comments
 (0)