From 0dbf21f9367018b86f9a536ebc292823106db895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Leszczy=C5=84ski?= Date: Wed, 30 Oct 2024 17:13:51 +0100 Subject: [PATCH 01/32] feat: implement enabling shield --- bt_ddos_shield/address.py | 2 +- bt_ddos_shield/event_processor.py | 20 ++- bt_ddos_shield/manifest_manager.py | 2 +- bt_ddos_shield/miner_shield.py | 226 +++++++++++++++++++++++++-- bt_ddos_shield/state_manager.py | 25 +-- bt_ddos_shield/utils.py | 1 + bt_ddos_shield/validators_manager.py | 2 +- tests/test_miner_shield.py | 23 +++ 8 files changed, 271 insertions(+), 30 deletions(-) create mode 100644 tests/test_miner_shield.py diff --git a/bt_ddos_shield/address.py b/bt_ddos_shield/address.py index 0209b53..4ebb9af 100644 --- a/bt_ddos_shield/address.py +++ b/bt_ddos_shield/address.py @@ -41,7 +41,7 @@ def encrypt(self) -> bytes: @classmethod @abstractmethod - def decrypt(cls, encrypted_data: bytes) -> Address: + def decrypt(cls, encrypted_data: bytes): """ Create address from encrypted address data. diff --git a/bt_ddos_shield/event_processor.py b/bt_ddos_shield/event_processor.py index b398942..15294c3 100644 --- a/bt_ddos_shield/event_processor.py +++ b/bt_ddos_shield/event_processor.py @@ -1,9 +1,10 @@ from abc import ABC, abstractmethod from dataclasses import dataclass +import traceback @dataclass -class Event: +class MinerShieldEvent: """ Class describing event, which happened in the shield. """ @@ -12,13 +13,13 @@ class Event: exception: Exception = None # Exception which caused the event. -class AbstractEventProcessor(ABC): +class AbstractMinerShieldEventProcessor(ABC): """ Abstract base class for processor handling events generated by shield. """ @abstractmethod - def add_event(self, event: Event): + def add_event(self, event: MinerShieldEvent): """ Add new event to be handled by processor. @@ -26,3 +27,16 @@ def add_event(self, event: Event): event: Event to add. """ pass + + +class LoggingMinerShieldEventProcessor(AbstractMinerShieldEventProcessor): + """ + Event processor which logs events to console. + """ + + def add_event(self, event: MinerShieldEvent): + if event.exception is not None: + print(f"MinerShieldEvent: {event.event_description}\nException happened:") + print(traceback.format_exc()) + else: + print(f"MinerShieldEvent: {event.event_description}") diff --git a/bt_ddos_shield/manifest_manager.py b/bt_ddos_shield/manifest_manager.py index 4960535..e836cc2 100644 --- a/bt_ddos_shield/manifest_manager.py +++ b/bt_ddos_shield/manifest_manager.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from bt_ddos_shield.address import Address -from bt_ddos_shield.miner_shield import Hotkey +from bt_ddos_shield.utils import Hotkey class AbstractManifestManager(ABC): diff --git a/bt_ddos_shield/miner_shield.py b/bt_ddos_shield/miner_shield.py index 5c36f7f..072f5a2 100644 --- a/bt_ddos_shield/miner_shield.py +++ b/bt_ddos_shield/miner_shield.py @@ -1,20 +1,21 @@ +import threading +from queue import Queue from dataclasses import dataclass +from time import sleep from bt_ddos_shield.blockchain_manager import AbstractBlockchainManager -from bt_ddos_shield.event_processor import AbstractEventProcessor +from bt_ddos_shield.event_processor import AbstractMinerShieldEventProcessor, MinerShieldEvent from bt_ddos_shield.address_manager import AbstractAddressManager +from bt_ddos_shield.utils import Hotkey from bt_ddos_shield.validators_manager import AbstractValidatorsManager from bt_ddos_shield.manifest_manager import AbstractManifestManager from bt_ddos_shield.state_manager import AbstractMinerShieldStateManager -Hotkey = str # type of Hotkey - - @dataclass class MinerShieldOptions: """ - A class to represent the configuration options for the MinerShield. + A class to represent the configuration options for the MinerShield. """ auto_hide_original_server: bool = False # If True, the original server will be hidden after some time after shield @@ -23,15 +24,19 @@ class MinerShieldOptions: auto_hide_delay_sec: int = 600 # Time in seconds after which the original server will be hidden if # auto_hide_original_server is set to True. + retry_delay: int = 5 # Time in seconds to wait before retrying failed task. + class MinerShield: """ - Main class to be used by Miner to shield himself from DDoS. Call enable() to start the shield. + Main class to be used by Miner to shield himself from DDoS. Call enable() to start the shield. No methods in + managers should be called directly. All operations are done by worker thread. After starting shield user can + schedule tasks to be executed asynchronously. """ def __init__(self, validators_manager: AbstractValidatorsManager, address_manager: AbstractAddressManager, manifest_manager: AbstractManifestManager, blockchain_manager: AbstractBlockchainManager, - state_manager: AbstractMinerShieldStateManager, event_processor: AbstractEventProcessor, + state_manager: AbstractMinerShieldStateManager, event_processor: AbstractMinerShieldEventProcessor, options: MinerShieldOptions): """ Initialize the MinerShield class. @@ -45,30 +50,221 @@ def __init__(self, validators_manager: AbstractValidatorsManager, address_manage event_processor: Instance of AbstractEventProcessor to handle events generated by the shield. options: Instance of MinerShieldOptions. """ - pass + self.validators_manager = validators_manager + self.address_manager = address_manager + self.manifest_manager = manifest_manager + self.blockchain_manager = blockchain_manager + self.state_manager = state_manager + self.event_processor = event_processor + self.options = options + + self.worker_thread = None + self.task_queue = Queue() + self.run = False + self.finishing = False def enable(self): """ - Enable shield. It asynchronously starts the shield, which consists of such steps: + Enable shield. It starts worker thread, which will do such steps if run for the first time: 1. Fetch validators keys. 2. Creates addresses for all validators. 3. Save manifest file. 4. Publish link to manifest file to blockchain. 5. Eventually close public access to original IP after some time. - It puts events to event_manager after each step. Current state is managed by state_manager. If shielding - process had been interrupted it is continued from the last step. + It puts events to event_manager after each finished operation. Current state is managed by state_manager. + If any error occurs it is retried forever until shield is disabled. - When shield is running, changing validators set triggers shield reconfiguration. + When shield is running, user can schedule tasks to be processed by worker. """ - pass + if self.worker_thread is not None: + raise Exception("Shield is already enabled") + + self.finishing = False + self.run = True + self._add_task(MinerShieldInitializeTask()) + self.worker_thread = threading.Thread(target=self._worker_function) + self.worker_thread.start() + + def disable(self): + """ + Disable shield. It stops worker thread after finishing current task. Function blocks until worker is stopped. + """ + self._add_task(MinerShieldDisableTask()) + self.finishing = True + self.worker_thread.join() + self.worker_thread = None + self.task_queue = Queue() # clear task queue def ban_validator(self, validator_hotkey: Hotkey): """ - Ban a validator by its hotkey. Function blocks execution until manifest file is updated and info about file - is published to Bittensor. + Ban a validator by its hotkey. Task will be executed by worker. It will update manifest file and publish info + about new file version to blockchain. Args: validator_hotkey: The hotkey of the validator. """ + self._add_task(MinerShieldBanValidatorTask(validator_hotkey)) + pass + + def _add_task(self, task): + """ + Add task to task queue. It will be handled by _worker_function. + """ + if not isinstance(task, MinerShieldTask): + raise Exception("Task is not instance of MinerShieldTask") + if not self.run: + raise Exception("Shield is disabled") + + self.task_queue.put(task) + + def _worker_function(self): + """ + Function called in separate thread by enable() to start the shield. It is handling events put to task_queue. + """ + + self.event_processor.add_event(MinerShieldEvent(f"Starting shield")) + + while self.run: + task = self.task_queue.get() + try_count = 1 + + while self.run: + self.event_processor.add_event(MinerShieldEvent(f"Handling task {task}, try {try_count}")) + + try: + task.handle(self) + self.event_processor.add_event(MinerShieldEvent(f"Task {task} finished successfully")) + break + except Exception as e: + self.event_processor.add_event(MinerShieldEvent(f"Error during handling task {task}", e)) + + if self.finishing: + break + + try_count += 1 + sleep(self.options.retry_delay) + + self.event_processor.add_event(MinerShieldEvent(f"Stopping shield")) + + def _handle_initialize(self): + """ + Initialize shield. Load state and initial validators set. + """ + self.state_manager.get_state() + self.event_processor.add_event(MinerShieldEvent("State loaded")) + + self.validators_manager.refresh_validators() + validators: dict[Hotkey, str] = self.validators_manager.get_validators() + self.event_processor.add_event(MinerShieldEvent(f"Validators refreshed, got {len(validators)} validators")) + + self._add_task(MinerShieldValidatorsChangedTask()) + + def _handle_disable(self): + self.run = False + + def _handle_validators_changed(self): + """ + Calculates difference between newly fetched validators set and one saved in state and run logic for any changes. + """ + + # get current state and recently fetched validators + current_state = self.state_manager.get_state() + fetched_validators: dict[Hotkey, str] = self.validators_manager.get_validators() + + # remove banned validators from fetched validators + for banned_validator in current_state.banned_validators.keys(): + fetched_validators.pop(banned_validator, None) + + # calculate difference between current state and fetched validators + deprecated_validators = current_state.known_validators.keys() - fetched_validators.keys() + new_validators = fetched_validators.keys() - current_state.known_validators.keys() + changed_validators = { + k: fetched_validators[k] for k in fetched_validators.keys() & current_state.known_validators.keys() + if fetched_validators[k] != current_state.known_validators[k] + } + + # handle changes in validators + + self.event_processor.add_event(MinerShieldEvent( + f"Handling validators change, deprecated_validators count={len(deprecated_validators)}" + f", new_validators count={len(new_validators)}, changed_validators count={len(changed_validators)}") + ) + + for validator in deprecated_validators: + self.event_processor.add_event(MinerShieldEvent(f"Removing validator {validator}")) + + if validator in current_state.active_addresses: + self.address_manager.remove_address(current_state.active_addresses[validator]) + + self.state_manager.remove_validator(validator) + + # TODO handle new_validators and changed_validators + + if deprecated_validators or new_validators or changed_validators: + # if anything changed update manifest file and publish new version to blockchain + # TODO also check state of shield if manifest was published at all + pass + + def _handle_ban_validator(self, validator_hotkey: Hotkey): + """ + Ban validator by its hotkey. It will update manifest file and publish info about new file version to blockchain. + """ + # TODO pass + + +class MinerShieldTask: + """ + Task to be executed by shield worker. + """ + + def __init__(self, task_name: str): + """ + Initialize task. + + Args: + task_name: Short name of the task. + """ + self.task_name = task_name + + def handle(self, miner_shield: MinerShield): + """ + Run task logic. + + Args + miner_shield: Instance of MinerShield in which task is executed. + """ + pass + + def __repr__(self): + return self.task_name + +class MinerShieldInitializeTask(MinerShieldTask): + def __init__(self): + super().__init__("INITIALIZE_SHIELD") + + def handle(self, miner_shield: MinerShield): + miner_shield._handle_initialize() + +class MinerShieldDisableTask(MinerShieldTask): + def __init__(self): + super().__init__("DISABLE_SHIELD") + + def handle(self, miner_shield: MinerShield): + miner_shield._handle_disable() + +class MinerShieldValidatorsChangedTask(MinerShieldTask): + def __init__(self): + super().__init__("VALIDATORS_CHANGED") + + def handle(self, miner_shield: MinerShield): + miner_shield._handle_validators_changed() + +class MinerShieldBanValidatorTask(MinerShieldTask): + def __init__(self, validator_hotkey: Hotkey): + super().__init__("BAN_VALIDATOR") + self.validator_hotkey = validator_hotkey + + def handle(self, miner_shield: MinerShield): + miner_shield._handle_ban_validator(self.validator_hotkey) diff --git a/bt_ddos_shield/state_manager.py b/bt_ddos_shield/state_manager.py index c313946..c3e2cb5 100644 --- a/bt_ddos_shield/state_manager.py +++ b/bt_ddos_shield/state_manager.py @@ -3,7 +3,7 @@ from enum import Enum from bt_ddos_shield.address import Address -from bt_ddos_shield.miner_shield import Hotkey +from bt_ddos_shield.utils import Hotkey class MinerShieldPhase(Enum): @@ -22,29 +22,32 @@ class MinerShieldState: """ phase: MinerShieldPhase # current phase of the shield + known_validators: dict[Hotkey, str] # known validators (HotKey -> validator public key) banned_validators: dict[Hotkey, datetime] # banned validators with ban time (HotKey -> time of ban) active_addresses: dict[Hotkey, Address] # active addresses (validator HotKey -> Address created for him) def __init__(self): self.phase = MinerShieldPhase.DISABLED + self.known_validators = {} self.banned_validators = {} self.active_addresses = {} class AbstractMinerShieldStateManager(ABC): """ - Abstract base class for manager handling state of MinerShield. + Abstract base class for manager handling state of MinerShield. Each change in state should be instantly saved to storage. """ current_miner_shield_state: MinerShieldState - @abstractmethod - def load_state(self): - pass + def get_state(self): + """ + Get current state of MinerShield. If state is not loaded, it is loaded first. + """ + if self.current_miner_shield_state is None: + self.current_miner_shield_state = self._load_state() - @abstractmethod - def save_state(self): - pass + return self.current_miner_shield_state @abstractmethod def ban_validator(self, validator_hotkey: Hotkey): @@ -59,7 +62,7 @@ def ban_validator(self, validator_hotkey: Hotkey): @abstractmethod def remove_validator(self, validator_hotkey: Hotkey): """ - Remove validator from the lists of banned validators or active addresses. + Remove validator from the lists of known validators and active addresses. Args: validator_hotkey: The hotkey of the validator. @@ -76,3 +79,7 @@ def add_address(self, validator_hotkey: Hotkey, address: Address): address: Address to add. """ pass + + @abstractmethod + def _load_state(self): + pass diff --git a/bt_ddos_shield/utils.py b/bt_ddos_shield/utils.py index e69de29..c80b33b 100644 --- a/bt_ddos_shield/utils.py +++ b/bt_ddos_shield/utils.py @@ -0,0 +1 @@ +Hotkey = str # type of Hotkey diff --git a/bt_ddos_shield/validators_manager.py b/bt_ddos_shield/validators_manager.py index d1c2268..a45f615 100644 --- a/bt_ddos_shield/validators_manager.py +++ b/bt_ddos_shield/validators_manager.py @@ -1,6 +1,6 @@ from abc import ABC, abstractmethod -from bt_ddos_shield.miner_shield import Hotkey +from bt_ddos_shield.utils import Hotkey class AbstractValidatorsManager(ABC): diff --git a/tests/test_miner_shield.py b/tests/test_miner_shield.py new file mode 100644 index 0000000..aedbc27 --- /dev/null +++ b/tests/test_miner_shield.py @@ -0,0 +1,23 @@ +import pytest +from time import sleep + +from bt_ddos_shield.event_processor import LoggingMinerShieldEventProcessor +from bt_ddos_shield.miner_shield import MinerShield, MinerShieldOptions + + +class TestMinerShield: + """ + Test suite for the MinerShield class. + """ + + def test_start_stop(self): + """ + Test if shield is properly starting and stopping. + """ + shield = MinerShield(None, None, None, None, + None, LoggingMinerShieldEventProcessor(), MinerShieldOptions(retry_delay=1)) + shield.enable() + assert shield.run + sleep(1) + shield.disable() + assert not shield.run From 9648ecbebda42490371ec5b561a9e9ee264e2234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Leszczy=C5=84ski?= Date: Tue, 5 Nov 2024 19:01:24 +0100 Subject: [PATCH 02/32] fix: fixes after code review --- bt_ddos_shield/address.py | 2 +- bt_ddos_shield/blockchain_manager.py | 18 ++++- bt_ddos_shield/event_processor.py | 2 +- bt_ddos_shield/miner_shield.py | 104 ++++++++++++++------------- tests/test_miner_shield.py | 4 +- 5 files changed, 75 insertions(+), 55 deletions(-) diff --git a/bt_ddos_shield/address.py b/bt_ddos_shield/address.py index 4ebb9af..28351ee 100644 --- a/bt_ddos_shield/address.py +++ b/bt_ddos_shield/address.py @@ -41,7 +41,7 @@ def encrypt(self) -> bytes: @classmethod @abstractmethod - def decrypt(cls, encrypted_data: bytes): + def decrypt(cls, encrypted_data: bytes) -> 'Address': """ Create address from encrypted address data. diff --git a/bt_ddos_shield/blockchain_manager.py b/bt_ddos_shield/blockchain_manager.py index 9d926f2..e26fba3 100644 --- a/bt_ddos_shield/blockchain_manager.py +++ b/bt_ddos_shield/blockchain_manager.py @@ -1,5 +1,7 @@ from abc import ABC, abstractmethod +from bt_ddos_shield.utils import Hotkey + class AbstractBlockchainManager(ABC): """ @@ -7,11 +9,25 @@ class AbstractBlockchainManager(ABC): """ @abstractmethod - def publish(self, data: bytes): + def put(self, hotkey: Hotkey, data: bytes): """ Puts data to blockchain. Args: + hotkey: Hotkey of user for whom we are putting data. data: Data. """ pass + + @abstractmethod + def get(self, hotkey: Hotkey) -> bytes: + """ + Gets data from blockchain. + + Args: + hotkey: Hotkey of user to get data from. + + Returns: + data: Last block of data put by user. + """ + pass diff --git a/bt_ddos_shield/event_processor.py b/bt_ddos_shield/event_processor.py index 15294c3..cf17367 100644 --- a/bt_ddos_shield/event_processor.py +++ b/bt_ddos_shield/event_processor.py @@ -29,7 +29,7 @@ def add_event(self, event: MinerShieldEvent): pass -class LoggingMinerShieldEventProcessor(AbstractMinerShieldEventProcessor): +class PrintingMinerShieldEventProcessor(AbstractMinerShieldEventProcessor): """ Event processor which logs events to console. """ diff --git a/bt_ddos_shield/miner_shield.py b/bt_ddos_shield/miner_shield.py index 072f5a2..b1d1f15 100644 --- a/bt_ddos_shield/miner_shield.py +++ b/bt_ddos_shield/miner_shield.py @@ -1,4 +1,6 @@ +import re import threading +from abc import ABC, abstractmethod from queue import Queue from dataclasses import dataclass from time import sleep @@ -24,7 +26,21 @@ class MinerShieldOptions: auto_hide_delay_sec: int = 600 # Time in seconds after which the original server will be hidden if # auto_hide_original_server is set to True. - retry_delay: int = 5 # Time in seconds to wait before retrying failed task. + retry_delay: int = 5 # Time in seconds to wait before retrying failed task. + + max_retries: int = -1 # Maximum number of retries for failed task. If set to negative value, task will be retried + # forever, until eventually shield gets disabled. + + +class MinerShieldException(Exception): + pass + + +class MinerShieldDisabledException(MinerShieldException): + """ + Exception raised when shield is disabled and user want to schedule some action to it. + """ + pass class MinerShield: @@ -78,7 +94,8 @@ def enable(self): When shield is running, user can schedule tasks to be processed by worker. """ if self.worker_thread is not None: - raise Exception("Shield is already enabled") + # already started + return self.finishing = False self.run = True @@ -105,16 +122,13 @@ def ban_validator(self, validator_hotkey: Hotkey): validator_hotkey: The hotkey of the validator. """ self._add_task(MinerShieldBanValidatorTask(validator_hotkey)) - pass - def _add_task(self, task): + def _add_task(self, task: 'AbstractMinerShieldTask'): """ Add task to task queue. It will be handled by _worker_function. """ - if not isinstance(task, MinerShieldTask): - raise Exception("Task is not instance of MinerShieldTask") if not self.run: - raise Exception("Shield is disabled") + raise MinerShieldDisabledException() self.task_queue.put(task) @@ -123,40 +137,41 @@ def _worker_function(self): Function called in separate thread by enable() to start the shield. It is handling events put to task_queue. """ - self.event_processor.add_event(MinerShieldEvent(f"Starting shield")) + self._event(f"Starting shield") while self.run: task = self.task_queue.get() - try_count = 1 + try_count: int = 1 while self.run: - self.event_processor.add_event(MinerShieldEvent(f"Handling task {task}, try {try_count}")) + self._event(f"Handling task {task}, try {try_count}") try: - task.handle(self) - self.event_processor.add_event(MinerShieldEvent(f"Task {task} finished successfully")) + task.run(self) + self._event(f"Task {task} finished successfully") break except Exception as e: - self.event_processor.add_event(MinerShieldEvent(f"Error during handling task {task}", e)) + self._event(f"Error during handling task {task}", e) - if self.finishing: + # for negative max_retries value, retry forever + if self.finishing or (0 <= self.options.max_retries < try_count): break try_count += 1 sleep(self.options.retry_delay) - self.event_processor.add_event(MinerShieldEvent(f"Stopping shield")) + self._event(f"Stopping shield") def _handle_initialize(self): """ Initialize shield. Load state and initial validators set. """ self.state_manager.get_state() - self.event_processor.add_event(MinerShieldEvent("State loaded")) + self._event("State loaded") self.validators_manager.refresh_validators() validators: dict[Hotkey, str] = self.validators_manager.get_validators() - self.event_processor.add_event(MinerShieldEvent(f"Validators refreshed, got {len(validators)} validators")) + self._event(f"Validators refreshed, got {len(validators)} validators") self._add_task(MinerShieldValidatorsChangedTask()) @@ -186,13 +201,13 @@ def _handle_validators_changed(self): # handle changes in validators - self.event_processor.add_event(MinerShieldEvent( + self._event( f"Handling validators change, deprecated_validators count={len(deprecated_validators)}" - f", new_validators count={len(new_validators)}, changed_validators count={len(changed_validators)}") + f", new_validators count={len(new_validators)}, changed_validators count={len(changed_validators)}" ) for validator in deprecated_validators: - self.event_processor.add_event(MinerShieldEvent(f"Removing validator {validator}")) + self._event(f"Removing validator {validator}") if validator in current_state.active_addresses: self.address_manager.remove_address(current_state.active_addresses[validator]) @@ -213,24 +228,23 @@ def _handle_ban_validator(self, validator_hotkey: Hotkey): # TODO pass + def _event(self, description: str, *args, **kwargs): + """ + Add event to event processor. + """ + return self.event_processor.add_event(MinerShieldEvent(description, *args, **kwargs)) -class MinerShieldTask: +class AbstractMinerShieldTask(ABC): """ Task to be executed by shield worker. """ - def __init__(self, task_name: str): - """ - Initialize task. - - Args: - task_name: Short name of the task. - """ - self.task_name = task_name + NAME_DELETER = re.compile(r'^MinerShield(.*)Task$') - def handle(self, miner_shield: MinerShield): + @abstractmethod + def run(self, miner_shield: MinerShield): """ - Run task logic. + Run task in miner_shield context. Args miner_shield: Instance of MinerShield in which task is executed. @@ -238,33 +252,23 @@ def handle(self, miner_shield: MinerShield): pass def __repr__(self): - return self.task_name - -class MinerShieldInitializeTask(MinerShieldTask): - def __init__(self): - super().__init__("INITIALIZE_SHIELD") + return self.NAME_DELETER.sub(r'\1', self.__class__.__name__) - def handle(self, miner_shield: MinerShield): +class MinerShieldInitializeTask(AbstractMinerShieldTask): + def run(self, miner_shield: MinerShield): miner_shield._handle_initialize() -class MinerShieldDisableTask(MinerShieldTask): - def __init__(self): - super().__init__("DISABLE_SHIELD") - - def handle(self, miner_shield: MinerShield): +class MinerShieldDisableTask(AbstractMinerShieldTask): + def run(self, miner_shield: MinerShield): miner_shield._handle_disable() -class MinerShieldValidatorsChangedTask(MinerShieldTask): - def __init__(self): - super().__init__("VALIDATORS_CHANGED") - - def handle(self, miner_shield: MinerShield): +class MinerShieldValidatorsChangedTask(AbstractMinerShieldTask): + def run(self, miner_shield: MinerShield): miner_shield._handle_validators_changed() -class MinerShieldBanValidatorTask(MinerShieldTask): +class MinerShieldBanValidatorTask(AbstractMinerShieldTask): def __init__(self, validator_hotkey: Hotkey): - super().__init__("BAN_VALIDATOR") self.validator_hotkey = validator_hotkey - def handle(self, miner_shield: MinerShield): + def run(self, miner_shield: MinerShield): miner_shield._handle_ban_validator(self.validator_hotkey) diff --git a/tests/test_miner_shield.py b/tests/test_miner_shield.py index aedbc27..a2de1ec 100644 --- a/tests/test_miner_shield.py +++ b/tests/test_miner_shield.py @@ -1,7 +1,7 @@ import pytest from time import sleep -from bt_ddos_shield.event_processor import LoggingMinerShieldEventProcessor +from bt_ddos_shield.event_processor import PrintingMinerShieldEventProcessor from bt_ddos_shield.miner_shield import MinerShield, MinerShieldOptions @@ -15,7 +15,7 @@ def test_start_stop(self): Test if shield is properly starting and stopping. """ shield = MinerShield(None, None, None, None, - None, LoggingMinerShieldEventProcessor(), MinerShieldOptions(retry_delay=1)) + None, PrintingMinerShieldEventProcessor(), MinerShieldOptions(retry_delay=1)) shield.enable() assert shield.run sleep(1) From 6f321c7fd0e5fdc42e97145dd396bc02b233c054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Leszczy=C5=84ski?= Date: Thu, 7 Nov 2024 16:33:07 +0100 Subject: [PATCH 03/32] feat: shield logic finished --- README.md | 12 +-- assets/diagrams/CommunicationFlow.svg | 12 +-- bt_ddos_shield/address.py | 15 ++-- bt_ddos_shield/manifest_manager.py | 2 +- bt_ddos_shield/miner_shield.py | 111 +++++++++++++++++++------- bt_ddos_shield/state_manager.py | 54 ++++++------- bt_ddos_shield/utils.py | 5 +- bt_ddos_shield/validators_manager.py | 4 +- tests/test_miner_shield.py | 2 +- 9 files changed, 140 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 1bf1dd1..39cf4dd 100644 --- a/README.md +++ b/README.md @@ -30,25 +30,27 @@ The goal of this project is to implement a distributed and decentralized system - Validators can request the connection information of miners from the subtensor network. This information is validated and decrypted locally using the validator's private key. -## Communication Flow +## Basic Communication Flow diff --git a/assets/diagrams/CommunicationFlow.svg b/assets/diagrams/CommunicationFlow.svg index 29814ea..408c893 100644 --- a/assets/diagrams/CommunicationFlow.svg +++ b/assets/diagrams/CommunicationFlow.svg @@ -1,20 +1,22 @@ -ValidatorValidatorMinerMinerGitHubGitHubStorageStorageBittensorBittensorGenerate Validator key-pairPublish public key along with HotKeyFetch Validator infoEncrypt Miner IP/Domain with Validator public keyAdd/update file with encrypted IPs/Domains for ValidatorsPublish file locationFetch file locationFetch Miner fileDecrypt Miner file entry encrypted for given ValidatorSend request using decrypted Miner IP/Domain