Skip to content

Commit f12ca4e

Browse files
authored
Merge branch 'main' into write_msg_pubsub
2 parents 8bddbfb + 5a3adad commit f12ca4e

File tree

14 files changed

+408
-39
lines changed

14 files changed

+408
-39
lines changed

examples/doc-examples/example_encryption_insecure.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ async def main():
2424
insecure_transport = InsecureTransport(
2525
# local_key_pair: The key pair used for libp2p identity
2626
local_key_pair=key_pair,
27+
# secure_bytes_provider: Optional function to generate secure random bytes
28+
# (defaults to secrets.token_bytes)
29+
secure_bytes_provider=None, # Use default implementation
30+
# peerstore: Optional peerstore to store peer IDs and public keys
31+
# (defaults to None)
32+
peerstore=None,
2733
)
2834

2935
# Create a security options dictionary mapping protocol ID to transport

libp2p/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,9 @@ def new_swarm(
200200
key_pair, noise_privkey=noise_key_pair.private_key
201201
),
202202
TProtocol(secio.ID): secio.Transport(key_pair),
203-
TProtocol(PLAINTEXT_PROTOCOL_ID): InsecureTransport(key_pair),
203+
TProtocol(PLAINTEXT_PROTOCOL_ID): InsecureTransport(
204+
key_pair, peerstore=peerstore_opt
205+
),
204206
}
205207

206208
# Use given muxer preference if provided, otherwise use global default

libp2p/kad_dht/common.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""
2+
Shared constants and protocol parameters for the Kademlia DHT.
3+
"""
4+
5+
from libp2p.custom_types import (
6+
TProtocol,
7+
)
8+
9+
# Constants for the Kademlia algorithm
10+
ALPHA = 3 # Concurrency parameter
11+
PROTOCOL_ID = TProtocol("/ipfs/kad/1.0.0")
12+
QUERY_TIMEOUT = 10
13+
14+
TTL = DEFAULT_TTL = 24 * 60 * 60 # 24 hours in seconds

libp2p/kad_dht/kad_dht.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
implementation based on the Kademlia algorithm and protocol.
66
"""
77

8-
from enum import Enum
8+
from enum import (
9+
Enum,
10+
)
911
import logging
1012
import time
1113

@@ -18,9 +20,6 @@
1820
from libp2p.abc import (
1921
IHost,
2022
)
21-
from libp2p.custom_types import (
22-
TProtocol,
23-
)
2423
from libp2p.network.stream.net_stream import (
2524
INetStream,
2625
)
@@ -34,6 +33,11 @@
3433
Service,
3534
)
3635

36+
from .common import (
37+
ALPHA,
38+
PROTOCOL_ID,
39+
QUERY_TIMEOUT,
40+
)
3741
from .pb.kademlia_pb2 import (
3842
Message,
3943
)
@@ -53,11 +57,7 @@
5357
logger = logging.getLogger("kademlia-example.kad_dht")
5458
# logger = logging.getLogger("libp2p.kademlia")
5559
# Default parameters
56-
PROTOCOL_ID = TProtocol("/ipfs/kad/1.0.0")
57-
ROUTING_TABLE_REFRESH_INTERVAL = 1 * 60 # 1 min in seconds for testing
58-
TTL = 24 * 60 * 60 # 24 hours in seconds
59-
ALPHA = 3
60-
QUERY_TIMEOUT = 10 # seconds
60+
ROUTING_TABLE_REFRESH_INTERVAL = 60 # 1 min in seconds for testing
6161

6262

6363
class DHTMode(Enum):

libp2p/kad_dht/peer_routing.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@
1515
INetStream,
1616
IPeerRouting,
1717
)
18-
from libp2p.custom_types import (
19-
TProtocol,
20-
)
2118
from libp2p.peer.id import (
2219
ID,
2320
)
2421
from libp2p.peer.peerinfo import (
2522
PeerInfo,
2623
)
2724

25+
from .common import (
26+
ALPHA,
27+
PROTOCOL_ID,
28+
)
2829
from .pb.kademlia_pb2 import (
2930
Message,
3031
)
@@ -38,10 +39,7 @@
3839
# logger = logging.getLogger("libp2p.kademlia.peer_routing")
3940
logger = logging.getLogger("kademlia-example.peer_routing")
4041

41-
# Constants for the Kademlia algorithm
42-
ALPHA = 3 # Concurrency parameter
4342
MAX_PEER_LOOKUP_ROUNDS = 20 # Maximum number of rounds in peer lookup
44-
PROTOCOL_ID = TProtocol("/ipfs/kad/1.0.0")
4543

4644

4745
class PeerRouting(IPeerRouting):
@@ -62,7 +60,6 @@ def __init__(self, host: IHost, routing_table: RoutingTable):
6260
"""
6361
self.host = host
6462
self.routing_table = routing_table
65-
self.protocol_id = PROTOCOL_ID
6663

6764
async def find_peer(self, peer_id: ID) -> PeerInfo | None:
6865
"""
@@ -247,7 +244,7 @@ async def _query_peer_for_closest(self, peer: ID, target_key: bytes) -> list[ID]
247244
# Open a stream to the peer using the Kademlia protocol
248245
logger.debug(f"Opening stream to {peer} for closest peers query")
249246
try:
250-
stream = await self.host.new_stream(peer, [self.protocol_id])
247+
stream = await self.host.new_stream(peer, [PROTOCOL_ID])
251248
logger.debug(f"Stream opened to {peer}")
252249
except Exception as e:
253250
logger.warning(f"Failed to open stream to {peer}: {e}")

libp2p/kad_dht/provider_store.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
PeerInfo,
3030
)
3131

32+
from .common import (
33+
ALPHA,
34+
PROTOCOL_ID,
35+
QUERY_TIMEOUT,
36+
)
3237
from .pb.kademlia_pb2 import (
3338
Message,
3439
)
@@ -40,9 +45,6 @@
4045
PROVIDER_RECORD_REPUBLISH_INTERVAL = 22 * 60 * 60 # 22 hours in seconds
4146
PROVIDER_RECORD_EXPIRATION_INTERVAL = 48 * 60 * 60 # 48 hours in seconds
4247
PROVIDER_ADDRESS_TTL = 30 * 60 # 30 minutes in seconds
43-
PROTOCOL_ID = TProtocol("/ipfs/kad/1.0.0")
44-
ALPHA = 3 # Number of parallel queries/advertisements
45-
QUERY_TIMEOUT = 10 # Timeout for each query in seconds
4648

4749

4850
class ProviderRecord:

libp2p/kad_dht/routing_table.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,19 @@
1313
from libp2p.abc import (
1414
IHost,
1515
)
16-
from libp2p.custom_types import (
17-
TProtocol,
16+
from libp2p.kad_dht.utils import (
17+
xor_distance,
1818
)
19-
from libp2p.kad_dht.utils import xor_distance
2019
from libp2p.peer.id import (
2120
ID,
2221
)
2322
from libp2p.peer.peerinfo import (
2423
PeerInfo,
2524
)
2625

26+
from .common import (
27+
PROTOCOL_ID,
28+
)
2729
from .pb.kademlia_pb2 import (
2830
Message,
2931
)
@@ -242,12 +244,9 @@ async def _ping_peer(self, peer_id: ID) -> bool:
242244
if not peer_info:
243245
raise ValueError(f"Peer {peer_id} not in bucket")
244246

245-
# Default protocol ID for Kademlia DHT
246-
protocol_id = TProtocol("/ipfs/kad/1.0.0")
247-
248247
try:
249248
# Open a stream to the peer with the DHT protocol
250-
stream = await self.host.new_stream(peer_id, [protocol_id])
249+
stream = await self.host.new_stream(peer_id, [PROTOCOL_ID])
251250

252251
try:
253252
# Create ping protobuf message

libp2p/kad_dht/value_store.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@
1919
ID,
2020
)
2121

22+
from .common import (
23+
DEFAULT_TTL,
24+
PROTOCOL_ID,
25+
)
2226
from .pb.kademlia_pb2 import (
2327
Message,
2428
)
2529

2630
# logger = logging.getLogger("libp2p.kademlia.value_store")
2731
logger = logging.getLogger("kademlia-example.value_store")
2832

29-
# Default time to live for values in seconds (24 hours)
30-
DEFAULT_TTL = 24 * 60 * 60
31-
PROTOCOL_ID = TProtocol("/ipfs/kad/1.0.0")
32-
3333

3434
class ValueStore:
3535
"""

libp2p/security/insecure/transport.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
from collections.abc import Callable
2+
13
from libp2p.abc import (
4+
IPeerStore,
25
IRawConnection,
36
ISecureConn,
47
)
58
from libp2p.crypto.exceptions import (
69
MissingDeserializerError,
710
)
811
from libp2p.crypto.keys import (
12+
KeyPair,
913
PrivateKey,
1014
PublicKey,
1115
)
@@ -30,11 +34,15 @@
3034
from libp2p.peer.id import (
3135
ID,
3236
)
37+
from libp2p.peer.peerstore import (
38+
PeerStoreError,
39+
)
3340
from libp2p.security.base_session import (
3441
BaseSession,
3542
)
3643
from libp2p.security.base_transport import (
3744
BaseSecureTransport,
45+
default_secure_bytes_provider,
3846
)
3947
from libp2p.security.exceptions import (
4048
HandshakeFailure,
@@ -102,6 +110,7 @@ async def run_handshake(
102110
conn: IRawConnection,
103111
is_initiator: bool,
104112
remote_peer_id: ID | None,
113+
peerstore: IPeerStore | None = None,
105114
) -> ISecureConn:
106115
"""Raise `HandshakeFailure` when handshake failed."""
107116
msg = make_exchange_message(local_private_key.get_public_key())
@@ -164,7 +173,14 @@ async def run_handshake(
164173
conn=conn,
165174
)
166175

167-
# TODO: Store `pubkey` and `peer_id` to `PeerStore`
176+
# Store `pubkey` and `peer_id` to `PeerStore`
177+
if peerstore is not None:
178+
try:
179+
peerstore.add_pubkey(received_peer_id, received_pubkey)
180+
except PeerStoreError:
181+
# If peer ID and pubkey don't match, it would have already been caught above
182+
# This might happen if the peer is already in the store
183+
pass
168184

169185
return secure_conn
170186

@@ -175,6 +191,18 @@ class InsecureTransport(BaseSecureTransport):
175191
transport does not add any additional security.
176192
"""
177193

194+
def __init__(
195+
self,
196+
local_key_pair: KeyPair,
197+
secure_bytes_provider: Callable[[int], bytes] | None = None,
198+
peerstore: IPeerStore | None = None,
199+
) -> None:
200+
# If secure_bytes_provider is None, use the default one
201+
if secure_bytes_provider is None:
202+
secure_bytes_provider = default_secure_bytes_provider
203+
super().__init__(local_key_pair, secure_bytes_provider)
204+
self.peerstore = peerstore
205+
178206
async def secure_inbound(self, conn: IRawConnection) -> ISecureConn:
179207
"""
180208
Secure the connection, either locally or by communicating with opposing
@@ -183,8 +211,9 @@ async def secure_inbound(self, conn: IRawConnection) -> ISecureConn:
183211
184212
:return: secure connection object (that implements secure_conn_interface)
185213
"""
214+
# For inbound connections, we don't know the remote peer ID yet
186215
return await run_handshake(
187-
self.local_peer, self.local_private_key, conn, False, None
216+
self.local_peer, self.local_private_key, conn, False, None, self.peerstore
188217
)
189218

190219
async def secure_outbound(self, conn: IRawConnection, peer_id: ID) -> ISecureConn:
@@ -195,7 +224,7 @@ async def secure_outbound(self, conn: IRawConnection, peer_id: ID) -> ISecureCon
195224
:return: secure connection object (that implements secure_conn_interface)
196225
"""
197226
return await run_handshake(
198-
self.local_peer, self.local_private_key, conn, True, peer_id
227+
self.local_peer, self.local_private_key, conn, True, peer_id, self.peerstore
199228
)
200229

201230

newsfragments/631.feature.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Store public key and peer ID in peerstore during handshake
2+
3+
Modified the InsecureTransport class to accept an optional peerstore parameter and updated the handshake process to store the received public key and peer ID in the peerstore when available.
4+
5+
Added test cases to verify:
6+
1. The peerstore remains unchanged when handshake fails due to peer ID mismatch
7+
2. The handshake correctly adds a public key to a peer ID that already exists in the peerstore but doesn't have a public key yet

0 commit comments

Comments
 (0)