diff --git a/src/tribler-core/tribler_core/modules/popularity/popularity_community.py b/src/tribler-core/tribler_core/modules/popularity/popularity_community.py index 80e2ef2f8bb..38a417b6949 100644 --- a/src/tribler-core/tribler_core/modules/popularity/popularity_community.py +++ b/src/tribler-core/tribler_core/modules/popularity/popularity_community.py @@ -9,10 +9,11 @@ from tribler_core.modules.metadata_store.community.remote_query_community import RemoteQueryCommunity from tribler_core.modules.popularity.payload import TorrentsHealthPayload +from tribler_core.modules.popularity.version_community_mixin import VersionCommunityMixin from tribler_core.utilities.unicode import hexlify -class PopularityCommunity(RemoteQueryCommunity): +class PopularityCommunity(RemoteQueryCommunity, VersionCommunityMixin): """ Community for disseminating the content across the network. @@ -44,6 +45,9 @@ def __init__(self, my_peer, endpoint, network, **kwargs): self.register_task("gossip_random_torrents", self.gossip_random_torrents_health, interval=PopularityCommunity.GOSSIP_INTERVAL_FOR_RANDOM_TORRENTS) + # Init version community message handlers + self.init_version_community() + @staticmethod def select_torrents_to_gossip(torrents, include_popular=True, include_random=True) -> (set, set): """ Select torrents to gossip. diff --git a/src/tribler-core/tribler_core/modules/popularity/test_version_community_mixin.py b/src/tribler-core/tribler_core/modules/popularity/test_version_community_mixin.py new file mode 100644 index 00000000000..706bf073229 --- /dev/null +++ b/src/tribler-core/tribler_core/modules/popularity/test_version_community_mixin.py @@ -0,0 +1,64 @@ +import os +import sys +from asyncio import Future + +from ipv8.community import Community +from ipv8.messaging.serialization import default_serializer +from ipv8.test.base import TestBase +from ipv8.test.mocking.ipv8 import MockIPv8 +from tribler_core.modules.popularity.version_community_mixin import VersionResponse, VersionCommunityMixin +from tribler_core.version import version_id + + +class VersionCommunity(VersionCommunityMixin, Community): + community_id = os.urandom(20) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.init_version_community() + + +class TestVersionCommunity(TestBase): + NUM_NODES = 2 + + def setUp(self): + super().setUp() + self.initialize(VersionCommunity, self.NUM_NODES) + + def create_node(self, *args, **kwargs): + return MockIPv8("curve25519", VersionCommunity) + + def test_version_response_payload(self): + """ + Check if the version response is correctly serialized. + """ + version = "v7.10.0" + platform = "linux" + + version_response = VersionResponse(version, platform) + serialized = default_serializer.pack_serializable(version_response) + deserialized, _ = default_serializer.unpack_serializable(VersionResponse, serialized) + + self.assertEqual(version_response.version, version) + self.assertEqual(version_response.platform, platform) + self.assertEqual(deserialized.version, version) + self.assertEqual(deserialized.platform, platform) + + async def test_request_for_version(self): + """ + Test whether version request is responded well. + """ + await self.introduce_nodes() + + on_process_version_response_called = Future() + + def on_process_version_response(peer, version, platform): + self.assertEqual(peer, self.peer(1)) + self.assertEqual(version, version_id) + self.assertEqual(platform, sys.platform) + on_process_version_response_called.set_result(True) + + self.overlay(0).process_version_response = on_process_version_response + self.overlay(0).send_version_request(self.peer(1)) + + return await on_process_version_response_called diff --git a/src/tribler-core/tribler_core/modules/popularity/version_community_mixin.py b/src/tribler-core/tribler_core/modules/popularity/version_community_mixin.py new file mode 100644 index 00000000000..80e33a8be51 --- /dev/null +++ b/src/tribler-core/tribler_core/modules/popularity/version_community_mixin.py @@ -0,0 +1,67 @@ +import sys + +from ipv8.lazy_community import lazy_wrapper +from ipv8.messaging.lazy_payload import VariablePayload, vp_compile +from tribler_core.version import version_id + + +@vp_compile +class VersionRequest(VariablePayload): + msg_id = 101 + + +@vp_compile +class VersionResponse(VariablePayload): + msg_id = 102 + format_list = ['varlenI', 'varlenI'] + names = ['version', 'platform'] + + def fix_pack_version(self, value): + return value.encode('utf-8') + + def fix_pack_platform(self, value): + return value.encode('utf-8') + + @classmethod + def fix_unpack_version(cls, value): + return value.decode('utf-8') + + @classmethod + def fix_unpack_platform(cls, value): + return value.decode('utf-8') + + +class VersionCommunityMixin: + """ + This mixin add the protocol messages to ask and receive version of Tribler and community the + peer is currently running. + + Knowing the version of Tribler or the individual community is not critical for normal operation + of Tribler but is useful in doing network experiments and monitoring of the network behavior + because of a new feature/algorithm deployment. + """ + + def init_version_community(self): + self.add_message_handler(VersionRequest, self.on_version_request) + self.add_message_handler(VersionResponse, self.on_version_response) + + def send_version_request(self, peer): + self.logger.info(f"Sending version request to {peer.address}") + self.ez_send(peer, VersionRequest()) + + @lazy_wrapper(VersionRequest) + async def on_version_request(self, peer, _): + self.logger.info(f"Received version request from {peer.address}") + version_response = VersionResponse(version_id, sys.platform) + self.ez_send(peer, version_response) + + @lazy_wrapper(VersionResponse) + async def on_version_response(self, peer, payload): + self.logger.info(f"Received version response from {peer.address}") + self.process_version_response(peer, payload.version, payload.platform) + + def process_version_response(self, peer, version, platform): + """ + This is the method the implementation community or the experiment will implement + to process the version and platform information. + """