mirror of
https://github.com/varun-r-mallya/py-libp2p.git
synced 2025-12-31 20:36:24 +00:00
Merge branch 'main' into add-ws-transport
This commit is contained in:
@ -13,6 +13,8 @@ from libp2p.identity.identify.identify import (
|
||||
_multiaddr_to_bytes,
|
||||
parse_identify_response,
|
||||
)
|
||||
from libp2p.peer.envelope import Envelope, consume_envelope, unmarshal_envelope
|
||||
from libp2p.peer.peer_record import unmarshal_record
|
||||
from tests.utils.factories import (
|
||||
host_pair_factory,
|
||||
)
|
||||
@ -40,6 +42,19 @@ async def test_identify_protocol(security_protocol):
|
||||
# Parse the response (handles both old and new formats)
|
||||
identify_response = parse_identify_response(response)
|
||||
|
||||
# Validate the recieved envelope and then store it in the certified-addr-book
|
||||
envelope, record = consume_envelope(
|
||||
identify_response.signedPeerRecord, "libp2p-peer-record"
|
||||
)
|
||||
assert host_b.peerstore.consume_peer_record(envelope, ttl=7200)
|
||||
|
||||
# Check if the peer_id in the record is same as of host_a
|
||||
assert record.peer_id == host_a.get_id()
|
||||
|
||||
# Check if the peer-record is correctly consumed
|
||||
assert host_a.get_addrs() == host_b.peerstore.addrs(host_a.get_id())
|
||||
assert isinstance(host_b.peerstore.get_peer_record(host_a.get_id()), Envelope)
|
||||
|
||||
logger.debug("host_a: %s", host_a.get_addrs())
|
||||
logger.debug("host_b: %s", host_b.get_addrs())
|
||||
|
||||
@ -71,5 +86,14 @@ async def test_identify_protocol(security_protocol):
|
||||
# Check protocols
|
||||
assert set(identify_response.protocols) == set(host_a.get_mux().get_protocols())
|
||||
|
||||
# sanity check
|
||||
assert identify_response == _mk_identify_protobuf(host_a, cleaned_addr)
|
||||
# sanity check if the peer_id of the identify msg are same
|
||||
assert (
|
||||
unmarshal_record(
|
||||
unmarshal_envelope(identify_response.signedPeerRecord).raw_payload
|
||||
).peer_id
|
||||
== unmarshal_record(
|
||||
unmarshal_envelope(
|
||||
_mk_identify_protobuf(host_a, cleaned_addr).signedPeerRecord
|
||||
).raw_payload
|
||||
).peer_id
|
||||
)
|
||||
|
||||
@ -3,7 +3,10 @@ from multiaddr import (
|
||||
Multiaddr,
|
||||
)
|
||||
|
||||
from libp2p.crypto.rsa import create_new_key_pair
|
||||
from libp2p.peer.envelope import Envelope, seal_record
|
||||
from libp2p.peer.id import ID
|
||||
from libp2p.peer.peer_record import PeerRecord
|
||||
from libp2p.peer.peerstore import (
|
||||
PeerStore,
|
||||
PeerStoreError,
|
||||
@ -84,3 +87,53 @@ def test_peers_with_addrs():
|
||||
store.clear_addrs(ID(b"peer2"))
|
||||
|
||||
assert set(store.peers_with_addrs()) == {ID(b"peer3")}
|
||||
|
||||
|
||||
def test_ceritified_addr_book():
|
||||
store = PeerStore()
|
||||
|
||||
key_pair = create_new_key_pair()
|
||||
peer_id = ID.from_pubkey(key_pair.public_key)
|
||||
addrs = [
|
||||
Multiaddr("/ip4/127.0.0.1/tcp/9000"),
|
||||
Multiaddr("/ip4/127.0.0.1/tcp/9001"),
|
||||
]
|
||||
ttl = 60
|
||||
|
||||
# Construct signed PereRecord
|
||||
record = PeerRecord(peer_id, addrs, 21)
|
||||
envelope = seal_record(record, key_pair.private_key)
|
||||
|
||||
result = store.consume_peer_record(envelope, ttl)
|
||||
assert result is True
|
||||
# Retrieve the record
|
||||
|
||||
retrieved = store.get_peer_record(peer_id)
|
||||
assert retrieved is not None
|
||||
assert isinstance(retrieved, Envelope)
|
||||
|
||||
addr_list = store.addrs(peer_id)
|
||||
assert set(addr_list) == set(addrs)
|
||||
|
||||
# Now try to push an older record (should be rejected)
|
||||
old_record = PeerRecord(peer_id, [Multiaddr("/ip4/10.0.0.1/tcp/4001")], 20)
|
||||
old_envelope = seal_record(old_record, key_pair.private_key)
|
||||
result = store.consume_peer_record(old_envelope, ttl)
|
||||
assert result is False
|
||||
|
||||
# Push a new record (should override)
|
||||
new_addrs = [Multiaddr("/ip4/192.168.0.1/tcp/5001")]
|
||||
new_record = PeerRecord(peer_id, new_addrs, 23)
|
||||
new_envelope = seal_record(new_record, key_pair.private_key)
|
||||
result = store.consume_peer_record(new_envelope, ttl)
|
||||
assert result is True
|
||||
|
||||
# Confirm the record is updated
|
||||
latest = store.get_peer_record(peer_id)
|
||||
assert isinstance(latest, Envelope)
|
||||
assert latest.record().seq == 23
|
||||
|
||||
# Merged addresses = old addres + new_addrs
|
||||
expected_addrs = set(new_addrs)
|
||||
actual_addrs = set(store.addrs(peer_id))
|
||||
assert actual_addrs == expected_addrs
|
||||
|
||||
129
tests/core/peer/test_envelope.py
Normal file
129
tests/core/peer/test_envelope.py
Normal file
@ -0,0 +1,129 @@
|
||||
from multiaddr import Multiaddr
|
||||
|
||||
from libp2p.crypto.rsa import (
|
||||
create_new_key_pair,
|
||||
)
|
||||
from libp2p.peer.envelope import (
|
||||
Envelope,
|
||||
consume_envelope,
|
||||
make_unsigned,
|
||||
seal_record,
|
||||
unmarshal_envelope,
|
||||
)
|
||||
from libp2p.peer.id import ID
|
||||
import libp2p.peer.pb.crypto_pb2 as crypto_pb
|
||||
import libp2p.peer.pb.envelope_pb2 as env_pb
|
||||
from libp2p.peer.peer_record import PeerRecord
|
||||
|
||||
DOMAIN = "libp2p-peer-record"
|
||||
|
||||
|
||||
def test_basic_protobuf_serialization_deserialization():
|
||||
pubkey = crypto_pb.PublicKey()
|
||||
pubkey.Type = crypto_pb.KeyType.Ed25519
|
||||
pubkey.Data = b"\x01\x02\x03"
|
||||
|
||||
env = env_pb.Envelope()
|
||||
env.public_key.CopyFrom(pubkey)
|
||||
env.payload_type = b"\x03\x01"
|
||||
env.payload = b"test-payload"
|
||||
env.signature = b"signature-bytes"
|
||||
|
||||
serialized = env.SerializeToString()
|
||||
|
||||
new_env = env_pb.Envelope()
|
||||
new_env.ParseFromString(serialized)
|
||||
|
||||
assert new_env.public_key.Type == crypto_pb.KeyType.Ed25519
|
||||
assert new_env.public_key.Data == b"\x01\x02\x03"
|
||||
assert new_env.payload_type == b"\x03\x01"
|
||||
assert new_env.payload == b"test-payload"
|
||||
assert new_env.signature == b"signature-bytes"
|
||||
|
||||
|
||||
def test_enevelope_marshal_unmarshal_roundtrip():
|
||||
keypair = create_new_key_pair()
|
||||
pubkey = keypair.public_key
|
||||
private_key = keypair.private_key
|
||||
|
||||
payload_type = b"\x03\x01"
|
||||
payload = b"test-record"
|
||||
sig = private_key.sign(make_unsigned(DOMAIN, payload_type, payload))
|
||||
|
||||
env = Envelope(pubkey, payload_type, payload, sig)
|
||||
serialized = env.marshal_envelope()
|
||||
new_env = unmarshal_envelope(serialized)
|
||||
|
||||
assert new_env.public_key == pubkey
|
||||
assert new_env.payload_type == payload_type
|
||||
assert new_env.raw_payload == payload
|
||||
assert new_env.signature == sig
|
||||
|
||||
|
||||
def test_seal_and_consume_envelope_roundtrip():
|
||||
keypair = create_new_key_pair()
|
||||
priv_key = keypair.private_key
|
||||
pub_key = keypair.public_key
|
||||
|
||||
peer_id = ID.from_pubkey(pub_key)
|
||||
addrs = [Multiaddr("/ip4/127.0.0.1/tcp/4001"), Multiaddr("/ip4/127.0.0.1/tcp/4002")]
|
||||
seq = 12345
|
||||
|
||||
record = PeerRecord(peer_id=peer_id, addrs=addrs, seq=seq)
|
||||
|
||||
# Seal
|
||||
envelope = seal_record(record, priv_key)
|
||||
serialized = envelope.marshal_envelope()
|
||||
|
||||
# Consume
|
||||
env, rec = consume_envelope(serialized, record.domain())
|
||||
|
||||
# Assertions
|
||||
assert env.public_key == pub_key
|
||||
assert rec.peer_id == peer_id
|
||||
assert rec.seq == seq
|
||||
assert rec.addrs == addrs
|
||||
|
||||
|
||||
def test_envelope_equal():
|
||||
# Create a new keypair
|
||||
keypair = create_new_key_pair()
|
||||
private_key = keypair.private_key
|
||||
|
||||
# Create a mock PeerRecord
|
||||
record = PeerRecord(
|
||||
peer_id=ID.from_base58("QmNM23MiU1Kd7yfiKVdUnaDo8RYca8By4zDmr7uSaVV8Px"),
|
||||
seq=1,
|
||||
addrs=[Multiaddr("/ip4/127.0.0.1/tcp/4001")],
|
||||
)
|
||||
|
||||
# Seal it into an Envelope
|
||||
env1 = seal_record(record, private_key)
|
||||
|
||||
# Create a second identical envelope
|
||||
env2 = Envelope(
|
||||
public_key=env1.public_key,
|
||||
payload_type=env1.payload_type,
|
||||
raw_payload=env1.raw_payload,
|
||||
signature=env1.signature,
|
||||
)
|
||||
|
||||
# They should be equal
|
||||
assert env1.equal(env2)
|
||||
|
||||
# Now change something — payload type
|
||||
env2.payload_type = b"\x99\x99"
|
||||
assert not env1.equal(env2)
|
||||
|
||||
# Restore payload_type but change signature
|
||||
env2.payload_type = env1.payload_type
|
||||
env2.signature = b"wrong-signature"
|
||||
assert not env1.equal(env2)
|
||||
|
||||
# Restore signature but change payload
|
||||
env2.signature = env1.signature
|
||||
env2.raw_payload = b"tampered"
|
||||
assert not env1.equal(env2)
|
||||
|
||||
# Finally, test with a non-envelope object
|
||||
assert not env1.equal("not-an-envelope")
|
||||
112
tests/core/peer/test_peer_record.py
Normal file
112
tests/core/peer/test_peer_record.py
Normal file
@ -0,0 +1,112 @@
|
||||
import time
|
||||
|
||||
from multiaddr import Multiaddr
|
||||
|
||||
from libp2p.peer.id import ID
|
||||
import libp2p.peer.pb.peer_record_pb2 as pb
|
||||
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)
|
||||
@ -120,3 +120,30 @@ async def test_addr_stream_yields_new_addrs():
|
||||
nursery.cancel_scope.cancel()
|
||||
|
||||
assert collected == [addr1, addr2]
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_cleanup_task_remove_expired_data():
|
||||
store = PeerStore()
|
||||
peer_id = ID(b"peer123")
|
||||
addr = Multiaddr("/ip4/127.0.0.1/tcp/4040")
|
||||
|
||||
# Insert addrs with short TTL (0.01s)
|
||||
store.add_addr(peer_id, addr, 1)
|
||||
|
||||
assert store.addrs(peer_id) == [addr]
|
||||
assert peer_id in store.peer_data_map
|
||||
|
||||
# Start cleanup task in a nursery
|
||||
async with trio.open_nursery() as nursery:
|
||||
# Run the cleanup task with a short interval so it runs soon
|
||||
nursery.start_soon(store.start_cleanup_task, 1)
|
||||
|
||||
# Sleep long enough for TTL to expire and cleanup to run
|
||||
await trio.sleep(3)
|
||||
|
||||
# Cancel the nursery to stop background tasks
|
||||
nursery.cancel_scope.cancel()
|
||||
|
||||
# Confirm the peer data is gone from the peer_data_map
|
||||
assert peer_id not in store.peer_data_map
|
||||
|
||||
Reference in New Issue
Block a user