Skip to content

Commit 4020ce1

Browse files
committed
Merge remote-tracking branch 'origin/upgrade-docu' into main-controller-in-docker
2 parents 38b1fb2 + 44a04d4 commit 4020ce1

39 files changed

Lines changed: 2757 additions & 76 deletions

nebula/addons/attacks/communications/communicationattack.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,28 @@ def decorator(self, *args):
4444
pass
4545

4646
async def select_targets(self):
47+
"""
48+
Selects a subset of neighboring nodes as attack targets based on the configured selectivity percentage.
49+
50+
This method determines which neighboring nodes should be targeted in the current round of attack.
51+
If the selectivity percentage is less than 100%, it samples a subset of the currently connected direct neighbors.
52+
The selection behavior can be influenced by a `selection_interval`:
53+
- If `selection_interval` is set, target selection occurs only at rounds that are multiples of this interval.
54+
- If no interval is defined but no targets have been selected yet, targets are selected once.
55+
If the selectivity is 100%, all direct neighbors are selected as targets.
56+
57+
Target addresses are retrieved from the CommunicationsManager (only direct connections).
58+
The number of selected targets is at least 1.
59+
60+
Logs are emitted at each selection event to indicate which targets were chosen.
61+
62+
Increments the internal `last_selection_round` counter after execution.
63+
64+
Notes:
65+
- The `self.targets` attribute is updated in-place.
66+
- The `self.last_selection_round` attribute tracks when the selection was last performed.
67+
68+
"""
4769
if self.selectivity_percentage != 100:
4870
if self.selection_interval:
4971
if self.last_selection_round % self.selection_interval == 0:
@@ -63,8 +85,6 @@ async def select_targets(self):
6385
logging.info(f"Selected {self.selectivity_percentage}% targets from neighbors: {self.targets}")
6486
self.last_selection_round += 1
6587

66-
self.last_selection_round += 1
67-
6888
async def _inject_malicious_behaviour(self):
6989
"""Inject malicious behavior into the target method."""
7090
decorated_method = self.decorator(self.decorator_args)(self.original_method)

nebula/addons/gps/gpsmodule.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,63 @@
22

33

44
class GPSModule(ABC):
5+
"""
6+
Abstract base class representing a GPS module interface.
7+
8+
This class defines the required asynchronous methods that any concrete GPS module implementation must provide.
9+
These methods allow for lifecycle control (start/stop), status checking, and distance calculation between coordinates.
10+
11+
Any subclass must implement all the following asynchronous methods:
12+
- `start()`: Begins GPS tracking or data acquisition.
13+
- `stop()`: Halts the GPS module's operation.
14+
- `is_running()`: Checks whether the GPS module is currently active.
15+
- `calculate_distance()`: Computes the distance between two geographic coordinates (latitude and longitude).
16+
17+
All implementations should ensure that methods are non-blocking and integrate smoothly with async event loops.
18+
"""
19+
520
@abstractmethod
621
async def start(self):
22+
"""
23+
Starts the GPS module operation.
24+
25+
This may involve initiating hardware tracking, establishing connections, or beginning periodic updates.
26+
"""
727
pass
828

929
@abstractmethod
1030
async def stop(self):
31+
"""
32+
Stops the GPS module operation.
33+
34+
Ensures that any background tasks or hardware interactions are properly terminated.
35+
"""
1136
pass
1237

1338
@abstractmethod
1439
async def is_running(self):
40+
"""
41+
Checks whether the GPS module is currently active.
42+
43+
Returns:
44+
bool: True if the module is running, False otherwise.
45+
"""
1546
pass
1647

1748
@abstractmethod
1849
async def calculate_distance(self, self_lat, self_long, other_lat, other_long):
50+
"""
51+
Calculates the distance between two geographic points.
52+
53+
Args:
54+
self_lat (float): Latitude of the source point.
55+
self_long (float): Longitude of the source point.
56+
other_lat (float): Latitude of the target point.
57+
other_long (float): Longitude of the target point.
58+
59+
Returns:
60+
float: Distance in meters (or implementation-defined units) between the two coordinates.
61+
"""
1962
pass
2063

2164

nebula/addons/mobility.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,17 +203,17 @@ async def change_geo_location_nearest_neighbor_strategy(
203203
if self._verbose:
204204
logging.info("📍 Changing geo location towards the nearest neighbor")
205205
scale_factor = min(1, self.max_movement_nearest_strategy / distance)
206-
# Calcular el ángulo hacia el vecino
206+
# Calculating angle to the neighbor
207207
angle = math.atan2(neighbor_longitude - longitude, neighbor_latitude - latitude)
208-
# Conversión de movimiento máximo a grados
209-
max_lat_change = self.max_movement_nearest_strategy / 111000 # Cambio en grados para latitud
208+
# Convert maximum movement to angle
209+
max_lat_change = self.max_movement_nearest_strategy / 111000 # Change degree to latitude
210210
max_lon_change = self.max_movement_nearest_strategy / (
211211
111000 * math.cos(math.radians(latitude))
212-
) # Cambio en grados para longitud
213-
# Aplicar escala y dirección
212+
) # Change dregree for longitude
213+
# Scale and direction
214214
delta_lat = max_lat_change * math.cos(angle) * scale_factor
215215
delta_lon = max_lon_change * math.sin(angle) * scale_factor
216-
# Actualizar latitud y longitud
216+
# Update values
217217
new_latitude = latitude + delta_lat
218218
new_longitude = longitude + delta_lon
219219
await self.set_geo_location(new_latitude, new_longitude)

nebula/addons/networksimulation/networksimulator.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,73 @@
22

33

44
class NetworkSimulator(ABC):
5+
"""
6+
Abstract base class representing a network simulator interface.
7+
8+
This interface defines the required methods for controlling and simulating network conditions between nodes.
9+
A concrete implementation is expected to manage artificial delays, bandwidth restrictions, packet loss,
10+
or other configurable conditions typically used in network emulation or testing.
11+
12+
Required asynchronous methods:
13+
- `start()`: Initializes the network simulation module.
14+
- `stop()`: Shuts down the simulation and cleans up any active conditions.
15+
- `set_thresholds(thresholds)`: Configures system-wide thresholds (e.g., max/min delay or distance mappings).
16+
- `set_network_conditions(dest_addr, distance)`: Applies network constraints to a target address based on distance.
17+
18+
Synchronous method:
19+
- `clear_network_conditions(interface)`: Clears any simulated network configuration for a given interface.
20+
21+
All asynchronous methods should be non-blocking to support integration in async systems.
22+
"""
23+
524
@abstractmethod
625
async def start(self):
26+
"""
27+
Starts the network simulation module.
28+
29+
This might involve preparing network interfaces, initializing tools like `tc`, or configuring internal state.
30+
"""
731
pass
832

933
@abstractmethod
1034
async def stop(self):
35+
"""
36+
Stops the network simulation module.
37+
38+
Cleans up any modifications made to network interfaces or system configuration.
39+
"""
1140
pass
1241

1342
@abstractmethod
1443
async def set_thresholds(self, thresholds: dict):
44+
"""
45+
Sets threshold values for simulating conditions.
46+
47+
Args:
48+
thresholds (dict): A dictionary specifying condition thresholds,
49+
e.g., {'low': 100, 'medium': 200, 'high': 300}, or distance-delay mappings.
50+
"""
1551
pass
1652

1753
@abstractmethod
1854
async def set_network_conditions(self, dest_addr, distance):
55+
"""
56+
Applies network simulation settings to a given destination based on the computed distance.
57+
58+
Args:
59+
dest_addr (str): The address of the destination node (e.g., IP or identifier).
60+
distance (float): The physical or logical distance used to determine the simulation severity.
61+
"""
1962
pass
2063

2164
@abstractmethod
2265
def clear_network_conditions(self, interface):
66+
"""
67+
Clears any simulated network conditions applied to the specified network interface.
68+
69+
Args:
70+
interface (str): The name of the network interface to restore (e.g., 'eth0').
71+
"""
2372
pass
2473

2574

nebula/core/addonmanager.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,44 @@
1010

1111

1212
class AddondManager:
13+
"""
14+
Responsible for initializing and managing system add-ons.
15+
16+
This class handles the lifecycle of optional services (add-ons) such as mobility simulation,
17+
GPS module, and network simulation. Add-ons are conditionally deployed based on the provided configuration.
18+
"""
19+
1320
def __init__(self, engine: "Engine", config: Config):
21+
"""
22+
Initializes the AddondManager instance.
23+
24+
Args:
25+
engine (Engine): Reference to the main engine instance of the system.
26+
config (dict): Configuration object containing participant settings for enabling add-ons.
27+
28+
This constructor sets up the internal references to the engine and configuration, and
29+
initializes the list of add-ons to be managed.
30+
"""
1431
self._engine = engine
1532
self._config = config
1633
self._addons = []
1734

1835
async def deploy_additional_services(self):
36+
"""
37+
Deploys and starts additional services based on the participant's configuration.
38+
39+
This method checks the configuration to determine which optional components should be
40+
activated. It supports:
41+
- Mobility simulation (e.g., moving node behavior).
42+
- GPS module (e.g., geolocation updates).
43+
- Network simulation (e.g., changing connectivity conditions).
44+
45+
All enabled add-ons are instantiated and started asynchronously.
46+
47+
Notes:
48+
- Add-ons are stored in the internal list `_addons` for lifecycle management.
49+
- Services are only launched if the corresponding configuration flags are set.
50+
"""
1951
print_msg_box(msg="Deploying Additional Services", indent=2, title="Addons Manager")
2052
if self._config.participant["trustworthiness"]:
2153
from nebula.addons.trustworthiness.trustworthiness import Trustworthiness

nebula/core/aggregation/aggregator.py

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,6 @@
1515
class AggregatorException(Exception):
1616
pass
1717

18-
19-
def create_target_aggregator(config, engine):
20-
from nebula.core.aggregation.fedavg import FedAvg
21-
from nebula.core.aggregation.krum import Krum
22-
from nebula.core.aggregation.median import Median
23-
from nebula.core.aggregation.trimmedmean import TrimmedMean
24-
25-
ALGORITHM_MAP = {
26-
"FedAvg": FedAvg,
27-
"Krum": Krum,
28-
"Median": Median,
29-
"TrimmedMean": TrimmedMean,
30-
}
31-
algorithm = config.participant["defense_args"]["target_aggregation"]
32-
aggregator = ALGORITHM_MAP.get(algorithm)
33-
if aggregator:
34-
return aggregator(config=config, engine=engine)
35-
else:
36-
raise AggregatorException(f"Aggregation algorithm {algorithm} not found.")
37-
38-
3918
class Aggregator(ABC):
4019
def __init__(self, config=None, engine=None):
4120
self.config = config
@@ -59,6 +38,7 @@ def __repr__(self):
5938

6039
@property
6140
def us(self):
41+
"""Federation type UpdateHandler (e.g. DFL-UpdateHandler, CFL-UpdateHandler...)"""
6242
return self._update_storage
6343

6444
@abstractmethod
@@ -71,6 +51,20 @@ async def init(self):
7151
await self.us.init(self.config)
7252

7353
async def update_federation_nodes(self, federation_nodes: set):
54+
"""
55+
Updates the current set of nodes expected to participate in the upcoming aggregation round.
56+
57+
This method informs the update handler (`us`) about the new set of federation nodes,
58+
clears any pending models, and attempts to acquire the aggregation lock to prepare
59+
for model aggregation. If the aggregation process is already running, it raises an exception.
60+
61+
Args:
62+
federation_nodes (set): A set of addresses representing the nodes expected to contribute
63+
updates for the next aggregation round.
64+
65+
Raises:
66+
Exception: If the aggregation process is already running and the lock is currently held.
67+
"""
7468
await self.us.round_expected_updates(federation_nodes=federation_nodes)
7569

7670
if not self._aggregation_done_lock.locked():
@@ -86,6 +80,23 @@ def get_nodes_pending_models_to_aggregate(self):
8680
return self._federation_nodes
8781

8882
async def get_aggregation(self):
83+
"""
84+
Handles the aggregation process for a training round.
85+
86+
This method waits for all expected model updates from federation nodes or until a timeout occurs.
87+
It uses an asynchronous lock to coordinate access and includes an early exit mechanism if all
88+
updates are received before the timeout. Once the condition is satisfied, it releases the lock,
89+
collects the updates, identifies any missing nodes, and publishes an `AggregationEvent`.
90+
Finally, it runs the aggregation algorithm and returns the result.
91+
92+
Returns:
93+
Any: The result of the aggregation process, as returned by `run_aggregation`.
94+
95+
Raises:
96+
TimeoutError: If the aggregation lock is not acquired within the defined timeout.
97+
asyncio.CancelledError: If the aggregation lock acquisition is cancelled.
98+
Exception: For any other unexpected errors during the aggregation process.
99+
"""
89100
try:
90101
timeout = self.config.participant["aggregator_args"]["aggregation_timeout"]
91102
logging.info(f"Aggregation timeout: {timeout} starts...")

0 commit comments

Comments
 (0)