das-research/DAS/samplEth/samplEth.py
Arunima Chaudhuri 3ec82dea46 debug
2025-01-10 23:25:52 +05:30

1994 lines
76 KiB
Python

import hashlib
import csv
import matplotlib.pyplot as plt
from collections import defaultdict
import random
import os
import sys
from datetime import datetime
import networkx as nx
import csv
import numpy as np
import time
import math
import json
import concurrent.futures
# # debug file
# class DualOutput:
# def __init__(self, file_path):
# self.terminal = sys.stdout
# self.log = open(file_path, "w")
# def write(self, message):
# self.terminal.write(message)
# self.log.write(message)
# def flush(self):
# self.terminal.flush()
# self.log.flush()
# results_dir = "results"
# debug_file_path = os.path.join(results_dir, "debug.log")
# os.makedirs(results_dir, exist_ok=True)
# sys.stdout = DualOutput(debug_file_path)
RANDOM_SEED = 30052001
random.seed(RANDOM_SEED)
num_rows = 512
num_cols = 512
connections_per_peer = [[50, 100], [100, 150], [150, 200]]
min_custody = 2
val_custody = 2
flat_custody = False
# poison nodes by value
row_number = 3 # 1 -> Country (United States), 2 -> Company (Amazon, Oracle), 3 -> ISP (lighthouse, teku, prysm)
row_value = 'prysm'
# poison nodes by %
poison_by_percentage = True
poison_percentage = 90
# query methods
exponential_growth = False
linear_growth = True
linear_constant_growth = False
hybrid_growth = False
exponential_constant_growth = False
linear_growth_constant = 10
def plot_peer_coverage(selected_node, peer_connections, peer_custody, peer_poison_map, results_folder):
rows_count = [0] * 512
cols_count = [0] * 512
rows_count_without_poisonous_nodes = [0] * 512
cols_count_without_poisonous_nodes = [0] * 512
if selected_node in peer_connections:
for peer in peer_connections[selected_node]:
if peer in peer_custody:
for row in peer_custody[peer]['rows']:
rows_count[row] += 1
if peer_poison_map[peer] == 0:
rows_count_without_poisonous_nodes[row] += 1
for col in peer_custody[peer]['cols']:
cols_count[col] += 1
if peer_poison_map[peer] == 0:
cols_count_without_poisonous_nodes[col] += 1
# First figure with all peers
plt.figure(figsize=(14, 7))
plt.subplots_adjust(top=0.8)
plt.figtext(
0.2, 0.96, # Adjusted x-coordinate to shift left
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
# Plot for rows
plt.subplot(1, 2, 1)
plt.bar(range(512), rows_count, color='blue')
plt.xlabel('Row Index', fontsize=16)
plt.ylabel('Number of Peers', fontsize=16)
plt.title('Level 1 Peer Coverage - Rows', fontsize=16)
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
# Plot for columns
plt.subplot(1, 2, 2)
plt.bar(range(512), cols_count, color='green')
plt.xlabel('Column Index', fontsize=16)
plt.ylabel('Number of Peers', fontsize=16)
plt.title('Level 1 Peer Coverage - Columns', fontsize=16)
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
os.makedirs(results_folder, exist_ok=True)
plt.tight_layout(rect=[0, 0, 1, 0.95]) # Adjust layout to prevent overlap with the top text box
plt.savefig(os.path.join(results_folder, 'peer_coverage.png'), bbox_inches='tight')
plt.close()
# Second figure with only honest peers
plt.figure(figsize=(14, 7))
plt.subplots_adjust(top=0.8)
plt.figtext(
0.2, 0.96, # Adjusted x-coordinate to shift left
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.subplot(1, 2, 1)
plt.bar(range(512), rows_count_without_poisonous_nodes, color='orange')
plt.xlabel('Row Index', fontsize=16)
plt.ylabel('Number of Peers', fontsize=16)
plt.title('Level 1 Peer Coverage - Rows (Honest Peers)', fontsize=16)
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.subplot(1, 2, 2)
plt.bar(range(512), cols_count_without_poisonous_nodes, color='purple')
plt.xlabel('Column Index', fontsize=16)
plt.ylabel('Number of Peers', fontsize=16)
plt.title('Level 1 Peer Coverage - Columns (Honest Peers)', fontsize=16)
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.savefig(os.path.join(results_folder, 'peer_coverage_honest.png'), bbox_inches='tight')
plt.close()
def calculate_peer_coverage_cells(selected_node, peer_connections, peer_custody, peer_poison_map):
# Initialize count for cells
cell_count = [0] * (num_rows * num_cols)
cell_count_without_poisonous_nodes = [0] * (num_rows * num_cols)
# Check if the selected node has connections
if selected_node in peer_connections:
for peer in peer_connections[selected_node]:
if peer in peer_custody:
# Count all cells in the covered rows
for row in peer_custody[peer]['rows']:
for col in range(num_cols): # Count all columns for the row
cell_index = row * num_cols + col
cell_count[cell_index] += 1
if peer_poison_map.get(peer, 0) == 0:
cell_count_without_poisonous_nodes[cell_index] += 1
# Count all cells in the covered columns
for col in peer_custody[peer]['cols']:
for row in range(num_rows): # Count all rows for the column
cell_index = row * num_cols + col
cell_count[cell_index] += 1
if peer_poison_map.get(peer, 0) == 0:
cell_count_without_poisonous_nodes[cell_index] += 1
# for row in peer_custody[peer]['rows']:
# for col in peer_custody[peer]['cols']:
# cell_index = row * num_cols + col
# cell_count[cell_index] -= 1
# if peer_poison_map.get(peer, 0) == 0:
# cell_count_without_poisonous_nodes[cell_index] -= 1
return cell_count, cell_count_without_poisonous_nodes
def plot_peer_coverage_cells(cell_count, results_folder):
# Create a box plot for cells with increased width to make space for the textbox
plt.figure(figsize=(14, 7))
plt.subplots_adjust(top=0.9)
plt.boxplot(cell_count, patch_artist=True, boxprops=dict(facecolor='lightblue', color='blue'),
whiskerprops=dict(color='blue'), medianprops=dict(color='red'))
plt.xlabel('Cells', fontsize=16)
plt.ylabel('Number of Peers', fontsize=16)
plt.title('Level 1 Peer Coverage - Cells', fontsize=16)
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
os.makedirs(results_folder, exist_ok=True)
plt.savefig(os.path.join(results_folder, 'peer_coverage_cells_boxplot.png'))
plt.close()
def plot_peer_coverage_cells_without_poisonous_nodes(cell_count_without_poisonous_nodes, output_dir):
plt.figure(figsize=(14, 7))
plt.subplots_adjust(top=0.9)
plt.boxplot(cell_count_without_poisonous_nodes, patch_artist=True, boxprops=dict(facecolor='lightblue', color='blue'),
whiskerprops=dict(color='blue'), medianprops=dict(color='red'))
plt.xlabel('Cells', fontsize=16)
plt.ylabel('Number of Peers', fontsize=16)
plt.title('Level 1 Honest Peer Coverage - Cells', fontsize=16)
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
os.makedirs(output_dir, exist_ok=True)
plt.savefig(os.path.join(output_dir, 'peer_coverage_cells_without_poisonous_nodes_boxplot.png'))
plt.close()
def plot_peer_coverage_cells_all(peerIDs, peer_connections, peer_custody, results_folder):
cell_count_sum = np.zeros(num_rows * num_cols, dtype=np.uint32)
cell_count_min = np.full(num_rows * num_cols, np.inf, dtype=np.float32)
cell_count_max = np.zeros(num_rows * num_cols, dtype=np.uint32)
for i, selected_node in enumerate(peerIDs):
cell_count = np.zeros(num_rows * num_cols, dtype=np.uint32)
if selected_node in peer_connections:
for peer in peer_connections[selected_node]:
if peer in peer_custody:
# Count all cells in the covered rows
for row in peer_custody[peer]['rows']:
for col in range(num_cols):
cell_index = row * num_cols + col
cell_count[cell_index] += 1
for col in peer_custody[peer]['cols']:
for row in range(num_rows):
cell_index = row * num_cols + col
cell_count[cell_index] += 1
cell_count_sum += cell_count
cell_count_min = np.minimum(cell_count_min, cell_count)
cell_count_max = np.maximum(cell_count_max, cell_count)
if i==800: break
cell_count_avg = cell_count_sum / 801
plt.figure(figsize=(20, 8))
plt.plot(range(num_rows * num_cols), cell_count_min, color='blue', label='Min Peer Coverage', linestyle='-', linewidth=0.2)
plt.plot(range(num_rows * num_cols), cell_count_avg, color='green', label='Avg Peer Coverage', linestyle='-', linewidth=0.2)
plt.plot(range(num_rows * num_cols), cell_count_max, color='red', label='Max Peer Coverage', linestyle='-', linewidth=0.2)
plt.xlabel('Cell Index', fontsize=16)
plt.ylabel('Peer Coverage',fontsize=16)
plt.title('Peer Coverage - Cells (Min, Avg, Max)', fontsize=16)
plt.legend(loc='upper right')
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
os.makedirs(results_folder, exist_ok=True)
plt.savefig(os.path.join(results_folder, 'peer_coverage_combined.png'))
plt.close()
with open(os.path.join(results_folder, 'peer_coverage_stats.txt'), 'w') as f:
f.write('cell_count_min = [')
f.write(', '.join(map(str, cell_count_min)))
f.write(']\n\n')
f.write('cell_count_avg = [')
f.write(', '.join(map(str, cell_count_avg)))
f.write(']\n\n')
f.write('cell_count_max = [')
f.write(', '.join(map(str, cell_count_max)))
f.write(']\n')
def calculate_cells_formula(vpn):
result = (min_custody + vpn * val_custody) * (num_rows/2 + num_cols/2)
return (min(result, 256*516) * 256) / (1024*1024)
def plot_node_cells_downloaded(node_data, results_folder):
node_numbers = list(node_data.keys())
second_column_values = [data[1] for data in node_data.values()]
formula_results = [
calculate_cells_formula(second_column_values[i])
for i in range(len(node_numbers))
]
plt.figure(figsize=(14, 5))
plt.boxplot(formula_results, vert=False, patch_artist=True,
boxprops=dict(facecolor='lightgreen', color='green'),
whiskerprops=dict(color='green'),
medianprops=dict(color='darkgreen'))
plt.title('Amount of Data Downloaded by Nodes', fontsize=16)
plt.xlabel('Megabytes', fontsize=16)
plt.tight_layout()
plt.grid(True, axis='x', which='both', color='gray', linestyle='--', linewidth=0.5)
max_value = max(formula_results)
x_ticks = np.arange(0, max_value + 2, 2)
plt.xticks(x_ticks)
plt.tick_params(axis='both', which='major', labelsize=16)
os.makedirs(results_folder, exist_ok=True)
plt.savefig(os.path.join(results_folder, 'node_data_custom_formula_boxplot.png'))
plt.close()
def print_horizon_cells_statistics(horizon_data_cells, connections_values):
print("Calculating and printing horizon by cells statistics for each connections_per_peer value...\n")
for i, cells in enumerate(horizon_data_cells):
print(f"Statistics for {connections_values[i]} connections per peer:")
mean_value = np.mean(cells)
quartiles = np.percentile(cells, [25, 50, 75])
print(f"Mean: {mean_value:.2f}")
print(f"1st Quartile: {quartiles[0]:.2f}")
print(f"Median (2nd Quartile): {quartiles[1]:.2f}")
print(f"3rd Quartile: {quartiles[2]:.2f}")
print("-" * 40)
print("Finished printing horizon by cells statistics.\n")
def plot_node_data(node_data, results_folder):
node_numbers = list(node_data.keys())
second_column_values = [data[1] for data in node_data.values()]
sorted_second_column_values = sorted(second_column_values)
plt.figure(figsize=(14, 7))
# Bar plot
plt.subplot(1, 2, 1)
plt.bar(node_numbers, sorted_second_column_values, color='blue')
plt.title('Node-validators distribution', fontsize=16)
plt.xlabel('Node Number', fontsize=16)
plt.ylabel('Number of Validators', fontsize=16)
plt.yscale('log')
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
# Box plot
plt.subplot(1, 2, 2)
plt.boxplot(second_column_values, vert=False, patch_artist=True,
boxprops=dict(facecolor='lightblue', color='blue'),
whiskerprops=dict(color='blue'),
medianprops=dict(color='red'))
plt.title('Node-validators distribution', fontsize=16)
plt.xlabel('Number of Validators', fontsize=16)
plt.xscale('log')
plt.tight_layout()
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
os.makedirs(results_folder, exist_ok=True)
plt.savefig(os.path.join(results_folder, 'node_data_plots.png'))
plt.close()
def hash_peer(peerID, iteration):
modified_peerID = peerID + str(iteration)
sha256 = hashlib.sha256(modified_peerID.encode('utf-8')).hexdigest()
binary_hash = bin(int(sha256, 16))[2:].zfill(256)
segment = binary_hash[-10:]
return segment
def assign_rows_cols(peerIDs, node_data):
print("Starting to assign rows and columns to peers...")
peer_custody = defaultdict(lambda: {'rows': set(), 'cols': set()})
row_custody = defaultdict(set)
col_custody = defaultdict(set)
for i, peerID in enumerate(peerIDs):
if flat_custody:
total_custody = min_custody
else:
total_custody = min_custody + node_data[i][1] * val_custody
total_custody = min(total_custody, num_rows)
iteration = 0
while len(peer_custody[peerID]['rows']) < total_custody or len(peer_custody[peerID]['cols']) < total_custody:
binary_hash = hash_peer(peerID, iteration)
bit_type = int(binary_hash[-1])
index = int(binary_hash[:-1], 2)
if bit_type == 0 and len(peer_custody[peerID]['rows']) < total_custody:
if index < num_rows and index not in peer_custody[peerID]['rows']:
peer_custody[peerID]['rows'].add(index)
row_custody[index].add(peerID)
elif bit_type == 1 and len(peer_custody[peerID]['cols']) < total_custody:
if index < num_cols and index not in peer_custody[peerID]['cols']:
peer_custody[peerID]['cols'].add(index)
col_custody[index].add(peerID)
iteration += 1
print(f"Peer index {i} custody assignment: rows - {sorted(peer_custody[peerID]['rows'])}, cols - {sorted(peer_custody[peerID]['cols'])}")
print("Finished assigning rows and columns to peers.")
return peer_custody, row_custody, col_custody
def plot_custody_distribution(row_custody, col_custody, peer_poison_map, results_folder):
row_counts = [len(peers) for peers in row_custody.values()]
col_counts = [len(peers) for peers in col_custody.values()]
def filter_non_poisonous(peers):
return [peer for peer in peers if peer_poison_map.get(peer, 0) == 0]
row_counts_without_poisonous = [len(filter_non_poisonous(peers)) for peers in row_custody.values()]
col_counts_without_poisonous = [len(filter_non_poisonous(peers)) for peers in col_custody.values()]
plt.figure(figsize=(14, 7))
plt.subplot(1, 2, 1)
plt.bar(range(len(row_counts)), row_counts, color='blue')
plt.title('Row Custody Distribution', fontsize=16)
plt.xlabel('Row Index', fontsize=16)
plt.ylabel('Number of Peers', fontsize=16)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.subplot(1, 2, 2)
plt.bar(range(len(col_counts)), col_counts, color='green')
plt.title('Column Custody Distribution', fontsize=16)
plt.xlabel('Column Index', fontsize=16)
plt.ylabel('Number of Peers', fontsize=16)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.tight_layout()
plt.subplots_adjust(top=0.9)
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'custody_distribution.png'))
plt.close()
# Plot custody distribution without poisonous peers
plt.figure(figsize=(14, 7))
plt.subplot(1, 2, 1)
plt.bar(range(len(row_counts_without_poisonous)), row_counts_without_poisonous, color='blue')
plt.title('Row Custody Distribution (Honest Peers)', fontsize=16)
plt.xlabel('Row Index', fontsize=16)
plt.ylabel('Number of Peers', fontsize=16)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.subplot(1, 2, 2)
plt.bar(range(len(col_counts_without_poisonous)), col_counts_without_poisonous, color='green')
plt.title('Column Custody Distribution (Honest Peers)', fontsize=16)
plt.xlabel('Column Index', fontsize=16)
plt.ylabel('Number of Peers', fontsize=16)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.tight_layout()
plt.subplots_adjust(top=0.9)
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'custody_distribution_non_poisonous.png'))
plt.close()
def plot_custody_distribution_boxplot(row_custody, col_custody, peer_poison_map, results_folder):
row_counts = [len(peers) for peers in row_custody.values()]
col_counts = [len(peers) for peers in col_custody.values()]
def filter_non_poisonous(peers):
return [peer for peer in peers if peer_poison_map.get(peer, 0) == 0]
row_counts_without_poisonous = [len(filter_non_poisonous(peers)) for peers in row_custody.values()]
col_counts_without_poisonous = [len(filter_non_poisonous(peers)) for peers in col_custody.values()]
plt.figure(figsize=(14, 7))
plt.subplot(1, 2, 1)
plt.boxplot(row_counts, vert=False, patch_artist=True,
boxprops=dict(facecolor='lightblue', color='blue'),
whiskerprops=dict(color='blue'),
medianprops=dict(color='red'))
plt.title('Row Custody Distribution', fontsize=16)
plt.xlabel('Number of Peers', fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.subplot(1, 2, 2)
plt.boxplot(col_counts, vert=False, patch_artist=True,
boxprops=dict(facecolor='lightgreen', color='green'),
whiskerprops=dict(color='green'),
medianprops=dict(color='red'))
plt.title('Column Custody Distribution', fontsize=16)
plt.xlabel('Number of Peers', fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.tight_layout()
plt.subplots_adjust(top=0.9)
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'custody_distribution_boxplots.png'))
plt.close()
# Plot boxplot for non-poisonous peers only
plt.figure(figsize=(14, 7))
plt.subplot(1, 2, 1)
plt.boxplot(row_counts_without_poisonous, vert=False, patch_artist=True,
boxprops=dict(facecolor='lightblue', color='blue'),
whiskerprops=dict(color='blue'),
medianprops=dict(color='red'))
plt.title('Row Custody Distribution (Honest Peers)', fontsize=16)
plt.xlabel('Number of Peers', fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.subplot(1, 2, 2)
plt.boxplot(col_counts_without_poisonous, vert=False, patch_artist=True,
boxprops=dict(facecolor='lightgreen', color='green'),
whiskerprops=dict(color='green'),
medianprops=dict(color='red'))
plt.title('Column Custody Distribution (Honest Peers)', fontsize=16)
plt.xlabel('Number of Peers', fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.tight_layout()
plt.subplots_adjust(top=0.9)
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'custody_distribution_boxplots_non_poisonous.png'))
plt.close()
# for every connection
def plot_horizon_distribution(peer_horizon, results_folder):
peer_ids = list(peer_horizon.keys())
num_peers = len(peer_ids)
unique_rows_seen = [0] * num_peers
unique_cols_seen = [0] * num_peers
for i, peer in enumerate(peer_ids):
unique_rows_seen[i] = len(peer_horizon[peer]['rows'])
unique_cols_seen[i] = len(peer_horizon[peer]['cols'])
for i in range(num_peers):
if unique_rows_seen[i] >= num_rows / 2:
unique_rows_seen[i] = num_rows
if unique_cols_seen[i] >= num_cols / 2:
unique_cols_seen[i] = num_cols
plt.figure(figsize=(14, 7))
plt.subplot(1, 2, 1)
plt.bar(range(num_peers), unique_rows_seen, color='blue')
plt.title('Horizon level 1 Rows', fontsize=16)
plt.xlabel('Peer Index', fontsize=16)
plt.ylabel('Number of Rows', fontsize=16)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.subplot(1, 2, 2)
plt.bar(range(num_peers), unique_cols_seen, color='green')
plt.title('Horizon level 1 Columns', fontsize=16)
plt.xlabel('Peer Index')
plt.ylabel('Number of Columns', fontsize=16)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.tight_layout()
plt.subplots_adjust(top=0.9)
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'horizon_distribution.png'))
plt.close()
# Plot boxplots for horizon level 1 rows and columns
plt.figure(figsize=(14, 7))
plt.subplot(1, 2, 1)
plt.boxplot(unique_rows_seen, patch_artist=True, boxprops=dict(facecolor='blue', color='blue'), vert=False)
plt.title('Horizon level 1 Rows', fontsize=16)
plt.xlabel('Number of Rows Seen by Each Peer', fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.subplot(1, 2, 2)
plt.boxplot(unique_cols_seen, patch_artist=True, boxprops=dict(facecolor='green', color='green'), vert=False)
plt.title('Horizon level 1 Columns', fontsize=16)
plt.xlabel('Number of Columns Seen by Each Peer', fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.tight_layout()
plt.subplots_adjust(top=0.9)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'horizon_boxplot.png'))
# for every connection
def plot_horizon_distribution_boxplot(peer_horizon, results_folder):
peer_ids = list(peer_horizon.keys())
num_peers = len(peer_ids)
unique_rows_seen = [len(peer_horizon[peer]['rows']) for peer in peer_ids]
unique_cols_seen = [len(peer_horizon[peer]['cols']) for peer in peer_ids]
for i in range(num_peers):
if unique_rows_seen[i] >= num_rows / 2:
unique_rows_seen[i] = num_rows
if unique_cols_seen[i] >= num_cols / 2:
unique_cols_seen[i] = num_cols
plt.figure(figsize=(14, 7))
plt.subplot(1, 2, 1)
plt.boxplot(unique_rows_seen, vert=False, patch_artist=True,
boxprops=dict(facecolor='lightblue', color='blue'),
whiskerprops=dict(color='blue'),
medianprops=dict(color='red'))
plt.title('Horizon level 1 Rows', fontsize=16)
plt.xlabel('Horizon level 1 Rows', fontsize=16)
plt.subplot(1, 2, 2)
plt.boxplot(unique_cols_seen, vert=False, patch_artist=True,
boxprops=dict(facecolor='lightgreen', color='green'),
whiskerprops=dict(color='green'),
medianprops=dict(color='red'))
plt.title('Horizon level 1 Columns', fontsize=16)
plt.xlabel('Horizon level 1 Columns', fontsize=16)
plt.tight_layout()
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.subplots_adjust(top=0.9)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'horizon_distribution_boxplot.png'))
def load_node_data(csv_file):
peerIDs = []
node_data = {}
todes_data = {}
with open(csv_file, newline='') as csvfile:
reader = csv.reader(csvfile)
for row in reader:
peerID = row[0]
peerIDs.append(peerID)
node_name = int(row[1].replace("node", ""))
# Extract node_data
node_values = list(map(int, row[2:6]))
node_data[node_name] = node_values
# Extract todes_data
todes_values = row[7:11]
todes_data[node_name] = todes_values
return peerIDs, node_data, todes_data
def map_peerIDs_to_values(peerIDs, node_data):
peerID_to_val = {}
for key, peerID in enumerate(peerIDs):
if key in node_data:
peerID_to_val[peerID] = node_data[key][1]
else:
peerID_to_val[peerID] = None
return peerID_to_val
def mark_poisoned_nodes(peerIDs, node_data, todes_data):
peer_poison_map = {}
for i, node_id in enumerate(node_data):
i = i % len(peerIDs)
peer_id = peerIDs[i]
if todes_data[node_id][row_number] == row_value:
peer_poison_map[peer_id] = 1
else:
peer_poison_map[peer_id] = 0
print("Peer Poison Map")
# Get indexes of poisonous and non-poisonous peers
poisonous_indexes = sorted([i for i, peer_id in enumerate(peerIDs) if peer_poison_map.get(peer_id) == 1])
non_poisonous_indexes = sorted([i for i, peer_id in enumerate(peerIDs) if peer_poison_map.get(peer_id) == 0])
print("Poisonous Peer Indexes (sorted):", poisonous_indexes)
print("Non-Poisonous Peer Indexes (sorted):", non_poisonous_indexes)
return peer_poison_map
def mark_poisoned_nodes_by_percentage(peerIDs):
peer_poison_map = {peer_id: 0 for peer_id in peerIDs}
num_peers_to_poison = int(len(peerIDs) * poison_percentage / 100)
poisoned_peers = random.sample(peerIDs, num_peers_to_poison)
for peer_id in poisoned_peers:
peer_poison_map[peer_id] = 1
print("Peer Poison Map")
# Get indexes of poisonous and non-poisonous peers
poisonous_indexes = sorted([i for i, peer_id in enumerate(peerIDs) if peer_poison_map.get(peer_id) == 1])
non_poisonous_indexes = sorted([i for i, peer_id in enumerate(peerIDs) if peer_poison_map.get(peer_id) == 0])
print("Poisonous Peer Indexes (sorted):", poisonous_indexes)
print("Non-Poisonous Peer Indexes (sorted):", non_poisonous_indexes)
return peer_poison_map
def connect_peers(peerIDs, connections_range):
print("Starting peer connection process...")
peer_connections = defaultdict(set)
reached_target = []
for peer in peerIDs:
num_connections = random.randint(connections_range[0], connections_range[1])
available_peers = peerIDs[:]
random.shuffle(available_peers)
while len(peer_connections[peer]) < num_connections and available_peers:
other_peer = available_peers.pop()
if other_peer != peer and len(peer_connections[other_peer]) < num_connections:
peer_connections[peer].add(other_peer)
peer_connections[other_peer].add(peer)
peer_index = peerIDs.index(peer)
reached_target.append(peer_index)
print("\nPeers that reached the target connections:")
for peer_index in sorted(reached_target):
connected_indices = sorted(peerIDs.index(p) for p in peer_connections[peerIDs[peer_index]])
print(f"Peer index {peer_index} connected to peers {connected_indices}")
print("\nPeer connection process completed.")
return peer_connections
def calculate_horizon_level_2(peer_custody, peer_connections, peer_poison_map):
peer_horizon_level_2 = {}
for peer in peer_custody:
if peer_poison_map.get(peer, 0) == 1:
continue
level_2_rows = set(peer_custody[peer]['rows'])
level_2_cols = set(peer_custody[peer]['cols'])
neighbors = peer_connections.get(peer, [])
for neighbor in neighbors:
if peer_poison_map.get(neighbor, 0) == 1:
continue
# If we've already reached the maximum possible rows/columns, stop further additions
if len(level_2_rows) < num_rows:
level_2_rows.update(peer_custody[neighbor]['rows'])
if len(level_2_cols) < num_cols:
level_2_cols.update(peer_custody[neighbor]['cols'])
# Get neighbors of neighbors (second hop)
second_hop_neighbors = peer_connections.get(neighbor, [])
# Add second hop neighbors' custody to the level 2 horizon
for second_hop_neighbor in second_hop_neighbors:
if peer_poison_map.get(second_hop_neighbor, 0) == 1:
continue
if len(level_2_rows) < num_rows:
level_2_rows.update(peer_custody[second_hop_neighbor]['rows'])
if len(level_2_cols) < num_cols:
level_2_cols.update(peer_custody[second_hop_neighbor]['cols'])
if len(level_2_rows) >= num_rows and len(level_2_cols) >= num_cols:
break
if len(level_2_rows) >= num_rows and len(level_2_cols) >= num_cols:
break
peer_horizon_level_2[peer] = {
'rows': level_2_rows,
'cols': level_2_cols
}
return peer_horizon_level_2
def calculate_horizon_cells_level_2(peer_horizon_level_2):
print("Calculating level 2 horizon by cells for each peer...")
horizon_cells_level_2 = []
total_cells = num_rows * num_cols
for peer in peer_horizon_level_2:
rows_seen = len(peer_horizon_level_2[peer]['rows'])
cols_seen = len(peer_horizon_level_2[peer]['cols'])
if rows_seen >= num_rows / 2 or cols_seen >= num_cols / 2:
cells_seen = num_rows * num_cols
else:
cells_seen = rows_seen * num_cols + cols_seen * num_rows - rows_seen * cols_seen
normalized_cells_seen = (cells_seen * 100.0) / total_cells # Ensure float division
horizon_cells_level_2.append(normalized_cells_seen)
return horizon_cells_level_2
def calculate_horizon(peer_custody, peer_connections, peer_poison_map):
print("Starting horizon calculation...")
peer_horizon = defaultdict(lambda: {'rows': set(), 'cols': set()})
for peer, connections in peer_connections.items():
# Skip poisoned peers
if peer_poison_map.get(peer, 0) == 1:
continue
horizon_rows = set(peer_custody[peer]['rows'])
horizon_cols = set(peer_custody[peer]['cols'])
for connected_peer in connections:
# Skip poisoned connected peers
if peer_poison_map.get(connected_peer, 0) == 1:
continue
horizon_rows.update(peer_custody[connected_peer]['rows'])
horizon_cols.update(peer_custody[connected_peer]['cols'])
# Update the peer horizon with the final calculated rows and cols
peer_horizon[peer]['rows'] = horizon_rows
peer_horizon[peer]['cols'] = horizon_cols
return peer_horizon
def calculate_horizon_cells(peer_horizon):
horizon_cells = []
horizon_cells_count = []
total_cells = num_rows * num_cols
for peer in peer_horizon:
rows_seen = len(peer_horizon[peer]['rows'])
cols_seen = len(peer_horizon[peer]['cols'])
if rows_seen >= num_rows / 2 or cols_seen >= num_cols / 2:
cells_seen = num_rows * num_cols
else:
cells_seen = rows_seen * num_cols + cols_seen * num_rows - rows_seen * cols_seen
horizon_cells_count.append(cells_seen)
normalized_cells_seen = (cells_seen * 100.0) / total_cells
horizon_cells.append(normalized_cells_seen)
return horizon_cells
def print_horizon_statistics(horizon_data):
print("Horizon statistics:")
for i, horizon in enumerate(horizon_data):
rows = np.array(horizon['rows'])
cols = np.array(horizon['cols'])
# Calculate statistics for rows
rows_mean = np.mean(rows)
rows_q1 = np.percentile(rows, 25)
rows_median = np.median(rows)
rows_q3 = np.percentile(rows, 75)
# Calculate statistics for columns
cols_mean = np.mean(cols)
cols_q1 = np.percentile(cols, 25)
cols_median = np.median(cols)
cols_q3 = np.percentile(cols, 75)
print(f"\nGroup {i+1} Statistics:")
print("Rows:")
print(f"Mean: {rows_mean:.2f}, 1st Quartile (Q1): {rows_q1:.2f}, Median (Q2): {rows_median:.2f}, 3rd Quartile (Q3): {rows_q3:.2f}")
print("Columns:")
print(f"Mean: {cols_mean:.2f}, 1st Quartile (Q1): {cols_q1:.2f}, Median (Q2): {cols_median:.2f}, 3rd Quartile (Q3): {cols_q3:.2f}")
print("Finished printing horizon statistics.")
def plot_horizon_distribution_boxplot(horizon_data, results_folder):
plt.figure(figsize=(16, 7))
num_groups = len(horizon_data)
labels = [f"{i*50} - {(i+1)*50}" for i in range(1,num_groups+1)]
# Plot for Rows
plt.subplot(1, 2, 1)
plt.boxplot([horizon['rows'] for horizon in horizon_data], vert=False, patch_artist=True,
boxprops=dict(facecolor='lightblue', color='blue'),
whiskerprops=dict(color='blue'),
medianprops=dict(color='red'),
labels=labels)
plt.title('Number of rows nodes can see at horizon level 1', fontsize=16)
plt.xlabel('Number of rows', fontsize=16)
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.tight_layout()
# Plot for Columns
plt.subplot(1, 2, 2)
plt.boxplot([horizon['cols'] for horizon in horizon_data], vert=False, patch_artist=True,
boxprops=dict(facecolor='lightgreen', color='green'),
whiskerprops=dict(color='green'),
medianprops=dict(color='red'),
labels=labels)
plt.title('Number of columns nodes can see at horizon level 1', fontsize=16)
plt.xlabel('Number of columns', fontsize=16)
plt.tight_layout()
plt.subplots_adjust(wspace=0.4, top=0.9)
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.subplots_adjust(top=0.9)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'horizon_distribution_boxplot_comparison.png'))
def plot_horizon_cells_distribution_boxplot(horizon_data, results_folder):
plt.figure(figsize=(14, 7))
num_groups = len(horizon_data)
labels = [f"{i*50} - {(i+1)*50}" for i in range(1,num_groups+1)]
box = plt.boxplot(horizon_data, vert=False, patch_artist=True,
boxprops=dict(facecolor='lightcoral', color='red'),
whiskerprops=dict(color='red'),
medianprops=dict(color='darkred'),
labels=labels)
plt.xlim(0, 105)
plt.title('Percentage of cells nodes can see at horizon level 1', fontsize=16)
plt.xlabel('%', fontsize=16)
plt.ylabel('Connection Ranges', fontsize=16)
plt.tight_layout()
plt.grid(True, which='both', axis='both', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.subplots_adjust(top=0.9)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'horizon_cells_distribution_boxplot_comparison.png'))
plt.close()
def print_horizon_statistics_level_2(peer_horizon_level_2):
print("Calculating and printing level 2 horizon statistics...")
horizon_rows_level_2 = [
len(horizon['rows']) if len(horizon['rows']) < num_rows / 2 else num_rows
for horizon in peer_horizon_level_2.values()
]
horizon_cols_level_2 = [
len(horizon['cols']) if len(horizon['cols']) < num_cols / 2 else num_cols
for horizon in peer_horizon_level_2.values()
]
# Calculate statistics
row_mean_level_2 = np.mean(horizon_rows_level_2)
col_mean_level_2 = np.mean(horizon_cols_level_2)
row_quartiles_level_2 = np.percentile(horizon_rows_level_2, [25, 50, 75])
col_quartiles_level_2 = np.percentile(horizon_cols_level_2, [25, 50, 75])
# Print statistics
print(f"Horizon level 2 Rows Statistics:")
print(f"Mean: {row_mean_level_2:.2f}")
print(f"1st Quartile: {row_quartiles_level_2[0]:.2f}")
print(f"Median (2nd Quartile): {row_quartiles_level_2[1]:.2f}")
print(f"3rd Quartile: {row_quartiles_level_2[2]:.2f}")
print(f"Horizon level 2 Columns Statistics:")
print(f"Mean: {col_mean_level_2:.2f}")
print(f"1st Quartile: {col_quartiles_level_2[0]:.2f}")
print(f"Median (2nd Quartile): {col_quartiles_level_2[1]:.2f}")
print(f"3rd Quartile: {col_quartiles_level_2[2]:.2f}")
print("Finished printing level 2 horizon statistics.")
def plot_horizon_distribution_boxplot_level_2(horizon_data_level_2, results_folder):
plt.figure(figsize=(16, 7))
num_groups = len(horizon_data_level_2)
labels = [f"{i*50} - {(i+1)*50}" for i in range(1,num_groups+1)]
# Plot for Rows
plt.subplot(1, 2, 1)
plt.boxplot([horizon['rows'] for horizon in horizon_data_level_2], vert=False, patch_artist=True,
boxprops=dict(facecolor='lightblue', color='blue'),
whiskerprops=dict(color='blue'),
medianprops=dict(color='red'),
labels=labels)
plt.title('Horizon level 2 Rows', fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.xlabel('Number of Rows', fontsize=16)
# Plot for Columns
plt.subplot(1, 2, 2)
plt.boxplot([horizon['cols'] for horizon in horizon_data_level_2], vert=False, patch_artist=True,
boxprops=dict(facecolor='lightgreen', color='green'),
whiskerprops=dict(color='green'),
medianprops=dict(color='red'),
labels=labels)
plt.title('Horizon level 2 Columns', fontsize=16)
plt.xlabel('Number Columns', fontsize=16)
plt.tight_layout()
plt.subplots_adjust(wspace=0.4, top=0.9)
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.subplots_adjust(top=0.9)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'horizon_distribution_boxplot_comparison_level_2.png'))
def plot_horizon_cells_distribution_boxplot_level_2(horizon_data_level_2, results_folder):
plt.figure(figsize=(14, 7))
num_groups = len(horizon_data_level_2)
labels = [f"{i*50} - {(i+1)*50}" for i in range(1,num_groups+1)]
plt.boxplot(horizon_data_level_2, vert=False, patch_artist=True,
boxprops=dict(facecolor='lightcoral', color='red'),
whiskerprops=dict(color='red'),
medianprops=dict(color='darkred'),
labels=labels)
plt.xlim(0, 110)
plt.title('Horizon by Cells (%) - Level 2', fontsize=16)
plt.xlabel('%', fontsize=16)
plt.ylabel('Connection Ranges', fontsize=16)
plt.tight_layout()
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.subplots_adjust(top=0.9)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'horizon_cells_distribution_boxplot_comparison_level_2.png'))
plt.close()
def calculate_supernode_distribution(peer_connections, peerID_to_val):
def count_supernodes(peers):
x = (num_rows/4 - min_custody)/val_custody
return sum(1 for peer in peers if peerID_to_val.get(peer, 0) >= x)
level_1_supernodes_count = []
level_2_supernodes_count = []
# Get all peers
all_peers = list(peer_connections.keys())
for peer in all_peers:
# Level 1 connections
level_1_peers = peer_connections.get(peer, set())
level_1_supernodes = count_supernodes(level_1_peers)
# Level 2 connections
level_2_peers = set()
for connected_peer in level_1_peers:
level_2_peers.update(peer_connections.get(connected_peer, set()))
level_2_peers -= level_1_peers # Exclude Level 1 peers from Level 2 peers
level_2_supernodes = count_supernodes(level_2_peers)
level_1_supernodes_count.append(level_1_supernodes)
level_2_supernodes_count.append(level_2_supernodes)
return level_1_supernodes_count, level_2_supernodes_count
def plot_supernode_distribution(level_1_supernodes_count, level_2_supernodes_count, connections, results_folder):
plt.figure(figsize=(14, 6))
plt.boxplot(level_1_supernodes_count, patch_artist=True,
boxprops=dict(facecolor='blue', color='blue'),
medianprops=dict(color='red'))
plt.title(f'Supernode Distribution - Level 1\nConnection range: {connections[0]} to {connections[1]}', fontsize=16)
plt.ylabel('Number of Supernodes', fontsize=16)
plt.yscale('log')
plt.grid(True, which='both', linestyle='--', linewidth=1)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.minorticks_on()
plt.grid(True, which='minor', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.tight_layout()
plt.subplots_adjust(top=0.85)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'supernode_distribution_level_1_boxplot.png'))
plt.close()
# Create boxplots for Level 1 and Level 2 supernodes in the same figure
plt.figure(figsize=(14, 6))
plt.boxplot([level_1_supernodes_count, level_2_supernodes_count], patch_artist=True,
boxprops=dict(facecolor='blue', color='blue'),
medianprops=dict(color='red'),
labels=['Level 1', 'Level 2'])
plt.title(f'Supernode Distribution - Level 1 and Level 2 \nConnection range: {connections[0]} to {connections[1]}', fontsize=16)
plt.ylabel('Number of Supernodes', fontsize=16)
plt.yscale('log')
plt.grid(True, which='both', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.minorticks_on()
plt.grid(True, which='minor', linestyle=':', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.tight_layout()
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.subplots_adjust(top=0.85)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'supernode_distribution_boxplot.png'))
plt.close()
def plot_cell_count_boxplot(cell_count_summary, results_folder):
plt.figure(figsize=(14, 7))
num_groups = len(cell_count_summary)
labels = [f"{i*50} - {(i+1)*50}" for i in range(1, num_groups + 1)]
plt.boxplot(cell_count_summary, patch_artist=True,
boxprops=dict(facecolor='lightblue', color='blue'),
whiskerprops=dict(color='blue'),
medianprops=dict(color='darkblue'))
plt.title('Number of peers having each cell across connection ranges', fontsize=16)
plt.xlabel('Connection Ranges', fontsize=16)
plt.ylabel('Number of Peers', fontsize=16)
plt.xticks(ticks=range(1, num_groups + 1), labels=labels) # Update x-axis labels
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
os.makedirs(results_folder, exist_ok=True)
plt.subplots_adjust(top=0.9)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'cell_count_boxplot.png'))
plt.close()
def plot_cell_count_without_poisonous_nodes_boxplot(cell_count_summary_without_poisonous_nodes, exec_dir):
plt.figure(figsize=(14, 7))
num_groups = len(cell_count_summary_without_poisonous_nodes)
labels = [f"{i*50} - {(i+1)*50}" for i in range(1, num_groups + 1)]
plt.boxplot(cell_count_summary_without_poisonous_nodes, patch_artist=True,
boxprops=dict(facecolor='lightblue', color='blue'),
whiskerprops=dict(color='blue'),
medianprops=dict(color='darkblue'))
plt.title('Number of honest peers having each cell across connection ranges', fontsize=16)
plt.xlabel('Connection Ranges', fontsize=16)
plt.ylabel('Number of Peers', fontsize=16)
plt.xticks(ticks=range(1, num_groups + 1), labels=labels)
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
os.makedirs(exec_dir, exist_ok=True)
plt.subplots_adjust(top=0.9)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(exec_dir, 'honest_cell_count_boxplot.png'))
plt.close()
def plot_peer_coverage_cells_summary(cell_count_summary, connections_per_peer, results_folder):
data = []
for cell_count in cell_count_summary:
data.append(cell_count)
plt.figure(figsize=(14, 8))
plt.boxplot(data, patch_artist=True,
boxprops=dict(facecolor='lightblue', color='blue'),
whiskerprops=dict(color='blue'),
medianprops=dict(color='red'),
labels=[f'{connections} peers' for connections in connections_per_peer])
plt.xlabel('Connections per Peer Range', fontsize=16)
plt.ylabel('Cell Peer Coverage', fontsize=16)
plt.title('Peer Coverage Over Cells - Summary for Different Connection Ranges', fontsize=16)
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
os.makedirs(results_folder, exist_ok=True)
plt.subplots_adjust(top=0.85)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(results_folder, 'peer_coverage_cells_summary_boxplot.png'))
plt.close()
# Cache latency values based on horizon level
latency_cache = {
"level_1": [random.uniform(0.1, 0.2) for _ in range(1000)],
"level_2": [random.uniform(0.2, 0.3) for _ in range(1000)],
}
def get_latency(peer_to_query, peer_horizon_level_1, peer_horizon_level_2):
if peer_to_query in peer_horizon_level_1:
return random.choice(latency_cache["level_1"])
elif peer_to_query in peer_horizon_level_2:
return random.choice(latency_cache["level_2"])
return None
def generate_random_samples(num_queries):
return [(random.randint(0, num_rows - 1), random.randint(0, num_cols - 1)) for _ in range(num_queries)]
def query_peer(peer_to_query, peer_horizon_level_1, peer_horizon_level_2, peer_poison_map):
"""Query peer with custody, simulate latency, and return the time taken."""
if peer_poison_map.get(peer_to_query, 0) == 1:
return 'timeout', 0.5
latency = get_latency(peer_to_query, peer_horizon_level_1, peer_horizon_level_2)
if latency:
return 'success', latency
return 'invalid', 0.5
def generate_growth_series():
if exponential_growth:
return [2**i for i in range(1000)]
elif linear_growth:
linear_part = list(range(10, 201, linear_growth_constant))
return [1] + linear_part
elif linear_constant_growth:
series = [1, 10, 20, 30, 40]
series.extend([40] * 1000)
return series
elif hybrid_growth:
exponential_part = [2**i for i in range(6)] # [1, 2, 4, 8, 16, 32]
linear_part = list(range(64, 105, 10)) # [64, 74, 84, 94, 104]
constant_part = [104] * 1000
return exponential_part + linear_part + constant_part
elif exponential_constant_growth:
exponential_part = [2**i for i in range(6)] # [1, 2, 4, 8, 16, 32]
constant_part = [32] * 1000
return exponential_part + constant_part
else:
raise ValueError("No growth method selected!")
def query_peer_with_retries(peers_with_custody, peers_with_custody_level_2, peer_horizon_level_1, peer_horizon_level_2, peer_poison_map, max_retries=10150):
queried_peers = []
retries = 0
original_retries = 0
peers_with_custody = list(set(peers_with_custody))
peers_with_custody_level_2 = list(set(peers_with_custody_level_2))
random.shuffle(peers_with_custody)
random.shuffle(peers_with_custody_level_2)
growth_series = generate_growth_series()
for num_peers_to_query in growth_series:
if retries >= max_retries:
break
if not peers_with_custody and not peers_with_custody_level_2:
break
original_retries += num_peers_to_query
# Query Level 1 peers
level_1_batch = peers_with_custody[:num_peers_to_query]
for peer_to_query in level_1_batch:
queried_peers.append(peer_to_query)
result, time_taken = query_peer(peer_to_query, peer_horizon_level_1, peer_horizon_level_2, peer_poison_map)
if result == 'success':
if retries <= 24:
return 'success', time_taken + 0.5 * retries, queried_peers, original_retries
else:
return 'failure', time_taken + 0.5 * retries, queried_peers, original_retries
elif result == 'timeout':
if retries >= max_retries:
return 'failure', 0.5 * max_retries, queried_peers, original_retries
# Remove queried Level 1 peers
peers_with_custody = peers_with_custody[num_peers_to_query:]
# If all Level 1 peers are queried, move to Level 2 peers
if not peers_with_custody:
level_2_batch = peers_with_custody_level_2[:num_peers_to_query]
for peer_to_query in level_2_batch:
queried_peers.append(peer_to_query)
result, time_taken = query_peer(peer_to_query, peer_horizon_level_1, peer_horizon_level_2, peer_poison_map)
if result == 'success':
if retries <= 24:
return 'success', time_taken + 0.5 * retries, queried_peers, original_retries
else:
return 'failure', time_taken + 0.5 * retries, queried_peers, original_retries
elif result == 'timeout':
if retries >= max_retries:
return 'failure', 0.5 * max_retries, queried_peers, original_retries
# Remove queried Level 2 peers
peers_with_custody_level_2 = peers_with_custody_level_2[num_peers_to_query:]
retries += 1
return 'failure', 0.5 * retries, queried_peers, original_retries
def query_all_nodes(peerIDs, peer_custody, peer_connections, peer_horizon_level_1, peer_horizon_level_2, peer_poison_map):
num_queries = 75
all_query_times = []
all_results = []
all_original_retries = []
all_retries_sum_per_node = []
max_total_time = 0
max_peer = None
max_query_times = []
queried_peers_for_max_total_time = []
queried_peers = {}
def query_samples_for_peer(peer):
if peer_poison_map.get(peer, 0) == 1:
return None
query_times = []
results = 'success'
original_retries_sum = 0
queried_peers[peer] = []
samples = generate_random_samples(num_queries)
for sample_row, sample_col in samples:
if (sample_row in peer_custody.get(peer, {}).get('rows', set()) or
sample_col in peer_custody.get(peer, {}).get('cols', set())):
query_times.append(0)
all_original_retries.extend([0])
else:
peers_with_custody = [p for p in peer_connections[peer] if (sample_row in peer_custody.get(p, {}).get('rows', set()) or sample_col in peer_custody.get(p, {}).get('cols', set()))]
peers_with_custody_level_2 = [p for p in peer_connections if p != peer and p not in peers_with_custody and (sample_row in peer_custody.get(p, {}).get('rows', set()) or sample_col in peer_custody.get(p, {}).get('cols', set()))]
result, time_taken, queried_peers_list, original_retries = query_peer_with_retries(
peers_with_custody, peers_with_custody_level_2, peer_horizon_level_1, peer_horizon_level_2, peer_poison_map
)
query_times.append(time_taken)
if result == 'failure':
results = 'failure'
queried_peers[peer].extend(queried_peers_list)
original_retries_sum += original_retries
all_original_retries.extend([original_retries])
total_time = max(query_times)
nonlocal max_total_time, max_peer, max_query_times, queried_peers_for_max_total_time
if total_time > max_total_time:
max_total_time = total_time
max_peer = peer
max_query_times = query_times
queried_peers_for_max_total_time = queried_peers[peer]
return total_time, results, original_retries_sum
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
results = list(executor.map(query_samples_for_peer, peer_connections.keys()))
results = [result for result in results if result is not None]
all_query_times = [result[0] for result in results]
all_results = [result[1] for result in results]
all_retries_sum_per_node = [result[2] for result in results]
return all_query_times, all_results, all_original_retries, all_retries_sum_per_node
def plot_query_times_boxplot(all_query_times, connections, output_dir):
"""Plot a boxplot for the query times and save it to the results/exec_dir."""
plt.figure(figsize=(14, 7))
plt.boxplot(all_query_times, patch_artist=True, boxprops=dict(facecolor="lightblue"))
plt.title(f"Boxplot of Query Times for All Peers\nConnection Range: {connections[0]}-{connections[1]}", fontsize=16)
plt.ylabel("Query Time (seconds)", fontsize=16)
plt.xlabel("All Peers", fontsize=16)
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
os.makedirs(output_dir, exist_ok=True)
plt.axhline(y=12, color='red', linestyle='--', linewidth=1)
plt.subplots_adjust(top=0.85)
x = ''
if exponential_growth:
x = 'Exponential Sampling'
elif linear_growth:
x = 'Linear Sampling'
elif hybrid_growth:
x = 'Hybrid Sampling'
elif linear_constant_growth:
x = 'Linear Constant Sampling'
elif exponential_constant_growth:
x = 'Exponential Constant Sampling'
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%, {x}",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(output_dir, 'query_times_boxplot.png'))
plt.close()
def plot_query_times_boxplot_all(all_connections_query_time, output_dir):
"""Plot boxplots for query times across all connection ranges and save them in a single figure."""
plt.figure(figsize=(14, 7))
plt.boxplot(all_connections_query_time, patch_artist=True, boxprops=dict(facecolor="lightblue"))
plt.title(f"Query Times for Different Connection Ranges", fontsize=16)
plt.ylabel("Query Time (seconds)", fontsize=16)
plt.xlabel("Connection Ranges", fontsize=16)
plt.xticks(ticks=range(1, len(connections_per_peer) + 1),
labels=[f"{conn[0]}-{conn[1]}" for conn in connections_per_peer])
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.axhline(y=12, color='red', linestyle='--', linewidth=1)
plt.subplots_adjust(top=0.85)
x = ''
if exponential_growth:
x = 'Exponential Sampling'
elif linear_growth:
x = 'Linear Sampling'
elif hybrid_growth:
x = 'Hybrid Sampling'
elif linear_constant_growth:
x = 'Linear Constant Sampling'
elif exponential_constant_growth:
x = 'Exponential Constant Sampling'
plt.figtext(
0.3, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%, {x}",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
os.makedirs(output_dir, exist_ok=True)
plt.savefig(os.path.join(output_dir, 'query_times_all_connections_boxplot.png'))
plt.close()
def plot_query_results_all(all_connections_query_results, exec_dir):
"""Plot pie charts in the same figure for block availability across multiple connection ranges."""
num_ranges = len(connections_per_peer)
fig, axes = plt.subplots(1, num_ranges, figsize=(6 * num_ranges, 7))
for idx in range(num_ranges):
connection_range = connections_per_peer[idx]
range_label = f"{connection_range[0]}-{connection_range[1]}"
connection_results = all_connections_query_results[idx]
available_count = connection_results.count('success')
not_available_count = connection_results.count('failure')
sizes = [available_count, not_available_count]
colors = ['lightgreen', 'salmon']
ax = axes[idx]
wedges, texts, autotexts = ax.pie(
sizes, autopct='%1.1f%%', startangle=140, colors=colors, textprops={'fontsize': 16}
)
for autotext in autotexts:
autotext.set_fontsize(16)
ax.set_title(f"Connection Range: {range_label}", fontsize=16)
plt.subplots_adjust(top=0.9)
x = ''
if exponential_growth:
x = 'Exponential Sampling'
elif linear_growth:
x = 'Linear Sampling'
elif hybrid_growth:
x = 'Hybrid Sampling'
elif linear_constant_growth:
x = 'Linear Constant Sampling'
elif exponential_constant_growth:
x = 'Exponential Constant Sampling'
plt.figtext(
0.3, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%, {x}",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
os.makedirs(exec_dir, exist_ok=True)
output_path = os.path.join(exec_dir, 'query_results_pie_charts_all_ranges.png')
plt.savefig(output_path)
plt.close()
def count_malicious_peers(peerID_index, peer_id, peer_connections, peer_poison_map):
level_1_peers = peer_connections.get(peer_id, [])
level_2_peers = set()
for level_1_peer in level_1_peers:
level_2_peers.update(peer_connections.get(level_1_peer, []))
level_2_peers.difference_update(level_1_peers)
level_2_peers.discard(peer_id)
malicious_level_1 = [peer for peer in level_1_peers if peer_poison_map.get(peer, 0) == 1]
num_malicious_level_1 = len(malicious_level_1)
# Count malicious Level 2 peers
malicious_level_2 = [peer for peer in level_2_peers if peer_poison_map.get(peer, 0) == 1]
num_malicious_level_2 = len(malicious_level_2)
print(f"Peer {peerID_index} has : ")
print(f"Malicious Level 1 Peers: {len(malicious_level_1)}")
print(f"Malicious Level 2 Peers: {len(malicious_level_2)}")
return num_malicious_level_1, num_malicious_level_2
def count_poisonous_nodes(peer_connections, peer_poison_map):
level_1_poisonous_counts = []
level_2_poisonous_counts = []
for peer, level_1_peers in peer_connections.items():
# Count poisonous Level 1 peers
level_1_poisonous_count = sum(1 for p in level_1_peers if peer_poison_map.get(p, 0) == 1)
# Find Level 2 peers by checking connections of Level 1 peers, excluding the original peer and Level 1 peers
level_2_peers = set()
for p in level_1_peers:
level_2_peers.update(peer_connections.get(p, []))
level_2_peers -= set(level_1_peers)
level_2_peers.discard(peer)
# Count poisonous Level 2 peers
level_2_poisonous_count = sum(1 for p in level_2_peers if peer_poison_map.get(p, 0) == 1)
# Append counts to the respective arrays
level_1_poisonous_counts.append(level_1_poisonous_count)
level_2_poisonous_counts.append(level_2_poisonous_count)
return level_1_poisonous_counts, level_2_poisonous_counts
def plot_poisonous_counts(all_level_1_poisonous_counts, all_level_2_poisonous_counts, connection_ranges, exec_dir):
os.makedirs(exec_dir, exist_ok=True)
labels = [f"{r[0]}-{r[1]}" for r in connection_ranges]
plt.figure(figsize=(14, 7))
plt.boxplot(all_level_1_poisonous_counts, labels=labels)
plt.title("Level 1 Poisonous Node Count", fontsize=16)
plt.xlabel("Connections Per Peer Range", fontsize=16)
plt.ylabel("Number of Poisonous Nodes", fontsize=16)
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.subplots_adjust(top=0.9)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(exec_dir, "level_1_poisonous_counts.png"))
plt.close()
# Plot for Level 2 poisonous counts
plt.figure(figsize=(14, 7))
plt.boxplot(all_level_2_poisonous_counts, labels=labels)
plt.title("Level 2 Poisonous Nodes Count", fontsize=16)
plt.xlabel("Connections Per Peer Range", fontsize=16)
plt.ylabel("Number of Poisonous Nodes", fontsize=16)
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.subplots_adjust(top=0.9)
plt.figtext(
0.2, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
plt.savefig(os.path.join(exec_dir, "level_2_poisonous_counts.png"))
plt.close()
def plot_original_retries_boxplot_all(all_connections_original_retries, output_dir):
"""Plot boxplots for original retries across all connection ranges and save them in a single figure."""
plt.figure(figsize=(14, 7))
# all_connections_original_retries = [[val + 1 for val in retry_list] for retry_list in all_connections_original_retries]
plt.boxplot(all_connections_original_retries, patch_artist=True, boxprops=dict(facecolor="lightgreen"))
plt.title("Number of peers queried by each node for a sample across connection ranges", fontsize=16)
plt.ylabel("Count of Queried Peers", fontsize=16)
plt.xlabel("Connection Ranges", fontsize=16)
plt.xticks(ticks=range(1, len(connections_per_peer) + 1),
labels=[f"{conn[0]}-{conn[1]}" for conn in connections_per_peer],
fontsize=16)
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
x = ''
if exponential_growth:
x = 'Exponential Sampling'
elif linear_growth:
x = 'Linear Sampling'
elif hybrid_growth:
x = 'Hybrid Sampling'
elif linear_constant_growth:
x = 'Linear Constant Sampling'
elif exponential_constant_growth:
x = 'Exponential Constant Sampling'
plt.figtext(
0.3, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%, {x}",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
os.makedirs(output_dir, exist_ok=True)
plt.savefig(os.path.join(output_dir, 'original_retries_all_connections_boxplot.png'))
plt.close()
def plot_all_connections_all_retries_sum_per_node_boxplot(all_connections_all_retries_sum_per_node, output_dir):
plt.figure(figsize=(14, 7))
plt.boxplot(all_connections_all_retries_sum_per_node, patch_artist=True,
boxprops=dict(facecolor="lightgreen"))
plt.title("Total Sampling Requests Sent by Each Node", fontsize=16)
plt.ylabel("Sum of all sampling requests for each node", fontsize=16)
plt.xlabel("Connection Ranges", fontsize=16)
plt.xticks(ticks=range(1, len(connections_per_peer) + 1),
labels=[f"{conn[0]}-{conn[1]}" for conn in connections_per_peer],
fontsize=16)
plt.grid(True, axis='y', color='gray', linestyle='--', linewidth=0.5)
plt.tick_params(axis='both', which='major', labelsize=16)
x = ''
if exponential_growth:
x = 'Exponential Sampling'
elif linear_growth:
x = 'Linear Sampling'
elif hybrid_growth:
x = 'Hybrid Sampling'
elif linear_constant_growth:
x = 'Linear Constant Sampling'
elif exponential_constant_growth:
x = 'Exponential Constant Sampling'
plt.figtext(
0.3, 0.96,
f"Validator custody: {val_custody}, Malicious nodes: {poison_percentage}%, {x}",
fontsize=16,
ha='center',
bbox=dict(facecolor='white', edgecolor='black', boxstyle='round')
)
output_path = os.path.join(output_dir, 'retries_sum_boxplot_per_node.png')
plt.savefig(output_path)
plt.close()
def main():
results_dir = 'results'
if not os.path.exists(results_dir):
os.makedirs(results_dir)
# Generate unique execution ID based on current date and time
exec_id = datetime.now().strftime('%Y%m%d_%H%M%S')
exec_dir = os.path.join(results_dir, exec_id)
os.makedirs(exec_dir)
csv_file = 'transformed_node_data.csv'
print("Starting the program...")
# load csv files
peerIDs, node_data, todes_data = load_node_data(csv_file)
peerID_to_val = map_peerIDs_to_values(peerIDs, node_data)
if not poison_by_percentage:
peer_poison_map = mark_poisoned_nodes(peerIDs, node_data, todes_data)
else:
peer_poison_map = mark_poisoned_nodes_by_percentage(peerIDs)
peer_custody, row_custody, col_custody = assign_rows_cols(peerIDs, node_data)
plot_custody_distribution(row_custody, col_custody, peer_poison_map, exec_dir)
plot_custody_distribution_boxplot(row_custody, col_custody, peer_poison_map, exec_dir)
plot_node_cells_downloaded(node_data, exec_dir)
plot_node_data(node_data, exec_dir)
horizon_data = []
horizon_data_cells = []
horizon_data_level_2 = []
horizon_data_cells_level_2 = []
cell_count_summary = []
cell_count_summary_without_poisonous_nodes = []
all_connections_query_time = []
all_connections_original_retries = []
all_connections_query_results = []
all_connections_all_retries_sum_per_node = []
all_level_1_poisonous_counts = []
all_level_2_poisonous_counts = []
selected_index = random.randint(0, len(peerIDs) - 1)
selected_node = peerIDs[selected_index]
for connections in connections_per_peer:
peer_connections = connect_peers(peerIDs, connections)
level_1_poisonous_counts, level_2_poisonous_counts = count_poisonous_nodes(peer_connections, peer_poison_map)
all_level_1_poisonous_counts.append(level_1_poisonous_counts)
all_level_2_poisonous_counts.append(level_2_poisonous_counts)
# Create the directory for each connection
output_dir = os.path.join(exec_dir, 'connections', f'{connections}')
os.makedirs(output_dir, exist_ok=True)
peer_horizon = calculate_horizon(peer_custody, peer_connections, peer_poison_map)
peer_horizon_level_2 = calculate_horizon_level_2(peer_custody, peer_connections, peer_poison_map)
level_1_supernodes_count, level_2_supernodes_count = calculate_supernode_distribution(peer_connections, peerID_to_val)
plot_supernode_distribution(level_1_supernodes_count, level_2_supernodes_count, connections, output_dir)
# Plotting results in the specific directory
plot_horizon_distribution(peer_horizon, output_dir)
horizon_rows = [
len(peer_horizon.get(peer, {'rows': set()})['rows']) if len(peer_horizon.get(peer, {'rows': set()})['rows']) < num_rows / 2 else num_rows
for peer in peerIDs if peer_poison_map.get(peer) != 1
]
horizon_cols = [
len(peer_horizon.get(peer, {'cols': set()})['cols']) if len(peer_horizon.get(peer, {'cols': set()})['cols']) < num_cols / 2 else num_cols
for peer in peerIDs if peer_poison_map.get(peer) != 1
]
horizon_data.append({
'rows': horizon_rows,
'cols': horizon_cols
})
horizon_cells = calculate_horizon_cells(peer_horizon)
horizon_data_cells.append(horizon_cells)
plot_peer_coverage(selected_node, peer_connections, peer_custody, peer_poison_map, output_dir)
cell_count, cell_count_without_poisonous_nodes = calculate_peer_coverage_cells(selected_node, peer_connections, peer_custody, peer_poison_map)
cell_count_summary.append(cell_count)
cell_count_summary_without_poisonous_nodes.append(cell_count_without_poisonous_nodes)
plot_peer_coverage_cells(cell_count, output_dir)
plot_peer_coverage_cells_without_poisonous_nodes(cell_count_without_poisonous_nodes, output_dir)
# plot_peer_coverage_cells_all(peerIDs, peer_connections, peer_custody, output_dir)
horizon_rows_level_2 = [
len(peer_horizon_level_2.get(peer, {'rows': set()})['rows']) if len(peer_horizon_level_2.get(peer, {'rows': set()})['rows']) < num_rows / 2 else num_rows
for peer in peerIDs if peer_poison_map.get(peer) != 1
]
horizon_cols_level_2 = [
len(peer_horizon_level_2.get(peer, {'cols': set()})['cols']) if len(peer_horizon_level_2.get(peer, {'cols': set()})['cols']) < num_cols / 2 else num_cols
for peer in peerIDs if peer_poison_map.get(peer) != 1
]
horizon_data_level_2.append({
'rows': horizon_rows_level_2,
'cols': horizon_cols_level_2
})
horizon_cells_level_2 = calculate_horizon_cells_level_2(peer_horizon_level_2)
horizon_data_cells_level_2.append(horizon_cells_level_2)
all_query_times, all_results, all_original_retries, all_retries_sum_per_node = query_all_nodes(peerIDs, peer_custody, peer_connections, peer_horizon, peer_horizon_level_2, peer_poison_map)
all_connections_query_time.append(all_query_times)
all_connections_query_results.append(all_results)
all_connections_original_retries.append(all_original_retries)
all_connections_all_retries_sum_per_node.append(all_retries_sum_per_node)
plot_query_times_boxplot(all_query_times, connections, output_dir)
plot_query_times_boxplot_all(all_connections_query_time, exec_dir)
plot_query_results_all(all_connections_query_results, exec_dir)
plot_original_retries_boxplot_all(all_connections_original_retries, exec_dir)
plot_all_connections_all_retries_sum_per_node_boxplot(all_connections_all_retries_sum_per_node, exec_dir)
file_name_retries = "retries_sum_per_node.json"
file_name_query_time = "query_time_per_node.json"
file_path_retries = os.path.join(exec_dir, file_name_retries)
file_path_query_time = os.path.join(exec_dir, file_name_query_time)
with open(file_path_retries, "w") as file:
json.dump(all_connections_all_retries_sum_per_node, file)
with open(file_path_query_time, "w") as file:
json.dump(all_connections_query_time, file)
plot_cell_count_without_poisonous_nodes_boxplot(cell_count_summary_without_poisonous_nodes, exec_dir)
plot_poisonous_counts(all_level_1_poisonous_counts, all_level_2_poisonous_counts, connections_per_peer, exec_dir)
# Level 1 horizon boxplots
plot_horizon_distribution_boxplot(horizon_data, exec_dir)
plot_horizon_cells_distribution_boxplot(horizon_data_cells, exec_dir)
# print_horizon_statistics(horizon_data)
# print_horizon_cells_statistics(horizon_data_cells, connections_per_peer)
# Level 2 horizon boxplots
plot_horizon_distribution_boxplot_level_2(horizon_data_level_2, exec_dir)
plot_horizon_cells_distribution_boxplot_level_2(horizon_data_cells_level_2, exec_dir)
# print_horizon_cells_statistics(horizon_data_cells_level_2, connections_per_peer)
# sys.stdout.log.close()
# sys.stdout = sys.__stdout__
print("Program finished.")
if __name__ == "__main__":
main()