mirror of
https://github.com/varun-r-mallya/py-libp2p.git
synced 2026-02-10 07:00:54 +00:00
PeerRecord class with ProtoBuff implemented
This commit is contained in:
@ -41,6 +41,7 @@ from libp2p.io.abc import (
|
|||||||
from libp2p.peer.id import (
|
from libp2p.peer.id import (
|
||||||
ID,
|
ID,
|
||||||
)
|
)
|
||||||
|
import libp2p.peer.pb.peer_record_pb2 as pb
|
||||||
from libp2p.peer.peerinfo import (
|
from libp2p.peer.peerinfo import (
|
||||||
PeerInfo,
|
PeerInfo,
|
||||||
)
|
)
|
||||||
@ -1689,6 +1690,68 @@ class IHost(ABC):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------- peer-record interface.py --------------------------
|
||||||
|
class IPeerRecord(ABC):
|
||||||
|
"""
|
||||||
|
Interface for a libp2p PeerRecord object.
|
||||||
|
|
||||||
|
A PeerRecord contains metadata about a peer such as its ID, public addresses,
|
||||||
|
and a strictly increasing sequence number for versioning.
|
||||||
|
|
||||||
|
PeerRecords are used in signed routing Envelopes for secure peer data propagation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def domain(self) -> str:
|
||||||
|
"""
|
||||||
|
Return the domain string for this record type.
|
||||||
|
|
||||||
|
Used in envelope validation to distinguish different record types.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def codec(self) -> bytes:
|
||||||
|
"""
|
||||||
|
Return a binary codec prefix that identifies the PeerRecord type.
|
||||||
|
|
||||||
|
This is prepended in signed envelopes to allow type-safe decoding.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def to_protobuf(self) -> pb.PeerRecord:
|
||||||
|
"""
|
||||||
|
Convert this PeerRecord into its Protobuf representation.
|
||||||
|
|
||||||
|
:raises ValueError: if serialization fails (e.g., invalid peer ID).
|
||||||
|
:return: A populated protobuf `PeerRecord` message.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def marshal_record(self) -> bytes:
|
||||||
|
"""
|
||||||
|
Serialize this PeerRecord into a byte string.
|
||||||
|
|
||||||
|
Used when signing or sealing the record in an envelope.
|
||||||
|
|
||||||
|
:raises ValueError: if protobuf serialization fails.
|
||||||
|
:return: Byte-encoded PeerRecord.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def equal(self, other: object) -> bool:
|
||||||
|
"""
|
||||||
|
Compare this PeerRecord with another for equality.
|
||||||
|
|
||||||
|
Two PeerRecords are considered equal if:
|
||||||
|
- They have the same `peer_id`
|
||||||
|
- Their `seq` numbers match
|
||||||
|
- Their address lists are identical and ordered
|
||||||
|
|
||||||
|
:param other: Object to compare with.
|
||||||
|
:return: True if equal, False otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
# -------------------------- peerdata interface.py --------------------------
|
# -------------------------- peerdata interface.py --------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
31
libp2p/peer/pb/peer_record.proto
Normal file
31
libp2p/peer/pb/peer_record.proto
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package peer.pb;
|
||||||
|
|
||||||
|
option go_package = "github.com/libp2p/go-libp2p/core/peer/pb";
|
||||||
|
|
||||||
|
// PeerRecord messages contain information that is useful to share with other peers.
|
||||||
|
// Currently, a PeerRecord contains the public listen addresses for a peer, but this
|
||||||
|
// is expected to expand to include other information in the future.
|
||||||
|
//
|
||||||
|
// PeerRecords are designed to be serialized to bytes and placed inside of
|
||||||
|
// SignedEnvelopes before sharing with other peers.
|
||||||
|
// See https://github.com/libp2p/go-libp2p/blob/master/core/record/pb/envelope.proto for
|
||||||
|
// the SignedEnvelope definition.
|
||||||
|
message PeerRecord {
|
||||||
|
|
||||||
|
// AddressInfo is a wrapper around a binary multiaddr. It is defined as a
|
||||||
|
// separate message to allow us to add per-address metadata in the future.
|
||||||
|
message AddressInfo {
|
||||||
|
bytes multiaddr = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// peer_id contains a libp2p peer id in its binary representation.
|
||||||
|
bytes peer_id = 1;
|
||||||
|
|
||||||
|
// seq contains a monotonically-increasing sequence counter to order PeerRecords in time.
|
||||||
|
uint64 seq = 2;
|
||||||
|
|
||||||
|
// addresses is a list of public listen addresses for the peer.
|
||||||
|
repeated AddressInfo addresses = 3;
|
||||||
|
}
|
||||||
39
libp2p/peer/pb/peer_record_pb2.py
Normal file
39
libp2p/peer/pb/peer_record_pb2.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# NO CHECKED-IN PROTOBUF GENCODE
|
||||||
|
# source: libp2p/peer/pb/peer_record.proto
|
||||||
|
# Protobuf Python Version: 6.31.1
|
||||||
|
"""Generated protocol buffer code."""
|
||||||
|
from google.protobuf import descriptor as _descriptor
|
||||||
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
|
from google.protobuf import runtime_version as _runtime_version
|
||||||
|
from google.protobuf import symbol_database as _symbol_database
|
||||||
|
from google.protobuf.internal import builder as _builder
|
||||||
|
_runtime_version.ValidateProtobufRuntimeVersion(
|
||||||
|
_runtime_version.Domain.PUBLIC,
|
||||||
|
6,
|
||||||
|
31,
|
||||||
|
1,
|
||||||
|
'',
|
||||||
|
'libp2p/peer/pb/peer_record.proto'
|
||||||
|
)
|
||||||
|
# @@protoc_insertion_point(imports)
|
||||||
|
|
||||||
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n libp2p/peer/pb/peer_record.proto\x12\x07peer.pb\"\x80\x01\n\nPeerRecord\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x0b\n\x03seq\x18\x02 \x01(\x04\x12\x32\n\taddresses\x18\x03 \x03(\x0b\x32\x1f.peer.pb.PeerRecord.AddressInfo\x1a \n\x0b\x41\x64\x64ressInfo\x12\x11\n\tmultiaddr\x18\x01 \x01(\x0c\x42*Z(github.com/libp2p/go-libp2p/core/peer/pbb\x06proto3')
|
||||||
|
|
||||||
|
_globals = globals()
|
||||||
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'libp2p.peer.pb.peer_record_pb2', _globals)
|
||||||
|
if not _descriptor._USE_C_DESCRIPTORS:
|
||||||
|
_globals['DESCRIPTOR']._loaded_options = None
|
||||||
|
_globals['DESCRIPTOR']._serialized_options = b'Z(github.com/libp2p/go-libp2p/core/peer/pb'
|
||||||
|
_globals['_PEERRECORD']._serialized_start=46
|
||||||
|
_globals['_PEERRECORD']._serialized_end=174
|
||||||
|
_globals['_PEERRECORD_ADDRESSINFO']._serialized_start=142
|
||||||
|
_globals['_PEERRECORD_ADDRESSINFO']._serialized_end=174
|
||||||
|
# @@protoc_insertion_point(module_scope)
|
||||||
235
libp2p/peer/peer_record.py
Normal file
235
libp2p/peer/peer_record.py
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
import time
|
||||||
|
import threading
|
||||||
|
from typing import Optional
|
||||||
|
from libp2p.abc import IPeerRecord
|
||||||
|
import libp2p.peer.pb.peer_record_pb2 as pb
|
||||||
|
|
||||||
|
from multiaddr import Multiaddr
|
||||||
|
from libp2p.peer.peerinfo import PeerInfo
|
||||||
|
from libp2p.peer.id import ID
|
||||||
|
|
||||||
|
|
||||||
|
PEER_RECORD_ENVELOPE_DOMAIN = "libp2p-peer-record"
|
||||||
|
PEER_RECORD_ENVELOPE_PAYLOAD_TYPE = b'\x03\x01'
|
||||||
|
|
||||||
|
_last_timestamp_lock = threading.Lock()
|
||||||
|
_last_timestamp: int = 0
|
||||||
|
|
||||||
|
class PeerRecord(IPeerRecord):
|
||||||
|
"""
|
||||||
|
A record that contains metatdata about a peer in the libp2p network.
|
||||||
|
|
||||||
|
This includes:
|
||||||
|
- `peer_id`: The peer's globally unique indentifier.
|
||||||
|
- `addrs`: A list of the peer's publicly reachable multiaddrs.
|
||||||
|
- `seq`: A strictly monotonically increasing timestamp used
|
||||||
|
to order records over time.
|
||||||
|
|
||||||
|
PeerRecords are designed to be signed and transmitted in libp2p routing Envelopes.
|
||||||
|
"""
|
||||||
|
peer_id: ID
|
||||||
|
addrs: list[Multiaddr]
|
||||||
|
seq: int
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
peer_id: Optional[ID] = None,
|
||||||
|
addrs: Optional[list[Multiaddr]] = None,
|
||||||
|
seq: Optional[int] = None,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Initialize a new PeerRecord.
|
||||||
|
If `seq` is not provided, a timestamp-based strictly increasing sequence
|
||||||
|
number will be generated.
|
||||||
|
|
||||||
|
:param peer_id: ID of the peer this record refers to.
|
||||||
|
:param addrs: Public multiaddrs of the peer.
|
||||||
|
:param seq: Monotonic sequence number.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if peer_id is not None:
|
||||||
|
self.peer_id = peer_id
|
||||||
|
self.addrs = addrs or []
|
||||||
|
if seq is not None:
|
||||||
|
self.seq = seq
|
||||||
|
else:
|
||||||
|
self.seq = timestamp_seq()
|
||||||
|
|
||||||
|
def domain(self) -> str:
|
||||||
|
"""
|
||||||
|
Return the domain string associated with this PeerRecord.
|
||||||
|
|
||||||
|
Used during record signing and envelope validation to identify the record type.
|
||||||
|
"""
|
||||||
|
return PEER_RECORD_ENVELOPE_DOMAIN
|
||||||
|
|
||||||
|
def codec(self) -> bytes:
|
||||||
|
"""
|
||||||
|
Return the codec identifier for PeerRecords.
|
||||||
|
|
||||||
|
This binary perfix helps distinguish PeerRecords in serialized envelopes.
|
||||||
|
"""
|
||||||
|
return PEER_RECORD_ENVELOPE_PAYLOAD_TYPE
|
||||||
|
|
||||||
|
def to_protobuf(self) -> pb.PeerRecord:
|
||||||
|
"""
|
||||||
|
Convert the current PeerRecord into a ProtoBuf PeerRecord message.
|
||||||
|
|
||||||
|
:raises ValueError: if peer_id serialization fails.
|
||||||
|
:return: A ProtoBuf-encoded PeerRecord message object.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
id_bytes = self.peer_id.to_bytes()
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError(f"failed to marshal peer_id: {e}")
|
||||||
|
|
||||||
|
msg = pb.PeerRecord()
|
||||||
|
msg.peer_id = id_bytes
|
||||||
|
msg.seq = self.seq
|
||||||
|
msg.addresses.extend(addrs_to_protobuf(self.addrs))
|
||||||
|
return msg
|
||||||
|
|
||||||
|
def marshal_record(self) -> bytes:
|
||||||
|
"""
|
||||||
|
Serialize a PeerRecord into raw bytes suitable for embedding in an Envelope.
|
||||||
|
|
||||||
|
This is typically called during the process of signing or sealing the record.
|
||||||
|
:raises ValueError: if serialization to protobuf fails.
|
||||||
|
:return: Serialized PeerRecord bytes.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
msg = self.to_protobuf()
|
||||||
|
return msg.SerializeToString()
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError(f"failed to marshal PeerRecord: {e}")
|
||||||
|
|
||||||
|
def equal(self, other) -> bool:
|
||||||
|
"""
|
||||||
|
Check if this PeerRecord is identical to another.
|
||||||
|
|
||||||
|
Two PeerRecords are considered equal if:
|
||||||
|
- Their peer IDs match.
|
||||||
|
- Their sequence numbers are identical.
|
||||||
|
- Their address lists are identical and in the same order.
|
||||||
|
|
||||||
|
:param other: Another PeerRecord instance.
|
||||||
|
:return: True if all fields mathch, False otherwise.
|
||||||
|
"""
|
||||||
|
if isinstance(other, PeerRecord):
|
||||||
|
if self.peer_id == other.peer_id:
|
||||||
|
if self.seq == other.seq:
|
||||||
|
if len(self.addrs) == len(other.addrs):
|
||||||
|
for a1, a2 in zip(self.addrs, other.addrs):
|
||||||
|
if a1 == a2:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def unmarshal_record(data: bytes) -> PeerRecord:
|
||||||
|
"""
|
||||||
|
Deserialize a PeerRecord from its serialized byte representation.
|
||||||
|
|
||||||
|
Typically used when receiveing a PeerRecord inside a signed routing Envelope.
|
||||||
|
|
||||||
|
:param data: Serialized protobuf-encoded bytes.
|
||||||
|
:raises ValueError: if parsing or conversion fails.
|
||||||
|
:reurn: A valid PeerRecord instance.
|
||||||
|
"""
|
||||||
|
if data is None:
|
||||||
|
raise ValueError("cannot unmarshal PeerRecord from None")
|
||||||
|
|
||||||
|
msg = pb.PeerRecord()
|
||||||
|
try:
|
||||||
|
msg.ParseFromString(data)
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError(f"Failed to parse PeerRecord protobuf: {e}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
record = peer_record_from_protobuf(msg)
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError(f"Failed to convert protobuf to PeerRecord: {e}")
|
||||||
|
|
||||||
|
return record
|
||||||
|
|
||||||
|
def timestamp_seq() -> int:
|
||||||
|
"""
|
||||||
|
Generate a strictly increasing timestamp-based sequence number.
|
||||||
|
|
||||||
|
Ensures that even if multiple PeerRecords are generated in the same nanosecond,
|
||||||
|
their `seq` values will still be strictly increasing by using a lock to track the
|
||||||
|
last value.
|
||||||
|
|
||||||
|
:return: A strictly increasing integer timestamp.
|
||||||
|
"""
|
||||||
|
global _last_timestamp
|
||||||
|
now = int(time.time_ns())
|
||||||
|
with _last_timestamp_lock:
|
||||||
|
if now <= _last_timestamp:
|
||||||
|
now = _last_timestamp + 1
|
||||||
|
_last_timestamp = now
|
||||||
|
return now
|
||||||
|
|
||||||
|
|
||||||
|
def peer_record_from_peer_info(info: PeerInfo) -> PeerRecord:
|
||||||
|
"""
|
||||||
|
Create a PeerRecord from a PeerInfo object.
|
||||||
|
|
||||||
|
This automatically assigns a timestamp-based sequence number to the record.
|
||||||
|
:param info: A PeerInfo instance (contains peer_id and addrs).
|
||||||
|
:return: A PeerRecord instance.
|
||||||
|
"""
|
||||||
|
record = PeerRecord()
|
||||||
|
record.peer_id = info.peer_id
|
||||||
|
record.addrs = info.addrs
|
||||||
|
return record
|
||||||
|
|
||||||
|
def peer_record_from_protobuf(msg: pb.PeerRecord) -> PeerRecord:
|
||||||
|
"""
|
||||||
|
Convert a protobuf PeerRecord message into a PeerRecord object.
|
||||||
|
|
||||||
|
:param msg: Protobuf PeerRecord message.
|
||||||
|
:raises ValueError: if the peer_id cannot be parsed.
|
||||||
|
:return: A deserialized PeerRecord instance.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
peer_id = ID(msg.peer_id)
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError(f"Failed to unmarshal peer_id: {e}")
|
||||||
|
|
||||||
|
addrs = addrs_from_protobuf(msg.addresses)
|
||||||
|
seq = msg.seq
|
||||||
|
|
||||||
|
return PeerRecord(peer_id, addrs, seq)
|
||||||
|
|
||||||
|
|
||||||
|
def addrs_from_protobuf(addrs: list[pb.PeerRecord.AddressInfo]) -> list[Multiaddr]:
|
||||||
|
"""
|
||||||
|
Convert a list of protobuf address records to Multiaddr objects.
|
||||||
|
|
||||||
|
:param addrs: A list of protobuf PeerRecord.AddressInfo messages.
|
||||||
|
:return: A list of decoded Multiaddr instances (invalid ones are skipped).
|
||||||
|
"""
|
||||||
|
out = []
|
||||||
|
for addr_info in addrs:
|
||||||
|
try:
|
||||||
|
addr = Multiaddr(addr_info.multiaddr)
|
||||||
|
out.append(addr)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
return out
|
||||||
|
|
||||||
|
def addrs_to_protobuf(addrs: list[Multiaddr]) -> list[pb.PeerRecord.AddressInfo]:
|
||||||
|
"""
|
||||||
|
Convert a list of Multiaddr objects into their protobuf representation.
|
||||||
|
|
||||||
|
:param addrs: A list of Multiaddr instances.
|
||||||
|
:return: A list of PeerRecord.AddressInfo protobuf messages.
|
||||||
|
"""
|
||||||
|
out = []
|
||||||
|
for addr in addrs:
|
||||||
|
addr_info = pb.PeerRecord.AddressInfo()
|
||||||
|
addr_info.multiaddr = addr.to_bytes()
|
||||||
|
out.append(addr_info)
|
||||||
|
return out
|
||||||
104
tests/core/peer/test_peer_record.py
Normal file
104
tests/core/peer/test_peer_record.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import time
|
||||||
|
from libp2p.peer.id import ID
|
||||||
|
import libp2p.peer.pb.peer_record_pb2 as pb
|
||||||
|
from multiaddr import Multiaddr
|
||||||
|
|
||||||
|
from libp2p.peer.peer_record import (
|
||||||
|
PeerRecord,
|
||||||
|
addrs_from_protobuf,
|
||||||
|
peer_record_from_protobuf,
|
||||||
|
unmarshal_record,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Testing methods from PeerRecord base class and PeerRecord protobuf:
|
||||||
|
|
||||||
|
def test_basic_protobuf_serializatrion_deserialization():
|
||||||
|
record = pb.PeerRecord()
|
||||||
|
record.seq = 1
|
||||||
|
|
||||||
|
serialized = record.SerializeToString()
|
||||||
|
new_record = pb.PeerRecord()
|
||||||
|
new_record.ParseFromString(serialized)
|
||||||
|
|
||||||
|
assert new_record.seq == 1
|
||||||
|
|
||||||
|
def test_timestamp_seq_monotonicity():
|
||||||
|
rec1 = PeerRecord()
|
||||||
|
time.sleep(1)
|
||||||
|
rec2 = PeerRecord()
|
||||||
|
|
||||||
|
assert isinstance(rec1.seq, int)
|
||||||
|
assert isinstance(rec2.seq, int)
|
||||||
|
assert rec2.seq > rec1.seq, f"Expected seq2 ({rec2.seq}) > seq1 ({rec1.seq})"
|
||||||
|
|
||||||
|
def test_addrs_from_protobuf_multiple_addresses():
|
||||||
|
ma1 = Multiaddr("/ip4/127.0.0.1/tcp/4001")
|
||||||
|
ma2 = Multiaddr("/ip4/127.0.0.1/tcp/4002")
|
||||||
|
|
||||||
|
addr_info1 = pb.PeerRecord.AddressInfo()
|
||||||
|
addr_info1.multiaddr = ma1.to_bytes()
|
||||||
|
|
||||||
|
addr_info2 = pb.PeerRecord.AddressInfo()
|
||||||
|
addr_info2.multiaddr = ma2.to_bytes()
|
||||||
|
|
||||||
|
result = addrs_from_protobuf([addr_info1, addr_info2])
|
||||||
|
assert result == [ma1, ma2]
|
||||||
|
|
||||||
|
def test_peer_record_from_protobuf():
|
||||||
|
peer_id = ID.from_base58("QmNM23MiU1Kd7yfiKVdUnaDo8RYca8By4zDmr7uSaVV8Px")
|
||||||
|
record = pb.PeerRecord()
|
||||||
|
record.peer_id = peer_id.to_bytes()
|
||||||
|
record.seq = 42
|
||||||
|
|
||||||
|
for addr_str in ["/ip4/127.0.0.1/tcp/4001", "/ip4/127.0.0.1/tcp/4002"]:
|
||||||
|
ma = Multiaddr(addr_str)
|
||||||
|
addr_info = pb.PeerRecord.AddressInfo()
|
||||||
|
addr_info.multiaddr = ma.to_bytes()
|
||||||
|
record.addresses.append(addr_info)
|
||||||
|
|
||||||
|
result = peer_record_from_protobuf(record)
|
||||||
|
|
||||||
|
assert result.peer_id == peer_id
|
||||||
|
assert result.seq == 42
|
||||||
|
assert len(result.addrs) == 2
|
||||||
|
assert str(result.addrs[0]) == "/ip4/127.0.0.1/tcp/4001"
|
||||||
|
assert str(result.addrs[1]) == "/ip4/127.0.0.1/tcp/4002"
|
||||||
|
|
||||||
|
def test_to_protobuf_generates_correct_message():
|
||||||
|
peer_id = ID.from_base58("QmNM23MiU1Kd7yfiKVdUnaDo8RYca8By4zDmr7uSaVV8Px")
|
||||||
|
addrs = [Multiaddr("/ip4/127.0.0.1/tcp/4001")]
|
||||||
|
seq = 12345
|
||||||
|
|
||||||
|
record = PeerRecord(peer_id, addrs, seq)
|
||||||
|
proto = record.to_protobuf()
|
||||||
|
|
||||||
|
assert isinstance(proto, pb.PeerRecord)
|
||||||
|
assert proto.peer_id == peer_id.to_bytes()
|
||||||
|
assert proto.seq == seq
|
||||||
|
assert len(proto.addresses) == 1
|
||||||
|
assert proto.addresses[0].multiaddr == addrs[0].to_bytes()
|
||||||
|
|
||||||
|
def test_unmarshal_record_roundtrip():
|
||||||
|
record = PeerRecord(
|
||||||
|
peer_id = ID.from_base58("QmNM23MiU1Kd7yfiKVdUnaDo8RYca8By4zDmr7uSaVV8Px"),
|
||||||
|
addrs = [Multiaddr("/ip4/127.0.0.1/tcp/4001")],
|
||||||
|
seq = 999,
|
||||||
|
)
|
||||||
|
|
||||||
|
serialized = record.to_protobuf().SerializeToString()
|
||||||
|
deserialized = unmarshal_record(serialized)
|
||||||
|
|
||||||
|
assert deserialized.peer_id == record.peer_id
|
||||||
|
assert deserialized.seq == record.seq
|
||||||
|
assert len(deserialized.addrs) == 1
|
||||||
|
assert deserialized.addrs[0] == record.addrs[0]
|
||||||
|
|
||||||
|
def test_marshal_record_and_equal():
|
||||||
|
peer_id = ID.from_base58("QmNM23MiU1Kd7yfiKVdUnaDo8RYca8By4zDmr7uSaVV8Px")
|
||||||
|
addrs = [Multiaddr("/ip4/127.0.0.1/tcp/4001")]
|
||||||
|
original = PeerRecord(peer_id, addrs)
|
||||||
|
|
||||||
|
serialized = original.marshal_record()
|
||||||
|
deserailzed = unmarshal_record(serialized)
|
||||||
|
|
||||||
|
assert original.equal(deserailzed)
|
||||||
Reference in New Issue
Block a user