Running an Experiment with IPv8 OverlaysΒΆ

In the previous tutorials, we have discussed how to run both experiments on a single computer and on the DAS5 cluster. We now show how to combine Gumby experiments with IPv8 overlays and show how we can use Gumby to quickly test decentralized overlays. We assume that the reader is familiar with IPv8 overlays. Otherwise, we refer to the IPv8 documentation for more information.

In the following experiment, we spawn two instances and each instance joins a simple IPv8 community. After five seconds, instance 1 sends a ping message to instance 2 and instance 2 responds with a pong message. Our configuration file looks as follows:

experiment_name = simple_ipv8
experiment_time = 30
instances_to_run = 2
local_instance_cmd = "process_guard.py -c launch_scenario.py -n $INSTANCES_TO_RUN -t $EXPERIMENT_TIME -m $OUTPUT_DIR -o $OUTPUT_DIR "
post_process_cmd = graph_process_guard_data.sh
scenario_file = simple_ipv8.scenario
sync_host = localhost
sync_port = __unique_port__

All these configuration options have been discussed in previous tutorials. Our scenario file looks as follows:

&module gumby.modules.base_ipv8_module.BaseIPv8Module
&module docs.tutorials.ping_pong_module.PingPongModule

@0:0 isolate_ipv8_overlay PingPongCommunity
@0:1 start_session
@0:1 annotate start-experiment
@0:5 introduce_peers
@0:10 send_ping {1}
@0:15 stop_session
@0:20 stop

There are several things going on here. First, we call the isolate_ipv8_overlay method, which ensures that the overlay loaded in our experiments do not communicate with peers external to our experiments. After 1 second, we call the start_session method that starts the IPv8 service. After 5 seconds, we introduce the peers to each other so peers know about each other and can send messages. After 10 seconds, we call the send_ping message (defined in the PingPongModule class) which sends a ping message to all known peers. We then stop the IPv8 service after 15 seconds and stop the entire experiment after 20 seconds.

Note that the scenario file imports the ping_pong_module.py file. The content of this file is as follows:

from binascii import unhexlify

from ipv8.community import Community
from ipv8.lazy_community import lazy_wrapper
from ipv8.loader import overlay
from ipv8.messaging.lazy_payload import VariablePayload, vp_compile
from ipv8.types import Peer

from gumby.experiment import experiment_callback
from gumby.modules.community_experiment_module import IPv8OverlayExperimentModule
from gumby.modules.ipv8_community_launchers import IPv8CommunityLauncher


# ---- messages ---- #
@vp_compile
class PingPayload(VariablePayload):
    msg_id = 1


@vp_compile
class PongPayload(VariablePayload):
    msg_id = 2


# ---- community ---- #
class PingPongCommunity(Community):
    community_id = unhexlify("d37c847b628e1414cffb6a4626b7fa0999fba888")

    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.add_message_handler(PingPayload, self.received_ping)
        self.add_message_handler(PongPayload, self.received_pong)

    def send_ping(self):
        for peer in self.network.verified_peers:
            self.logger.info("Sending ping to peer %s", peer)
            self.ez_send(peer, PingPayload())

    @lazy_wrapper(PingPayload)
    def received_ping(self, peer: Peer, _: PingPayload):
        self.logger.info("Received ping from peer %s", peer)
        self.ez_send(peer, PongPayload())

    @lazy_wrapper(PongPayload)
    def received_pong(self, peer: Peer, _: PongPayload):
        self.logger.info("Received pong from peer %s", peer)


# ---- launcher ---- #
@overlay(PingPongCommunity)
class PingPongCommunityLauncher(IPv8CommunityLauncher):
    pass


# ---- module ---- #
class PingPongModule(IPv8OverlayExperimentModule):
    """
    This module contains code to manage experiments with the Basalt community.
    """
    def __init__(self, experiment):
        super().__init__(experiment, PingPongCommunity)

    def on_id_received(self):
        super().on_id_received()
        self.ipv8_provider.custom_ipv8_community_loader.set_launcher(PingPongCommunityLauncher())

    @experiment_callback
    def send_ping(self):
        self.overlay.send_ping()

This file implements the following:

  • We define two IPv8 payloads, namely PingPayload and PongPayload.

  • We define the PingPongCommunity class which contains some logic when receiving ping and pong messages.

  • We implement a PingPongCommunityLauncher which is used by Gumby to correctly load the community on experiment startup.

  • Finally, we implement the PingPongModule which contains a single experiment callback.

You can run the experiment with the following command:

$ IPV8_DIR=<PATH TO IPV8> gumby/run.py gumby/docs/tutorials/simple_ipv8.conf

Note the IPV8_DIR environment variables which tells Gumby where to find the IPv8 source code. You should change this to point to your IPv8 installation. The experiment ends after around 20 seconds. Inspecting the instance output should reveal log lines like:

2021-07-18 12:52:06,699:INFO:Sending ping to peer Peer<127.0.0.1:12002, s4WWx6gchewSqE27LpP+xBt8QsE=>
2021-07-18 12:52:06,700:INFO:Received pong from peer Peer<127.0.0.1:12002, s4WWx6gchewSqE27LpP+xBt8QsE=>

This shows that the peers have successfully communicated with each other using IPv8 and Gumby.