Skip to content

Commit a720fed

Browse files
authored
Merge pull request libp2p#996 from codemaestro64/trd-enhancements
Tracking: Transport, Relay, and Discovery Enhancements
2 parents e7dde5e + a31db10 commit a720fed

File tree

8 files changed

+1406
-55
lines changed

8 files changed

+1406
-55
lines changed

libp2p/relay/circuit_v2/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ def enable_stop(self) -> bool: # pragma: no cover – helper
132132
def enable_client(self) -> bool: # pragma: no cover – helper
133133
return bool(self.roles & RelayRole.CLIENT)
134134

135+
@property
136+
def enable_dht_discovery(self) -> bool: # pragma: no cover - helper
137+
return False
138+
135139
def __post_init__(self) -> None:
136140
"""Initialize default values."""
137141
if self.limits is None:

libp2p/relay/circuit_v2/protocol.py

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,9 @@ def __init__(
162162
self.read_timeout = read_timeout
163163
self.write_timeout = write_timeout
164164
self.close_timeout = close_timeout
165-
self.resource_manager = RelayResourceManager(self.limits)
165+
self.resource_manager = RelayResourceManager(
166+
self.limits, self.host.get_peerstore()
167+
)
166168
self._active_relays: dict[ID, tuple[INetStream, INetStream | None]] = {}
167169
self.event_started = trio.Event()
168170

@@ -550,38 +552,46 @@ async def handle_incoming_connection(
550552
async def _handle_reserve(self, stream: INetStream, msg: HopMessage) -> None:
551553
"""Handle a reservation request."""
552554
peer_id = None
555+
signed_envelope = None
553556
try:
554557
peer_id = ID(msg.peer)
555558
logger.debug("Handling reservation request from peer %s", peer_id)
556559
signed_envelope_bytes, _ = env_to_send_in_RPC(self.host)
557560
signed_envelope = unmarshal_envelope(signed_envelope_bytes)
558561

559-
# Check if we can accept more reservations
560-
if not self.resource_manager.can_accept_reservation(peer_id):
561-
logger.debug("Reservation limit exceeded for peer %s", peer_id)
562-
# Send status message with STATUS type
563-
status = create_status(
564-
code=StatusCode.RESOURCE_LIMIT_EXCEEDED,
565-
message="Reservation limit exceeded",
566-
)
562+
# Check if peer already has a reservation
563+
if self.resource_manager.has_reservation(peer_id):
564+
logger.debug("Peer %s already has a reservation — refreshing", peer_id)
565+
ttl = self.resource_manager.refresh_reservation(peer_id)
566+
status_code = StatusCode.OK
567+
status_msg_text = "Reservation refreshed"
568+
else:
569+
# Check if we can accept more reservations
570+
if not self.resource_manager.can_accept_reservation(peer_id):
571+
logger.debug("Reservation limit exceeded for peer %s", peer_id)
572+
# Send status message with STATUS type
573+
status = create_status(
574+
code=StatusCode.RESOURCE_LIMIT_EXCEEDED,
575+
message="Reservation limit exceeded",
576+
)
567577

568-
status_msg = HopMessage(
569-
type=HopMessage.STATUS,
570-
status=status,
571-
senderRecord=signed_envelope.marshal_envelope(),
572-
)
573-
await stream.write(status_msg.SerializeToString())
574-
return
578+
status_msg = HopMessage(
579+
type=HopMessage.STATUS,
580+
status=status,
581+
senderRecord=signed_envelope.marshal_envelope(),
582+
)
583+
await stream.write(status_msg.SerializeToString())
584+
return
575585

576-
# Accept reservation
577-
logger.debug("Accepting reservation from peer %s", peer_id)
578-
ttl = self.resource_manager.reserve(peer_id)
586+
# Accept reservation
587+
logger.debug("Accepting new reservation from peer %s", peer_id)
588+
ttl = self.resource_manager.reserve(peer_id)
589+
status_code = StatusCode.OK
590+
status_msg_text = "Reservation accepted"
579591

580592
# Send reservation success response
581593
with trio.fail_after(self.write_timeout):
582-
status = create_status(
583-
code=StatusCode.OK, message="Reservation accepted"
584-
)
594+
status = create_status(code=status_code, message=status_msg_text)
585595

586596
response = HopMessage(
587597
type=HopMessage.STATUS,

libp2p/relay/circuit_v2/resources.py

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import os
1414
import time
1515

16+
from libp2p.abc import IPeerStore
1617
from libp2p.peer.id import (
1718
ID,
1819
)
@@ -61,7 +62,7 @@ def __init__(self, peer_id: ID, limits: RelayLimits):
6162
self.peer_id = peer_id
6263
self.limits = limits
6364
self.created_at = time.time()
64-
self.expires_at = self.created_at + limits.duration
65+
self.expires_at = int(self.created_at + limits.duration)
6566
self.data_used = 0
6667
self.active_connections = 0
6768
self.voucher = self._generate_voucher()
@@ -137,18 +138,21 @@ class RelayResourceManager:
137138
- Managing connection quotas
138139
"""
139140

140-
def __init__(self, limits: RelayLimits):
141+
def __init__(self, limits: RelayLimits, peer_store: IPeerStore):
141142
"""
142143
Initialize the resource manager.
143144
144145
Parameters
145146
----------
146147
limits : RelayLimits
147148
The resource limits to enforce
149+
peer_store : IPeerStore
150+
Peer store for retrieving public keys and peer metadata
148151
149152
"""
150153
self.limits = limits
151154
self._reservations: dict[ID, Reservation] = {}
155+
self.peer_store = peer_store
152156

153157
def can_accept_reservation(self, peer_id: ID) -> bool:
154158
"""
@@ -212,13 +216,27 @@ def verify_reservation(self, peer_id: ID, proto_res: PbReservation) -> bool:
212216
True if the reservation is valid
213217
214218
"""
215-
# TODO: Implement voucher and signature verification
219+
# Fetch the reservation
216220
reservation = self._reservations.get(peer_id)
217-
return (
218-
reservation is not None
219-
and not reservation.is_expired()
220-
and reservation.expires_at == proto_res.expire
221-
)
221+
222+
# Reject if reservation is missing, expired, or mismatched
223+
if (
224+
reservation is None
225+
or reservation.is_expired()
226+
or reservation.voucher != proto_res.voucher
227+
or reservation.expires_at != proto_res.expire
228+
):
229+
return False
230+
231+
# verify signature
232+
try:
233+
public_key = self.peer_store.pubkey(peer_id)
234+
if public_key is None:
235+
return False
236+
237+
return public_key.verify(proto_res.voucher, proto_res.signature)
238+
except Exception:
239+
return False
222240

223241
def can_accept_connection(self, peer_id: ID) -> bool:
224242
"""
@@ -274,3 +292,30 @@ def reserve(self, peer_id: ID) -> int:
274292
# Create new reservation
275293
self.create_reservation(peer_id)
276294
return self.limits.duration
295+
296+
def has_reservation(self, peer_id: ID) -> bool:
297+
"""
298+
Check if a reservation already exists for a peer
299+
300+
Parameters
301+
----------
302+
peer_id : ID
303+
The peer ID to check for
304+
305+
Returns
306+
-------
307+
bool
308+
True if reservation exists, False otherwise
309+
310+
"""
311+
existing = self._reservations.get(peer_id)
312+
if existing and not existing.is_expired():
313+
return True
314+
return False
315+
316+
def refresh_reservation(self, peer_id: ID) -> int:
317+
if self.has_reservation(peer_id):
318+
self.create_reservation(peer_id)
319+
return self.limits.duration
320+
321+
return 0

0 commit comments

Comments
 (0)