Skip to content

Documentation for Scenarios Module

Scenario

Class to define a scenario for the NEBULA platform. It contains all the parameters needed to create a scenario and run it on the platform.

Source code in nebula/controller/scenarios.py
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
class Scenario:
    """
    Class to define a scenario for the NEBULA platform.
    It contains all the parameters needed to create a scenario and run it on the platform.
    """

    def __init__(
        self,
        scenario_title,
        scenario_description,
        deployment,
        federation,
        topology,
        nodes,
        nodes_graph,
        n_nodes,
        matrix,
        dataset,
        iid,
        partition_selection,
        partition_parameter,
        model,
        agg_algorithm,
        rounds,
        logginglevel,
        report_status_data_queue,
        accelerator,
        gpu_id,
        network_subnet,
        network_gateway,
        epochs,
        attacks,
        poisoned_node_percent,
        poisoned_sample_percent,
        poisoned_noise_percent,
        attack_params,
        with_reputation,
        reputation_metrics,
        initial_reputation,
        weighting_factor,
        weight_model_arrival_latency,
        weight_model_similarity,
        weight_num_messages,
        weight_fraction_params_changed,
        random_geo,
        latitude,
        longitude,
        mobility,
        network_simulation,
        mobility_type,
        radius_federation,
        scheme_mobility,
        round_frequency,
        mobile_participants_percent,
        additional_participants,
        schema_additional_participants,
        random_topology_probability,
        with_sa,
        strict_topology,
        sad_candidate_selector,
        sad_model_handler,
        sar_arbitration_policy,
        sar_neighbor_policy,
    ):
        """
        Initialize the scenario.

        Args:
            title (str): Title of the scenario.
            description (str): Description of the scenario.
            deployment (str): Type of deployment (e.g., 'docker', 'process').
            federation (str): Type of federation.
            topology (str): Network topology.
            nodes (dict): Dictionary of nodes.
            nodes_graph (dict): Graph of nodes.
            n_nodes (int): Number of nodes.
            matrix (list): Matrix of connections.
            dataset (str): Dataset used.
            iid (bool): Indicator if data is independent and identically distributed.
            partition_selection (str): Method of partition selection.
            partition_parameter (float): Parameter for partition selection.
            model (str): Model used.
            agg_algorithm (str): Aggregation algorithm.
            rounds (int): Number of rounds.
            logginglevel (str): Logging level.
            report_status_data_queue (bool): Indicator to report information about the nodes of the scenario
            accelerator (str): Accelerator used.
            gpu_id (list) : Id list of the used gpu
            network_subnet (str): Network subnet.
            network_gateway (str): Network gateway.
            epochs (int): Number of epochs.
            attacks (list): List of attacks.
            poisoned_node_percent (float): Percentage of poisoned nodes.
            poisoned_sample_percent (float): Percentage of poisoned samples.
            noise_type (str): The type of noise applied by the attack.
            targeted (bool): Indicator if the attack is targeted.
            target_label (int): The label to change when `targeted` is True.
            target_changed_label (int): The label to which `target_label` will be changed .
            attack_params (dict) : Attack parameters.
            with_reputation (bool): Indicator if reputation is used.
            reputation_metrics (list): List of reputation metrics.
            initial_reputation (float): Initial reputation.
            weighting_factor (str): dymanic or static weighting factor.
            weight_model_arrival_latency (float): Weight of model arrival latency.
            weight_model_similarity (float): Weight of model similarity.
            weight_num_messages (float): Weight of number of messages.
            weight_fraction_params_changed (float): Weight of fraction of parameters changed.
            random_geo (bool): Indicator if random geo is used.
            latitude (float): Latitude for mobility.
            longitude (float): Longitude for mobility.
            mobility (bool): Indicator if mobility is used.
            mobility_type (str): Type of mobility.
            radius_federation (float): Radius of federation.
            scheme_mobility (str): Scheme of mobility.
            round_frequency (int): Frequency of rounds.
            mobile_participants_percent (float): Percentage of mobile participants.
            additional_participants (list): List of additional participants.
            schema_additional_participants (str): Schema for additional participants.
            random_topology_probability (float): Probability for random topology.
            with_sa (bool) : Indicator if Situational Awareness is used.
            strict_topology (bool) :
            sad_candidate_selector (str) :
            sad_model_handler (str) :
            sar_arbitration_policy (str) :
            sar_neighbor_policy (str) :
        """
        self.scenario_title = scenario_title
        self.scenario_description = scenario_description
        self.deployment = deployment
        self.federation = federation
        self.topology = topology
        self.nodes = nodes
        self.nodes_graph = nodes_graph
        self.n_nodes = n_nodes
        self.matrix = matrix
        self.dataset = dataset
        self.iid = iid
        self.partition_selection = partition_selection
        self.partition_parameter = partition_parameter
        self.model = model
        self.agg_algorithm = agg_algorithm
        self.rounds = rounds
        self.logginglevel = logginglevel
        self.report_status_data_queue = report_status_data_queue
        self.accelerator = accelerator
        self.gpu_id = gpu_id
        self.network_subnet = network_subnet
        self.network_gateway = network_gateway
        self.epochs = epochs
        self.attacks = attacks
        self.poisoned_node_percent = poisoned_node_percent
        self.poisoned_sample_percent = poisoned_sample_percent
        self.poisoned_noise_percent = poisoned_noise_percent
        self.attack_params = attack_params
        self.with_reputation = with_reputation
        self.reputation_metrics = reputation_metrics
        self.initial_reputation = initial_reputation
        self.weighting_factor = weighting_factor
        self.weight_model_arrival_latency = weight_model_arrival_latency
        self.weight_model_similarity = weight_model_similarity
        self.weight_num_messages = weight_num_messages
        self.weight_fraction_params_changed = weight_fraction_params_changed
        self.random_geo = random_geo
        self.latitude = latitude
        self.longitude = longitude
        self.mobility = mobility
        self.network_simulation = network_simulation
        self.mobility_type = mobility_type
        self.radius_federation = radius_federation
        self.scheme_mobility = scheme_mobility
        self.round_frequency = round_frequency
        self.mobile_participants_percent = mobile_participants_percent
        self.additional_participants = additional_participants
        self.schema_additional_participants = schema_additional_participants
        self.random_topology_probability = random_topology_probability
        self.with_sa = with_sa
        self.strict_topology = strict_topology
        self.sad_candidate_selector = sad_candidate_selector
        self.sad_model_handler = sad_model_handler
        self.sar_arbitration_policy = sar_arbitration_policy
        self.sar_neighbor_policy = sar_neighbor_policy

    def attack_node_assign(
        self,
        nodes,
        federation,
        attack,
        poisoned_node_percent,
        poisoned_sample_percent,
        poisoned_noise_percent,
        attack_params,
    ):
        """Identify which nodes will be attacked"""
        import math
        import random
        import logging

        # Validate input parameters
        def validate_percentage(value, name):
            try:
                value = float(value)
                if not 0 <= value <= 100:
                    raise ValueError(f"{name} must be between 0 and 100")
                return value
            except (TypeError, ValueError) as e:
                raise ValueError(f"Invalid {name}: {str(e)}")

        def validate_positive_int(value, name):
            try:
                value = int(value)
                if value < 0:
                    raise ValueError(f"{name} must be positive")
                return value
            except (TypeError, ValueError) as e:
                raise ValueError(f"Invalid {name}: {str(e)}")

        # Validate attack type
        valid_attacks = {
            "No Attack", "Label Flipping", "Sample Poisoning", "Model Poisoning",
            "GLL Neuron Inversion", "Swapping Weights", "Delayer", "Flooding"
        }

        # Handle attack parameter which can be either a string or a list
        if isinstance(attack, list):
            if not attack:  # Empty list
                attack = "No Attack"
            else:
                attack = attack[0]  # Take the first attack if it's a list
        elif not isinstance(attack, str):
            raise ValueError(f"Invalid attack type: {attack}. Expected string or list.")

        if attack not in valid_attacks:
            raise ValueError(f"Invalid attack type: {attack}")

        # Validate percentage parameters
        poisoned_node_percent = validate_percentage(poisoned_node_percent, "poisoned_node_percent")
        poisoned_sample_percent = validate_percentage(poisoned_sample_percent, "poisoned_sample_percent")
        poisoned_noise_percent = validate_percentage(poisoned_noise_percent, "poisoned_noise_percent")

        nodes_index = []
        # Get the nodes index
        if federation == "DFL":
            nodes_index = list(nodes.keys())
        else:
            for node in nodes:
                if nodes[node]["role"] != "server":
                    nodes_index.append(node)

        logging.info(f"Nodes index: {nodes_index}")
        logging.info(f"Attack type: {attack}")
        logging.info(f"Poisoned node percent: {poisoned_node_percent}")

        mal_nodes_defined = any(nodes[node]["malicious"] for node in nodes)
        logging.info(f"Malicious nodes already defined: {mal_nodes_defined}")

        attacked_nodes = []

        if not mal_nodes_defined and attack != "No Attack":
            n_nodes = len(nodes_index)
            # Number of attacked nodes, round up
            num_attacked = int(math.ceil(poisoned_node_percent / 100 * n_nodes))
            if num_attacked > n_nodes:
                num_attacked = n_nodes

            # Get the index of attacked nodes
            attacked_nodes = random.sample(nodes_index, num_attacked)
            logging.info(f"Number of nodes to attack: {num_attacked}")
            logging.info(f"Attacked nodes: {attacked_nodes}")

        # Assign the role of each node
        for node in nodes:
            node_att = "No Attack"
            malicious = False
            with_reputation = self.with_reputation

            if node in attacked_nodes or nodes[node]["malicious"]:
                malicious = True
                with_reputation = False
                node_att = attack
                logging.info(f"Node {node} marked as malicious with attack {attack}")

                # Initialize attack parameters with defaults
                attack_params = attack_params.copy() if attack_params else {}

                # Set attack-specific parameters
                if attack == "Label Flipping":
                    attack_params["poisonedNodePercent"] = poisoned_node_percent
                    attack_params["poisonedSamplePercent"] = poisoned_sample_percent
                    attack_params["targeted"] = attack_params.get("targeted", False)
                    if attack_params["targeted"]:
                        attack_params["targetLabel"] = validate_positive_int(
                            attack_params.get("targetLabel", 4), "targetLabel"
                        )
                        attack_params["targetChangedLabel"] = validate_positive_int(
                            attack_params.get("targetChangedLabel", 7), "targetChangedLabel"
                        )

                elif attack == "Sample Poisoning":
                    attack_params["poisonedNodePercent"] = poisoned_node_percent
                    attack_params["poisonedSamplePercent"] = poisoned_sample_percent
                    attack_params["poisonedNoisePercent"] = poisoned_noise_percent
                    attack_params["noiseType"] = attack_params.get("noiseType", "Salt")
                    attack_params["targeted"] = attack_params.get("targeted", False)

                elif attack == "Model Poisoning":
                    attack_params["poisonedNodePercent"] = poisoned_node_percent
                    attack_params["poisonedNoisePercent"] = poisoned_noise_percent
                    attack_params["noiseType"] = attack_params.get("noiseType", "Salt")

                elif attack == "GLL Neuron Inversion":
                    attack_params["poisonedNodePercent"] = poisoned_node_percent

                elif attack == "Swapping Weights":
                    attack_params["poisonedNodePercent"] = poisoned_node_percent
                    attack_params["layerIdx"] = validate_positive_int(
                        attack_params.get("layerIdx", 0), "layerIdx"
                    )

                elif attack == "Delayer":
                    attack_params["poisonedNodePercent"] = poisoned_node_percent
                    attack_params["delay"] = validate_positive_int(
                        attack_params.get("delay", 10), "delay"
                    )
                    attack_params["targetPercentage"] = validate_percentage(
                        attack_params.get("targetPercentage", 100), "targetPercentage"
                    )
                    attack_params["selectionInterval"] = validate_positive_int(
                        attack_params.get("selectionInterval", 1), "selectionInterval"
                    )

                elif attack == "Flooding":
                    attack_params["poisonedNodePercent"] = poisoned_node_percent
                    attack_params["floodingFactor"] = validate_positive_int(
                        attack_params.get("floodingFactor", 100), "floodingFactor"
                    )
                    attack_params["targetPercentage"] = validate_percentage(
                        attack_params.get("targetPercentage", 100), "targetPercentage"
                    )
                    attack_params["selectionInterval"] = validate_positive_int(
                        attack_params.get("selectionInterval", 1), "selectionInterval"
                    )

                # Add common attack parameters
                attack_params["startRound"] = validate_positive_int(
                    attack_params.get("startRound", 1), "startRound"
                )
                attack_params["stopRound"] = validate_positive_int(
                    attack_params.get("stopRound", 10), "stopRound"
                )
                attack_params["attackInterval"] = validate_positive_int(
                    attack_params.get("attackInterval", 1), "attackInterval"
                )

                # Validate round parameters
                if attack_params["startRound"] >= attack_params["stopRound"]:
                    raise ValueError("startRound must be less than stopRound")

            nodes[node]["malicious"] = malicious
            nodes[node]["with_reputation"] = with_reputation
            nodes[node]["attacks"] = node_att
            nodes[node]["attack_params"] = attack_params

            # Ensure the attack type is properly set in the node configuration
            if malicious and attack != "No Attack":
                nodes[node]["adversarial_args"] = {
                    "attacks": attack,
                    "attack_params": attack_params
                }
            else:
                nodes[node]["adversarial_args"] = {
                    "attacks": "No Attack",
                    "attack_params": {}
                }

            logging.info(f"Node {node} final configuration - malicious: {nodes[node]['malicious']}, attack: {nodes[node]['attacks']}")

        return nodes

    def mobility_assign(self, nodes, mobile_participants_percent):
        """Assign mobility to nodes"""
        import random

        # Number of mobile nodes, round down
        num_mobile = math.floor(mobile_participants_percent / 100 * len(nodes))
        if num_mobile > len(nodes):
            num_mobile = len(nodes)

        # Get the index of mobile nodes
        mobile_nodes = random.sample(list(nodes.keys()), num_mobile)

        # Assign the role of each node
        for node in nodes:
            node_mob = False
            if node in mobile_nodes:
                node_mob = True
            nodes[node]["mobility"] = node_mob
        return nodes

    @classmethod
    def from_dict(cls, data):
        return cls(**data)

__init__(scenario_title, scenario_description, deployment, federation, topology, nodes, nodes_graph, n_nodes, matrix, dataset, iid, partition_selection, partition_parameter, model, agg_algorithm, rounds, logginglevel, report_status_data_queue, accelerator, gpu_id, network_subnet, network_gateway, epochs, attacks, poisoned_node_percent, poisoned_sample_percent, poisoned_noise_percent, attack_params, with_reputation, reputation_metrics, initial_reputation, weighting_factor, weight_model_arrival_latency, weight_model_similarity, weight_num_messages, weight_fraction_params_changed, random_geo, latitude, longitude, mobility, network_simulation, mobility_type, radius_federation, scheme_mobility, round_frequency, mobile_participants_percent, additional_participants, schema_additional_participants, random_topology_probability, with_sa, strict_topology, sad_candidate_selector, sad_model_handler, sar_arbitration_policy, sar_neighbor_policy)

Initialize the scenario.

Parameters:

Name Type Description Default
title str

Title of the scenario.

required
description str

Description of the scenario.

required
deployment str

Type of deployment (e.g., 'docker', 'process').

required
federation str

Type of federation.

required
topology str

Network topology.

required
nodes dict

Dictionary of nodes.

required
nodes_graph dict

Graph of nodes.

required
n_nodes int

Number of nodes.

required
matrix list

Matrix of connections.

required
dataset str

Dataset used.

required
iid bool

Indicator if data is independent and identically distributed.

required
partition_selection str

Method of partition selection.

required
partition_parameter float

Parameter for partition selection.

required
model str

Model used.

required
agg_algorithm str

Aggregation algorithm.

required
rounds int

Number of rounds.

required
logginglevel str

Logging level.

required
report_status_data_queue bool

Indicator to report information about the nodes of the scenario

required
accelerator str

Accelerator used.

required
gpu_id list)

Id list of the used gpu

required
network_subnet str

Network subnet.

required
network_gateway str

Network gateway.

required
epochs int

Number of epochs.

required
attacks list

List of attacks.

required
poisoned_node_percent float

Percentage of poisoned nodes.

required
poisoned_sample_percent float

Percentage of poisoned samples.

required
noise_type str

The type of noise applied by the attack.

required
targeted bool

Indicator if the attack is targeted.

required
target_label int

The label to change when targeted is True.

required
target_changed_label int

The label to which target_label will be changed .

required
attack_params dict)

Attack parameters.

required
with_reputation bool

Indicator if reputation is used.

required
reputation_metrics list

List of reputation metrics.

required
initial_reputation float

Initial reputation.

required
weighting_factor str

dymanic or static weighting factor.

required
weight_model_arrival_latency float

Weight of model arrival latency.

required
weight_model_similarity float

Weight of model similarity.

required
weight_num_messages float

Weight of number of messages.

required
weight_fraction_params_changed float

Weight of fraction of parameters changed.

required
random_geo bool

Indicator if random geo is used.

required
latitude float

Latitude for mobility.

required
longitude float

Longitude for mobility.

required
mobility bool

Indicator if mobility is used.

required
mobility_type str

Type of mobility.

required
radius_federation float

Radius of federation.

required
scheme_mobility str

Scheme of mobility.

required
round_frequency int

Frequency of rounds.

required
mobile_participants_percent float

Percentage of mobile participants.

required
additional_participants list

List of additional participants.

required
schema_additional_participants str

Schema for additional participants.

required
random_topology_probability float

Probability for random topology.

required
with_sa bool)

Indicator if Situational Awareness is used.

required
strict_topology bool)
required
sad_candidate_selector str)
required
sad_model_handler str)
required
sar_arbitration_policy str)
required
sar_neighbor_policy str)
required
Source code in nebula/controller/scenarios.py
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
def __init__(
    self,
    scenario_title,
    scenario_description,
    deployment,
    federation,
    topology,
    nodes,
    nodes_graph,
    n_nodes,
    matrix,
    dataset,
    iid,
    partition_selection,
    partition_parameter,
    model,
    agg_algorithm,
    rounds,
    logginglevel,
    report_status_data_queue,
    accelerator,
    gpu_id,
    network_subnet,
    network_gateway,
    epochs,
    attacks,
    poisoned_node_percent,
    poisoned_sample_percent,
    poisoned_noise_percent,
    attack_params,
    with_reputation,
    reputation_metrics,
    initial_reputation,
    weighting_factor,
    weight_model_arrival_latency,
    weight_model_similarity,
    weight_num_messages,
    weight_fraction_params_changed,
    random_geo,
    latitude,
    longitude,
    mobility,
    network_simulation,
    mobility_type,
    radius_federation,
    scheme_mobility,
    round_frequency,
    mobile_participants_percent,
    additional_participants,
    schema_additional_participants,
    random_topology_probability,
    with_sa,
    strict_topology,
    sad_candidate_selector,
    sad_model_handler,
    sar_arbitration_policy,
    sar_neighbor_policy,
):
    """
    Initialize the scenario.

    Args:
        title (str): Title of the scenario.
        description (str): Description of the scenario.
        deployment (str): Type of deployment (e.g., 'docker', 'process').
        federation (str): Type of federation.
        topology (str): Network topology.
        nodes (dict): Dictionary of nodes.
        nodes_graph (dict): Graph of nodes.
        n_nodes (int): Number of nodes.
        matrix (list): Matrix of connections.
        dataset (str): Dataset used.
        iid (bool): Indicator if data is independent and identically distributed.
        partition_selection (str): Method of partition selection.
        partition_parameter (float): Parameter for partition selection.
        model (str): Model used.
        agg_algorithm (str): Aggregation algorithm.
        rounds (int): Number of rounds.
        logginglevel (str): Logging level.
        report_status_data_queue (bool): Indicator to report information about the nodes of the scenario
        accelerator (str): Accelerator used.
        gpu_id (list) : Id list of the used gpu
        network_subnet (str): Network subnet.
        network_gateway (str): Network gateway.
        epochs (int): Number of epochs.
        attacks (list): List of attacks.
        poisoned_node_percent (float): Percentage of poisoned nodes.
        poisoned_sample_percent (float): Percentage of poisoned samples.
        noise_type (str): The type of noise applied by the attack.
        targeted (bool): Indicator if the attack is targeted.
        target_label (int): The label to change when `targeted` is True.
        target_changed_label (int): The label to which `target_label` will be changed .
        attack_params (dict) : Attack parameters.
        with_reputation (bool): Indicator if reputation is used.
        reputation_metrics (list): List of reputation metrics.
        initial_reputation (float): Initial reputation.
        weighting_factor (str): dymanic or static weighting factor.
        weight_model_arrival_latency (float): Weight of model arrival latency.
        weight_model_similarity (float): Weight of model similarity.
        weight_num_messages (float): Weight of number of messages.
        weight_fraction_params_changed (float): Weight of fraction of parameters changed.
        random_geo (bool): Indicator if random geo is used.
        latitude (float): Latitude for mobility.
        longitude (float): Longitude for mobility.
        mobility (bool): Indicator if mobility is used.
        mobility_type (str): Type of mobility.
        radius_federation (float): Radius of federation.
        scheme_mobility (str): Scheme of mobility.
        round_frequency (int): Frequency of rounds.
        mobile_participants_percent (float): Percentage of mobile participants.
        additional_participants (list): List of additional participants.
        schema_additional_participants (str): Schema for additional participants.
        random_topology_probability (float): Probability for random topology.
        with_sa (bool) : Indicator if Situational Awareness is used.
        strict_topology (bool) :
        sad_candidate_selector (str) :
        sad_model_handler (str) :
        sar_arbitration_policy (str) :
        sar_neighbor_policy (str) :
    """
    self.scenario_title = scenario_title
    self.scenario_description = scenario_description
    self.deployment = deployment
    self.federation = federation
    self.topology = topology
    self.nodes = nodes
    self.nodes_graph = nodes_graph
    self.n_nodes = n_nodes
    self.matrix = matrix
    self.dataset = dataset
    self.iid = iid
    self.partition_selection = partition_selection
    self.partition_parameter = partition_parameter
    self.model = model
    self.agg_algorithm = agg_algorithm
    self.rounds = rounds
    self.logginglevel = logginglevel
    self.report_status_data_queue = report_status_data_queue
    self.accelerator = accelerator
    self.gpu_id = gpu_id
    self.network_subnet = network_subnet
    self.network_gateway = network_gateway
    self.epochs = epochs
    self.attacks = attacks
    self.poisoned_node_percent = poisoned_node_percent
    self.poisoned_sample_percent = poisoned_sample_percent
    self.poisoned_noise_percent = poisoned_noise_percent
    self.attack_params = attack_params
    self.with_reputation = with_reputation
    self.reputation_metrics = reputation_metrics
    self.initial_reputation = initial_reputation
    self.weighting_factor = weighting_factor
    self.weight_model_arrival_latency = weight_model_arrival_latency
    self.weight_model_similarity = weight_model_similarity
    self.weight_num_messages = weight_num_messages
    self.weight_fraction_params_changed = weight_fraction_params_changed
    self.random_geo = random_geo
    self.latitude = latitude
    self.longitude = longitude
    self.mobility = mobility
    self.network_simulation = network_simulation
    self.mobility_type = mobility_type
    self.radius_federation = radius_federation
    self.scheme_mobility = scheme_mobility
    self.round_frequency = round_frequency
    self.mobile_participants_percent = mobile_participants_percent
    self.additional_participants = additional_participants
    self.schema_additional_participants = schema_additional_participants
    self.random_topology_probability = random_topology_probability
    self.with_sa = with_sa
    self.strict_topology = strict_topology
    self.sad_candidate_selector = sad_candidate_selector
    self.sad_model_handler = sad_model_handler
    self.sar_arbitration_policy = sar_arbitration_policy
    self.sar_neighbor_policy = sar_neighbor_policy

attack_node_assign(nodes, federation, attack, poisoned_node_percent, poisoned_sample_percent, poisoned_noise_percent, attack_params)

Identify which nodes will be attacked

Source code in nebula/controller/scenarios.py
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
def attack_node_assign(
    self,
    nodes,
    federation,
    attack,
    poisoned_node_percent,
    poisoned_sample_percent,
    poisoned_noise_percent,
    attack_params,
):
    """Identify which nodes will be attacked"""
    import math
    import random
    import logging

    # Validate input parameters
    def validate_percentage(value, name):
        try:
            value = float(value)
            if not 0 <= value <= 100:
                raise ValueError(f"{name} must be between 0 and 100")
            return value
        except (TypeError, ValueError) as e:
            raise ValueError(f"Invalid {name}: {str(e)}")

    def validate_positive_int(value, name):
        try:
            value = int(value)
            if value < 0:
                raise ValueError(f"{name} must be positive")
            return value
        except (TypeError, ValueError) as e:
            raise ValueError(f"Invalid {name}: {str(e)}")

    # Validate attack type
    valid_attacks = {
        "No Attack", "Label Flipping", "Sample Poisoning", "Model Poisoning",
        "GLL Neuron Inversion", "Swapping Weights", "Delayer", "Flooding"
    }

    # Handle attack parameter which can be either a string or a list
    if isinstance(attack, list):
        if not attack:  # Empty list
            attack = "No Attack"
        else:
            attack = attack[0]  # Take the first attack if it's a list
    elif not isinstance(attack, str):
        raise ValueError(f"Invalid attack type: {attack}. Expected string or list.")

    if attack not in valid_attacks:
        raise ValueError(f"Invalid attack type: {attack}")

    # Validate percentage parameters
    poisoned_node_percent = validate_percentage(poisoned_node_percent, "poisoned_node_percent")
    poisoned_sample_percent = validate_percentage(poisoned_sample_percent, "poisoned_sample_percent")
    poisoned_noise_percent = validate_percentage(poisoned_noise_percent, "poisoned_noise_percent")

    nodes_index = []
    # Get the nodes index
    if federation == "DFL":
        nodes_index = list(nodes.keys())
    else:
        for node in nodes:
            if nodes[node]["role"] != "server":
                nodes_index.append(node)

    logging.info(f"Nodes index: {nodes_index}")
    logging.info(f"Attack type: {attack}")
    logging.info(f"Poisoned node percent: {poisoned_node_percent}")

    mal_nodes_defined = any(nodes[node]["malicious"] for node in nodes)
    logging.info(f"Malicious nodes already defined: {mal_nodes_defined}")

    attacked_nodes = []

    if not mal_nodes_defined and attack != "No Attack":
        n_nodes = len(nodes_index)
        # Number of attacked nodes, round up
        num_attacked = int(math.ceil(poisoned_node_percent / 100 * n_nodes))
        if num_attacked > n_nodes:
            num_attacked = n_nodes

        # Get the index of attacked nodes
        attacked_nodes = random.sample(nodes_index, num_attacked)
        logging.info(f"Number of nodes to attack: {num_attacked}")
        logging.info(f"Attacked nodes: {attacked_nodes}")

    # Assign the role of each node
    for node in nodes:
        node_att = "No Attack"
        malicious = False
        with_reputation = self.with_reputation

        if node in attacked_nodes or nodes[node]["malicious"]:
            malicious = True
            with_reputation = False
            node_att = attack
            logging.info(f"Node {node} marked as malicious with attack {attack}")

            # Initialize attack parameters with defaults
            attack_params = attack_params.copy() if attack_params else {}

            # Set attack-specific parameters
            if attack == "Label Flipping":
                attack_params["poisonedNodePercent"] = poisoned_node_percent
                attack_params["poisonedSamplePercent"] = poisoned_sample_percent
                attack_params["targeted"] = attack_params.get("targeted", False)
                if attack_params["targeted"]:
                    attack_params["targetLabel"] = validate_positive_int(
                        attack_params.get("targetLabel", 4), "targetLabel"
                    )
                    attack_params["targetChangedLabel"] = validate_positive_int(
                        attack_params.get("targetChangedLabel", 7), "targetChangedLabel"
                    )

            elif attack == "Sample Poisoning":
                attack_params["poisonedNodePercent"] = poisoned_node_percent
                attack_params["poisonedSamplePercent"] = poisoned_sample_percent
                attack_params["poisonedNoisePercent"] = poisoned_noise_percent
                attack_params["noiseType"] = attack_params.get("noiseType", "Salt")
                attack_params["targeted"] = attack_params.get("targeted", False)

            elif attack == "Model Poisoning":
                attack_params["poisonedNodePercent"] = poisoned_node_percent
                attack_params["poisonedNoisePercent"] = poisoned_noise_percent
                attack_params["noiseType"] = attack_params.get("noiseType", "Salt")

            elif attack == "GLL Neuron Inversion":
                attack_params["poisonedNodePercent"] = poisoned_node_percent

            elif attack == "Swapping Weights":
                attack_params["poisonedNodePercent"] = poisoned_node_percent
                attack_params["layerIdx"] = validate_positive_int(
                    attack_params.get("layerIdx", 0), "layerIdx"
                )

            elif attack == "Delayer":
                attack_params["poisonedNodePercent"] = poisoned_node_percent
                attack_params["delay"] = validate_positive_int(
                    attack_params.get("delay", 10), "delay"
                )
                attack_params["targetPercentage"] = validate_percentage(
                    attack_params.get("targetPercentage", 100), "targetPercentage"
                )
                attack_params["selectionInterval"] = validate_positive_int(
                    attack_params.get("selectionInterval", 1), "selectionInterval"
                )

            elif attack == "Flooding":
                attack_params["poisonedNodePercent"] = poisoned_node_percent
                attack_params["floodingFactor"] = validate_positive_int(
                    attack_params.get("floodingFactor", 100), "floodingFactor"
                )
                attack_params["targetPercentage"] = validate_percentage(
                    attack_params.get("targetPercentage", 100), "targetPercentage"
                )
                attack_params["selectionInterval"] = validate_positive_int(
                    attack_params.get("selectionInterval", 1), "selectionInterval"
                )

            # Add common attack parameters
            attack_params["startRound"] = validate_positive_int(
                attack_params.get("startRound", 1), "startRound"
            )
            attack_params["stopRound"] = validate_positive_int(
                attack_params.get("stopRound", 10), "stopRound"
            )
            attack_params["attackInterval"] = validate_positive_int(
                attack_params.get("attackInterval", 1), "attackInterval"
            )

            # Validate round parameters
            if attack_params["startRound"] >= attack_params["stopRound"]:
                raise ValueError("startRound must be less than stopRound")

        nodes[node]["malicious"] = malicious
        nodes[node]["with_reputation"] = with_reputation
        nodes[node]["attacks"] = node_att
        nodes[node]["attack_params"] = attack_params

        # Ensure the attack type is properly set in the node configuration
        if malicious and attack != "No Attack":
            nodes[node]["adversarial_args"] = {
                "attacks": attack,
                "attack_params": attack_params
            }
        else:
            nodes[node]["adversarial_args"] = {
                "attacks": "No Attack",
                "attack_params": {}
            }

        logging.info(f"Node {node} final configuration - malicious: {nodes[node]['malicious']}, attack: {nodes[node]['attacks']}")

    return nodes

mobility_assign(nodes, mobile_participants_percent)

Assign mobility to nodes

Source code in nebula/controller/scenarios.py
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
def mobility_assign(self, nodes, mobile_participants_percent):
    """Assign mobility to nodes"""
    import random

    # Number of mobile nodes, round down
    num_mobile = math.floor(mobile_participants_percent / 100 * len(nodes))
    if num_mobile > len(nodes):
        num_mobile = len(nodes)

    # Get the index of mobile nodes
    mobile_nodes = random.sample(list(nodes.keys()), num_mobile)

    # Assign the role of each node
    for node in nodes:
        node_mob = False
        if node in mobile_nodes:
            node_mob = True
        nodes[node]["mobility"] = node_mob
    return nodes