Skip to content

Commit 57e803d

Browse files
committed
Merge branch 'main' of https://github.com/Khwahish29/py-libp2p into replace-async-with-anyio
2 parents 935ac0a + 18c6f52 commit 57e803d

30 files changed

+933
-43
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ ifndef bump
124124
endif
125125

126126
check-git:
127-
# require that upstream is configured for ethereum/py-libp2p
127+
# require that upstream is configured for libp2p/py-libp2p
128128
@if ! git remote -v | grep "upstream[[:space:]][email protected]:libp2p/py-libp2p.git (push)\|upstream[[:space:]]https://github.com/libp2p/py-libp2p (push)"; then \
129129
echo "Error: You must have a remote named 'upstream' that points to 'py-libp2p'"; \
130130
exit 1; \

docs/contributing.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Contributing
33

44
Thank you for your interest in contributing! We welcome all contributions no matter
55
their size. Please read along to learn how to get started. If you get stuck, feel free
6-
to ask for help in `Ethereum Python Discord server <https://discord.gg/GHryRvPB84>`_.
6+
to ask for help in `Libp2p Discover Server <https://discord.gg/GK8TxRNh2s>`_.
77

88
Setting the stage
99
~~~~~~~~~~~~~~~~~
@@ -294,7 +294,7 @@ do not pass the CI build yet won't get reviewed unless explicitly requested.
294294

295295
If the pull request introduces changes that should be reflected in the release notes,
296296
please add a newsfragment file as explained
297-
`here <https://github.com/ethereum/py-libp2p/blob/main/newsfragments/README.md>`_.
297+
`here <https://github.com/libp2p/py-libp2p/tree/main/newsfragments>`_.
298298

299299
If possible, the change to the release notes file should be included in the commit that
300300
introduces the feature or bugfix.

docs/getting_started.rst

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,106 @@ For Python, you can use the bootstrap list to connect to known peers:
9494
Debugging
9595
---------
9696

97-
When running libp2p you may want to see what things are happening behind the scenes. You can enable debug logging by setting the appropriate log level:
97+
When running libp2p you may want to see what things are happening behind the scenes. You can enable debug logging using the `LIBP2P_DEBUG` environment variable. This allows for fine-grained control over which modules log at which levels.
9898

99-
.. code:: python
99+
Basic Usage
100+
~~~~~~~~~~~
101+
102+
To enable debug logging for all modules:
103+
104+
.. code:: bash
105+
106+
# Method 1: Using export (persists for the shell session)
107+
# On Unix-like systems (Linux, macOS):
108+
export LIBP2P_DEBUG=DEBUG
109+
# On Windows (Command Prompt):
110+
set LIBP2P_DEBUG=DEBUG
111+
# On Windows (PowerShell):
112+
$env:LIBP2P_DEBUG="DEBUG"
113+
114+
# Method 2: Setting for a single command
115+
# On Unix-like systems:
116+
LIBP2P_DEBUG=DEBUG python your_script.py
117+
# On Windows (Command Prompt):
118+
set LIBP2P_DEBUG=DEBUG && python your_script.py
119+
# On Windows (PowerShell):
120+
$env:LIBP2P_DEBUG="DEBUG"; python your_script.py
121+
122+
# Method 3: Redirect logs to /dev/null to suppress console output
123+
# On Unix-like systems:
124+
LIBP2P_DEBUG=DEBUG python your_script.py 2>/dev/null
125+
# On Windows (Command Prompt):
126+
set LIBP2P_DEBUG=DEBUG && python your_script.py >nul 2>&1
127+
# On Windows (PowerShell):
128+
$env:LIBP2P_DEBUG="DEBUG"; python your_script.py *> $null
129+
130+
To enable debug logging for specific modules:
131+
132+
.. code:: bash
133+
134+
# Debug logging for identity module only
135+
# On Unix-like systems:
136+
export LIBP2P_DEBUG=identity.identify:DEBUG
137+
# On Windows (Command Prompt):
138+
set LIBP2P_DEBUG=identity.identify:DEBUG
139+
# On Windows (PowerShell):
140+
$env:LIBP2P_DEBUG="identity.identify:DEBUG"
141+
142+
# Multiple modules with different levels
143+
# On Unix-like systems:
144+
export LIBP2P_DEBUG=identity.identify:DEBUG,transport:INFO
145+
# On Windows (Command Prompt):
146+
set LIBP2P_DEBUG=identity.identify:DEBUG,transport:INFO
147+
# On Windows (PowerShell):
148+
$env:LIBP2P_DEBUG="identity.identify:DEBUG,transport:INFO"
149+
150+
Log Files
151+
~~~~~~~~~
152+
153+
By default, logs are written to a file in the system's temporary directory with a timestamp and unique identifier. The file path is printed to stderr when logging starts.
154+
155+
When no custom log file is specified:
156+
- Logs are written to both a default file and to stderr (console output)
157+
- The default file path is printed to stderr when logging starts
158+
159+
When a custom log file is specified:
160+
- Logs are written only to the specified file
161+
- No console output is generated
162+
163+
To specify a custom log file location:
164+
165+
.. code:: bash
166+
167+
# Method 1: Using export (persists for the shell session)
168+
# On Unix-like systems:
169+
export LIBP2P_DEBUG_FILE=/path/to/your/logfile.log
170+
# On Windows (Command Prompt):
171+
set LIBP2P_DEBUG_FILE=C:\path\to\your\logfile.log
172+
# On Windows (PowerShell):
173+
$env:LIBP2P_DEBUG_FILE="C:\path\to\your\logfile.log"
174+
175+
# Method 2: Setting for a single command
176+
# On Unix-like systems:
177+
LIBP2P_DEBUG=DEBUG LIBP2P_DEBUG_FILE=/path/to/your/logfile.log python your_script.py
178+
# On Windows (Command Prompt):
179+
set LIBP2P_DEBUG=DEBUG && set LIBP2P_DEBUG_FILE=C:\path\to\your\logfile.log && python your_script.py
180+
# On Windows (PowerShell):
181+
$env:LIBP2P_DEBUG="DEBUG"; $env:LIBP2P_DEBUG_FILE="C:\path\to\your\logfile.log"; python your_script.py
182+
183+
Log Format
184+
~~~~~~~~~~
185+
186+
Log messages follow this format:
187+
188+
.. code:: text
189+
190+
timestamp - module_name - level - message
191+
192+
Example output:
100193

101-
import logging
194+
.. code:: text
102195
103-
# Set debug level for libp2p
104-
logging.getLogger('libp2p').setLevel(logging.DEBUG)
196+
2024-01-01 12:00:00,123 - libp2p.identity.identify - DEBUG - Starting identify protocol
105197
106198
What's Next
107199
-----------

docs/libp2p.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Subpackages
1919
libp2p.stream_muxer
2020
libp2p.tools
2121
libp2p.transport
22+
libp2p.utils
2223

2324
Submodules
2425
----------

docs/libp2p.utils.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
libp2p.utils package
2+
====================
3+
4+
Submodules
5+
----------
6+
7+
libp2p.utils.logging module
8+
---------------------------
9+
10+
.. automodule:: libp2p.utils.logging
11+
:members:
12+
:undoc-members:
13+
:show-inheritance:
14+
15+
libp2p.utils.varint module
16+
--------------------------
17+
18+
.. automodule:: libp2p.utils.varint
19+
:members:
20+
:undoc-members:
21+
:show-inheritance:
22+
23+
libp2p.utils.version module
24+
---------------------------
25+
26+
.. automodule:: libp2p.utils.version
27+
:members:
28+
:undoc-members:
29+
:show-inheritance:

examples/pubsub/pubsub.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ async def run(topic: str, destination: Optional[str], port: Optional[int]) -> No
136136
degree=3, # Number of peers to maintain in mesh
137137
degree_low=2, # Lower bound for mesh peers
138138
degree_high=4, # Upper bound for mesh peers
139+
direct_peers=None, # Direct peers
139140
time_to_live=60, # TTL for message cache in seconds
140141
gossip_window=2, # Smaller window for faster gossip
141142
gossip_history=5, # Keep more history

libp2p/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@
4747
from libp2p.transport.upgrader import (
4848
TransportUpgrader,
4949
)
50+
from libp2p.utils.logging import (
51+
setup_logging,
52+
)
53+
54+
# Initialize logging configuration
55+
setup_logging()
5056

5157

5258
def generate_new_rsa_identity() -> KeyPair:

libp2p/peer/peerstore.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from collections.abc import (
55
Sequence,
66
)
7+
import sys
78
from typing import (
89
Any,
910
)
@@ -32,6 +33,8 @@
3233
PeerInfo,
3334
)
3435

36+
PERMANENT_ADDR_TTL = sys.maxsize
37+
3538

3639
class PeerStore(IPeerStore):
3740
peer_data_map: dict[ID, PeerData]

libp2p/pubsub/gossipsub.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
from libp2p.peer.id import (
3030
ID,
3131
)
32+
from libp2p.peer.peerinfo import (
33+
PeerInfo,
34+
)
35+
from libp2p.peer.peerstore import (
36+
PERMANENT_ADDR_TTL,
37+
)
3238
from libp2p.pubsub import (
3339
floodsub,
3440
)
@@ -82,17 +88,24 @@ class GossipSub(IPubsubRouter, Service):
8288
heartbeat_initial_delay: float
8389
heartbeat_interval: int
8490

91+
direct_peers: dict[ID, PeerInfo]
92+
direct_connect_initial_delay: float
93+
direct_connect_interval: int
94+
8595
def __init__(
8696
self,
8797
protocols: Sequence[TProtocol],
8898
degree: int,
8999
degree_low: int,
90100
degree_high: int,
101+
direct_peers: Sequence[PeerInfo] = None,
91102
time_to_live: int = 60,
92103
gossip_window: int = 3,
93104
gossip_history: int = 5,
94105
heartbeat_initial_delay: float = 0.1,
95106
heartbeat_interval: int = 120,
107+
direct_connect_initial_delay: float = 0.1,
108+
direct_connect_interval: int = 300,
96109
) -> None:
97110
self.protocols = list(protocols)
98111
self.pubsub = None
@@ -119,10 +132,19 @@ def __init__(
119132
self.heartbeat_initial_delay = heartbeat_initial_delay
120133
self.heartbeat_interval = heartbeat_interval
121134

135+
# Create direct peers
136+
self.direct_peers = dict()
137+
for direct_peer in direct_peers or []:
138+
self.direct_peers[direct_peer.peer_id] = direct_peer
139+
self.direct_connect_interval = direct_connect_interval
140+
self.direct_connect_initial_delay = direct_connect_initial_delay
141+
122142
async def run(self) -> None:
123143
if self.pubsub is None:
124144
raise NoPubsubAttached
125145
self.manager.run_daemon_task(self.heartbeat)
146+
if len(self.direct_peers) > 0:
147+
self.manager.run_daemon_task(self.direct_connect_heartbeat)
126148
await self.manager.wait_finished()
127149

128150
# Interface functions
@@ -142,6 +164,12 @@ def attach(self, pubsub: Pubsub) -> None:
142164
"""
143165
self.pubsub = pubsub
144166

167+
if len(self.direct_peers) > 0:
168+
for pi in self.direct_peers:
169+
self.pubsub.host.get_peerstore().add_addrs(
170+
pi, self.direct_peers[pi].addrs, PERMANENT_ADDR_TTL
171+
)
172+
145173
logger.debug("attached to pusub")
146174

147175
def add_peer(self, peer_id: ID, protocol_id: TProtocol) -> None:
@@ -241,6 +269,10 @@ def _get_peers_to_send(
241269
if topic not in self.pubsub.peer_topics:
242270
continue
243271

272+
# direct peers
273+
_direct_peers: set[ID] = {_peer for _peer in self.direct_peers}
274+
send_to.update(_direct_peers)
275+
244276
# floodsub peers
245277
floodsub_peers: set[ID] = {
246278
peer_id
@@ -425,6 +457,24 @@ async def heartbeat(self) -> None:
425457

426458
await trio.sleep(self.heartbeat_interval)
427459

460+
async def direct_connect_heartbeat(self) -> None:
461+
"""
462+
Connect to direct peers.
463+
"""
464+
await trio.sleep(self.direct_connect_initial_delay)
465+
while True:
466+
for direct_peer in self.direct_peers:
467+
if direct_peer not in self.pubsub.peers:
468+
try:
469+
await self.pubsub.host.connect(self.direct_peers[direct_peer])
470+
except Exception as e:
471+
logger.debug(
472+
"failed to connect to a direct peer %s: %s",
473+
direct_peer,
474+
e,
475+
)
476+
await trio.sleep(self.direct_connect_interval)
477+
428478
def mesh_heartbeat(
429479
self,
430480
) -> tuple[DefaultDict[ID, list[str]], DefaultDict[ID, list[str]]]:
@@ -654,6 +704,14 @@ async def handle_graft(
654704

655705
# Add peer to mesh for topic
656706
if topic in self.mesh:
707+
for direct_peer in self.direct_peers:
708+
if direct_peer == sender_peer_id:
709+
logger.warning(
710+
"GRAFT: ignoring request from direct peer %s", sender_peer_id
711+
)
712+
await self.emit_prune(topic, sender_peer_id)
713+
return
714+
657715
if sender_peer_id not in self.mesh[topic]:
658716
self.mesh[topic].add(sender_peer_id)
659717
else:

libp2p/tools/constants.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1+
from collections.abc import (
2+
Sequence,
3+
)
14
from typing import (
25
NamedTuple,
36
)
47

58
import multiaddr
69

10+
from libp2p.peer.peerinfo import (
11+
PeerInfo,
12+
)
713
from libp2p.pubsub import (
814
floodsub,
915
gossipsub,
@@ -26,11 +32,14 @@ class GossipsubParams(NamedTuple):
2632
degree: int = 10
2733
degree_low: int = 9
2834
degree_high: int = 11
35+
direct_peers: Sequence[PeerInfo] = None
2936
time_to_live: int = 30
3037
gossip_window: int = 3
3138
gossip_history: int = 5
3239
heartbeat_initial_delay: float = 0.1
3340
heartbeat_interval: float = 0.5
41+
direct_connect_initial_delay: float = 0.1
42+
direct_connect_interval: int = 300
3443

3544

3645
GOSSIPSUB_PARAMS = GossipsubParams()

0 commit comments

Comments
 (0)