-
Notifications
You must be signed in to change notification settings - Fork 186
Enhancement: Fix Kademlia DHT Bootstrap by Using Connected Peers as Fallback #943
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
SuchitraSwain
wants to merge
18
commits into
libp2p:main
Choose a base branch
from
SuchitraSwain:905-kademlia-dht
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
bde19ca
added flood publishing
mystical-prog fa99d32
Merge branch 'main' into flood-publishing
seetadev 75a3749
added tests for flood publising
Khwahish29 4780904
fix lint
Khwahish29 9ddc245
Merge branch 'main' into flood-publishing
Khwahish29 ed67340
resolved merge conflicts
Khwahish29 ca8d494
Merge branch 'main' into flood-publishing
seetadev 9f6b409
Merge branch 'main' into flood-publishing
seetadev 1f70934
Merge branch 'main' into flood-publishing
seetadev 2a3742b
Merge branch 'main' into flood-publishing
seetadev 888cc6e
Merge branch 'main' into flood-publishing
seetadev 70252b1
[905]: Enhancement: Fix Kademila DHT
405d377
Merge branch 'main' into 905-kademlia-dht
SuchitraSwain 35562a8
Merge branch 'main' into 905-kademlia-dht
seetadev f6bdf36
Merge branch 'main' into 905-kademlia-dht
seetadev 572746e
Merge branch 'main' into 905-kademlia-dht
SuchitraSwain 6415caf
Merge branch 'main' into 905-kademlia-dht
SuchitraSwain 579a681
Merge branch 'main' into 905-kademlia-dht
SuchitraSwain File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
#!/usr/bin/env python3 | ||
""" | ||
Basic FloodSub Example | ||
|
||
This is a simple example that demonstrates FloodSub publishing and subscribing | ||
without relying on test utilities. It shows the core functionality. | ||
|
||
Run this example with: | ||
python examples/floodsub/basic_example.py | ||
""" | ||
|
||
import asyncio | ||
import logging | ||
import sys | ||
|
||
import trio | ||
|
||
from libp2p import new_host | ||
from libp2p.crypto.secp256k1 import create_new_key_pair | ||
from libp2p.pubsub.floodsub import FloodSub | ||
from libp2p.pubsub.pubsub import Pubsub | ||
from libp2p.tools.async_service import background_trio_service | ||
from libp2p.tools.constants import FLOODSUB_PROTOCOL_ID | ||
|
||
# Configure logging | ||
logging.basicConfig(level=logging.INFO) | ||
logger = logging.getLogger("floodsub_basic") | ||
|
||
|
||
async def main() -> None: | ||
"""Main function demonstrating basic FloodSub functionality.""" | ||
logger.info("Starting basic FloodSub example...") | ||
|
||
# Create two hosts | ||
key_pair1 = create_new_key_pair() | ||
key_pair2 = create_new_key_pair() | ||
|
||
host1 = new_host( | ||
key_pair=key_pair1, | ||
listen_addrs=["/ip4/127.0.0.1/tcp/0"], | ||
) | ||
|
||
host2 = new_host( | ||
key_pair=key_pair2, | ||
listen_addrs=["/ip4/127.0.0.1/tcp/0"], | ||
) | ||
|
||
# Create FloodSub routers | ||
floodsub1 = FloodSub(protocols=[FLOODSUB_PROTOCOL_ID]) | ||
floodsub2 = FloodSub(protocols=[FLOODSUB_PROTOCOL_ID]) | ||
|
||
# Create Pubsub instances | ||
pubsub1 = Pubsub( | ||
host=host1, | ||
router=floodsub1, | ||
strict_signing=False, # Disable for simplicity | ||
) | ||
|
||
pubsub2 = Pubsub( | ||
host=host2, | ||
router=floodsub2, | ||
strict_signing=False, # Disable for simplicity | ||
) | ||
|
||
# Start both pubsub services | ||
async with background_trio_service(pubsub1): | ||
async with background_trio_service(pubsub2): | ||
await pubsub1.wait_until_ready() | ||
await pubsub2.wait_until_ready() | ||
|
||
logger.info(f"Host 1 ID: {host1.get_id()}") | ||
logger.info(f"Host 2 ID: {host2.get_id()}") | ||
|
||
# Start listening on both hosts | ||
logger.info("Starting hosts...") | ||
await host1.get_network().listen() | ||
await host2.get_network().listen() | ||
await trio.sleep(0.5) # Wait for hosts to start listening | ||
|
||
# Connect the hosts | ||
logger.info("Connecting hosts...") | ||
await host1.connect(host2.get_id(), host2.get_addrs()) | ||
await trio.sleep(1) # Wait for connection | ||
|
||
# Subscribe to topic on host2 | ||
topic = "test-topic" | ||
logger.info(f"Subscribing to topic: {topic}") | ||
subscription = await pubsub2.subscribe(topic) | ||
await trio.sleep(0.5) # Wait for subscription to propagate | ||
|
||
# Publish messages from host1 | ||
messages = [ | ||
"Hello from FloodSub!", | ||
"This is message number 2", | ||
"FloodSub is working great!" | ||
] | ||
|
||
for i, message in enumerate(messages): | ||
logger.info(f"Publishing message {i+1}: {message}") | ||
await pubsub1.publish(topic, message.encode()) | ||
await trio.sleep(0.5) | ||
|
||
# Receive messages on host2 | ||
logger.info("Receiving messages...") | ||
for i in range(len(messages)): | ||
message = await subscription.get() | ||
logger.info(f"Received message {i+1}: {message.data.decode()}") | ||
logger.info(f" From peer: {message.from_id.hex()}") | ||
logger.info(f" Topics: {message.topicIDs}") | ||
|
||
logger.info("Basic FloodSub example completed successfully!") | ||
|
||
|
||
if __name__ == "__main__": | ||
try: | ||
trio.run(main) | ||
except KeyboardInterrupt: | ||
logger.info("Example interrupted by user") | ||
sys.exit(0) | ||
except Exception as e: | ||
logger.error(f"Example failed: {e}") | ||
sys.exit(1) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -278,6 +278,20 @@ async def handle_stream(self, stream: INetStream) -> None: | |
closest_peers = self.routing_table.find_local_closest_peers( | ||
target_key, 20 | ||
) | ||
|
||
# Fallback to connected peers if routing table has insufficient peers | ||
MIN_PEERS_THRESHOLD = 5 # Configurable minimum | ||
if len(closest_peers) < MIN_PEERS_THRESHOLD: | ||
logger.debug("Routing table has insufficient peers (%d < %d) for FIND_NODE in KadDHT, using connected peers as fallback", | ||
len(closest_peers), MIN_PEERS_THRESHOLD) | ||
connected_peers = self.host.get_connected_peers() | ||
if connected_peers: | ||
# Sort connected peers by distance to target and use as response | ||
from .utils import sort_peer_ids_by_distance | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. import statements should be placed at top. |
||
fallback_peers = sort_peer_ids_by_distance(target_key, connected_peers)[:20] | ||
closest_peers = fallback_peers | ||
logger.debug("Using %d connected peers as fallback for FIND_NODE in KadDHT", len(closest_peers)) | ||
|
||
logger.debug(f"Found {len(closest_peers)} peers close to target") | ||
|
||
# Consume the source signed_peer_record if sent | ||
|
@@ -459,6 +473,20 @@ async def handle_stream(self, stream: INetStream) -> None: | |
closest_peers = self.routing_table.find_local_closest_peers( | ||
key, 20 | ||
) | ||
|
||
# Fallback to connected peers if routing table has insufficient peers | ||
MIN_PEERS_THRESHOLD = 5 # Configurable minimum | ||
if len(closest_peers) < MIN_PEERS_THRESHOLD: | ||
logger.debug("Routing table has insufficient peers (%d < %d) for provider response, using connected peers as fallback", | ||
len(closest_peers), MIN_PEERS_THRESHOLD) | ||
connected_peers = self.host.get_connected_peers() | ||
if connected_peers: | ||
# Sort connected peers by distance to target and use as response | ||
from .utils import sort_peer_ids_by_distance | ||
fallback_peers = sort_peer_ids_by_distance(key, connected_peers)[:20] | ||
closest_peers = fallback_peers | ||
logger.debug("Using %d connected peers as fallback for provider response", len(closest_peers)) | ||
|
||
logger.debug( | ||
f"No providers found, including {len(closest_peers)}" | ||
"closest peers" | ||
|
@@ -550,6 +578,20 @@ async def handle_stream(self, stream: INetStream) -> None: | |
closest_peers = self.routing_table.find_local_closest_peers( | ||
key, 20 | ||
) | ||
|
||
# Fallback to connected peers if routing table has insufficient peers | ||
MIN_PEERS_THRESHOLD = 5 # Configurable minimum | ||
if len(closest_peers) < MIN_PEERS_THRESHOLD: | ||
logger.debug("Routing table has insufficient peers (%d < %d) for GET_VALUE response, using connected peers as fallback", | ||
len(closest_peers), MIN_PEERS_THRESHOLD) | ||
connected_peers = self.host.get_connected_peers() | ||
if connected_peers: | ||
# Sort connected peers by distance to target and use as response | ||
from .utils import sort_peer_ids_by_distance | ||
fallback_peers = sort_peer_ids_by_distance(key, connected_peers)[:20] | ||
closest_peers = fallback_peers | ||
logger.debug("Using %d connected peers as fallback for GET_VALUE response", len(closest_peers)) | ||
|
||
logger.debug( | ||
"No value found," | ||
f"including {len(closest_peers)} closest peers" | ||
|
@@ -677,9 +719,24 @@ async def put_value(self, key: bytes, value: bytes) -> None: | |
) | ||
|
||
# 2. Get closest peers, excluding self | ||
routing_table_peers = self.routing_table.find_local_closest_peers(key) | ||
|
||
# Fallback to connected peers if routing table has insufficient peers | ||
MIN_PEERS_THRESHOLD = 5 # Configurable minimum | ||
if len(routing_table_peers) < MIN_PEERS_THRESHOLD: | ||
logger.debug("Routing table has insufficient peers (%d < %d) for put_value, using connected peers as fallback", | ||
len(routing_table_peers), MIN_PEERS_THRESHOLD) | ||
connected_peers = self.host.get_connected_peers() | ||
if connected_peers: | ||
# Sort connected peers by distance to target and use as fallback | ||
from .utils import sort_peer_ids_by_distance | ||
fallback_peers = sort_peer_ids_by_distance(key, connected_peers) | ||
routing_table_peers = fallback_peers | ||
logger.debug("Using %d connected peers as fallback for put_value", len(routing_table_peers)) | ||
|
||
closest_peers = [ | ||
peer | ||
for peer in self.routing_table.find_local_closest_peers(key) | ||
for peer in routing_table_peers | ||
if peer != self.local_peer_id | ||
] | ||
logger.debug(f"Found {len(closest_peers)} peers to store value at") | ||
|
@@ -722,9 +779,24 @@ async def get_value(self, key: bytes) -> bytes | None: | |
return value | ||
|
||
# 2. Get closest peers, excluding self | ||
routing_table_peers = self.routing_table.find_local_closest_peers(key) | ||
|
||
# Fallback to connected peers if routing table has insufficient peers | ||
MIN_PEERS_THRESHOLD = 5 # Configurable minimum | ||
if len(routing_table_peers) < MIN_PEERS_THRESHOLD: | ||
logger.debug("Routing table has insufficient peers (%d < %d) for get_value, using connected peers as fallback", | ||
len(routing_table_peers), MIN_PEERS_THRESHOLD) | ||
connected_peers = self.host.get_connected_peers() | ||
if connected_peers: | ||
# Sort connected peers by distance to target and use as fallback | ||
from .utils import sort_peer_ids_by_distance | ||
fallback_peers = sort_peer_ids_by_distance(key, connected_peers) | ||
routing_table_peers = fallback_peers | ||
logger.debug("Using %d connected peers as fallback for get_value", len(routing_table_peers)) | ||
|
||
closest_peers = [ | ||
peer | ||
for peer in self.routing_table.find_local_closest_peers(key) | ||
for peer in routing_table_peers | ||
if peer != self.local_peer_id | ||
] | ||
logger.debug(f"Searching {len(closest_peers)} peers for value") | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Added flood publishing. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Constants should be placed at top.