Skip to content
Merged
1 change: 1 addition & 0 deletions CIP-0140/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Implementors:
- Intersect
Solution-To:
- CPS-0017
- CPS-0021
Discussions:
- https://github.com/cardano-foundation/CIPs/pull/872
Created: 2024-08-15
Expand Down
1,815 changes: 1,815 additions & 0 deletions CIP-????/Readme.md

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions CIP-????/graph/NhandNa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import matplotlib.pyplot as plt

# Adjusted data to ensure matching lengths
s_a_values = [0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.25, 0.3, 0.33, 0.4, 0.45, 0.49, 0.5]
N_a_values = [269, 434, 730, 1537, 2794, 5204, 6384, 7554, 8252, 9872, 11024, 11942, 12171]
N_h_values = [19645, 19541, 18713, 17680, 15618, 14590, 13563, 12949, 11517, 10498, 9685, 9482, 9482]

# Plot
plt.figure(figsize=(14, 6))

# Left plot: Max #Adversarial blocks
plt.subplot(1, 2, 1)
plt.plot([x * 100 for x in s_a_values], N_a_values, marker='o', label="N_a")
plt.title("Nₐ s.t. Pr(Xₐ < Nₐ) = 1 - 2⁻¹²⁸")
plt.xlabel("Adversarial stake sₐ (%)")
plt.ylabel("Max #Adversarial blocks")
plt.grid(True)
plt.legend()

# Right plot: Min #Honest blocks
plt.subplot(1, 2, 2)
plt.plot([x * 100 for x in s_a_values], N_h_values, marker='o', color='orange', label="N_h")
plt.title("Nₕ s.t. Pr(Xₕ > Nₕ) = 2⁻¹²⁸")
plt.xlabel("Adversarial stake sₐ (%)")
plt.ylabel("Min #Honest blocks")
plt.grid(True)
plt.legend()

plt.tight_layout()
plt.show()
2,501 changes: 2,501 additions & 0 deletions CIP-????/graph/extended_error_series_corrected.csv

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions CIP-????/graph/scenario-cost-cross-thresholds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Patch

# Define the range for rho (Grinding Depth)
rho = np.linspace(1, 256, 1000)
f = 0.05
cost_per_cpu_hour = 0.01
w_O = 20 * (2 * rho - 1)
w_O_hours = w_O / 3600
epsilon = 1e-100 # Avoid log(0)

# Praos cost functions
def ant_glance_praos(rho): return 5e-10 * 2**(rho - 2)
def ant_patrol_praos(rho): return 5e-10 * 2**(rho - 1) + 2.16e-2 * 2**(rho - 1) / rho
def owl_stare_praos(rho): return 5e-10 * 2**(rho - 2) + 5e-2 * 2**(rho - 1) / rho
def owl_survey_praos(rho): return 5e-10 * 2**(rho - 2) + 7.16e-2 * 2**(rho - 1) / rho

# Phalanx cost function
def phalanx_cost(rho, scale): return (scale * 2**(rho - 1)) / rho

# Compute log10(Cost) from CPU count
def log_cost(n_cpu): return np.log10(np.maximum(n_cpu * cost_per_cpu_hour * w_O_hours, epsilon))

# Curves for Praos
praos_curves = {
'Ant Glance Praos': ('blue', log_cost(ant_glance_praos(rho))),
'Ant Patrol Praos': ('orange', log_cost(ant_patrol_praos(rho))),
'Owl Stare Praos': ('green', log_cost(owl_stare_praos(rho))),
'Owl Survey Praos': ('red', log_cost(owl_survey_praos(rho))),
}

# Curves for Phalanx configurations
phalanx_curves = {
'Phalanx$_{1/100}$': ('#6A5ACD', log_cost(phalanx_cost(rho, 2.16e2))),
'Phalanx$_{1/10}$': ('#228B22', log_cost(phalanx_cost(rho, 2.16e3))),
'Phalanx$_{max}$': ('#B22222', log_cost(phalanx_cost(rho, 2.16e4))),
}

# Feasibility zones
zones = [
(-10, 4, 'green', 'Trivial'),
(4, 6, 'yellow', 'Feasible'),
(6, 9, '#FFA07A', 'Possible'),
(9, 12, '#FF6347', 'Borderline Infeasible'),
(12, 90, 'red', 'Infeasible')
]

# Function to find crossing points and annotate
def annotate_crossings(log_costs, color, threshold, position='above'):
# Find indices where the curve crosses the threshold
indices = np.where((log_costs[:-1] < threshold) & (log_costs[1:] >= threshold))[0]
if len(indices) > 0:
idx = indices[0]
rho_val = rho[idx]
plt.scatter(rho_val, threshold, color=color, marker='o', s=50, zorder=5)
# Position above or below based on the curve
if position == 'below':
plt.annotate(f'{rho_val:.1f}',
xy=(rho_val, threshold),
xytext=(rho_val + 1.1, threshold - 0.4),
fontsize=8, color=color)
elif position == 'green':
plt.annotate(f'{rho_val:.1f}',
xy=(rho_val, threshold),
xytext=(rho_val - 0.6, threshold - 0.9),
fontsize=8, color=color)
else:
plt.annotate(f'{rho_val:.1f}',
xy=(rho_val, threshold),
xytext=(rho_val - 1, threshold + 0.3),
fontsize=8, color=color)


# # Annotate where curves cross threshold lines
# def annotate_crossings(log_costs, color, threshold, position='above'):
# indices = np.where((log_costs[:-1] < threshold) & (log_costs[1:] >= threshold))[0]
# if len(indices) > 0:
# idx = indices[0]
# rho_val = rho[idx]
# plt.scatter(rho_val, threshold, color=color, marker='o', s=50, zorder=5)
# offset = {'above': (1, 0.5), 'below': (1.1, -0.4), 'green': (-0.6, -0.9)}.get(position, (1, 0.5))
# plt.annotate(f'{rho_val:.1f}', xy=(rho_val, threshold),
# xytext=(rho_val + offset[0], threshold + offset[1]),
# fontsize=8, color=color)

# Unified curve list for annotation logic
curves = [
(praos_curves['Ant Glance Praos'][1], 'blue', 'above'),
(praos_curves['Ant Patrol Praos'][1], 'orange', 'below'),
(praos_curves['Owl Stare Praos'][1], 'green', 'green'),
(praos_curves['Owl Survey Praos'][1], 'red', 'above'),
(phalanx_curves['Phalanx$_{1/100}$'][1], '#6A5ACD', 'above'),
(phalanx_curves['Phalanx$_{1/10}$'][1], '#228B22', 'above'),
(phalanx_curves['Phalanx$_{max}$'][1], '#B22222', 'above')
]

# Plotting
plt.figure(figsize=(12, 7))

# Plot each Praos and Phalanx curve
for label, (color, values) in praos_curves.items():
plt.plot(rho, values, label=label, color=color, linewidth=2)
for label, (color, values) in phalanx_curves.items():
plt.plot(rho, values, label=label, color=color, linestyle='--', linewidth=2)

# Add feasibility zones
for y0, y1, color, label in zones:
plt.axhspan(y0, y1, color=color, alpha=0.1, label=label)

# Annotate crossings
for threshold, _, _, _ in zones:
for log_costs, color, position in curves:
annotate_crossings(log_costs, color, threshold, position)

# Axis labels and title
plt.xlabel(r'$\rho$ (Grinding Depth)', fontsize=14)
plt.ylabel(r'$\log_{10}(\mathrm{Cost\ (USD)})$', fontsize=14)
plt.title('Cost of Grinding Attacks Across Praos and Phalanx Scenarios', fontsize=16)
plt.grid(True, linestyle='--', alpha=0.7)
plt.xlim(0, 256)
y_max = max(np.max(v[np.isfinite(v)]) for _, v in list(praos_curves.values()) + list(phalanx_curves.values())) + 5
plt.ylim(-5, y_max)

# Custom legend
legend_elements = [
*[plt.Line2D([0], [0], color=color, lw=2, label=label) for label, (color, _) in praos_curves.items()],
*[plt.Line2D([0], [0], color=color, lw=2, linestyle='--', label=label) for label, (color, _) in phalanx_curves.items()],
*[Patch(facecolor=color, alpha=0.1, label=label) for _, _, color, label in zones]
]
plt.legend(handles=legend_elements, fontsize=10, loc='lower right',
bbox_to_anchor=(1, 0), ncol=2, handletextpad=0.5, columnspacing=1.5)

# Final layout and save
plt.subplots_adjust(left=0.1, right=0.95, top=0.9, bottom=0.2)

plt.show()
114 changes: 114 additions & 0 deletions CIP-????/graph/scenario_cost_praos_vs_phalanx-full-scenarios.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Patch

# Range for rho (Grinding Depth)
rho = np.linspace(1, 256, 1000)
f = 0.05
cost_per_cpu_hour = 0.01 # $0.01 per CPU-hour

# Time window for computation (w_O)
w_O = 20 * (2 * rho - 1)
w_O_hours = w_O / 3600

# PRAOS cost functions
def ant_glance_praos(rho): return 5e-10 * 2**(rho - 2)
def ant_patrol_praos(rho): return ant_glance_praos(rho) + 2.16e-2 * 2**(rho - 1) / rho
def owl_stare_praos(rho): return ant_glance_praos(rho) + 5e-2 * 2**(rho - 1) / rho
def owl_survey_praos(rho): return ant_glance_praos(rho) + 7.16e-2 * 2**(rho - 1) / rho

# PHALANX curves (generalized)
def phalanx_curve(multiplier):
return lambda rho: (multiplier * 2**(rho - 1)) / rho

phalanx_1_100 = phalanx_curve(2.16e2)
phalanx_1_10 = phalanx_curve(2.16e3)
phalanx_max = phalanx_curve(2.16e4)

# Convert to log10 cost
def compute_log_cost(n_cpu):
return np.log10(np.maximum(n_cpu * cost_per_cpu_hour * w_O_hours, 1e-100))

# Scenario definitions
scenarios = {
"Ant Glance Praos": compute_log_cost(ant_glance_praos(rho)),
"Ant Patrol Praos": compute_log_cost(ant_patrol_praos(rho)),
"Owl Stare Praos": compute_log_cost(owl_stare_praos(rho)),
"Owl Survey Praos": compute_log_cost(owl_survey_praos(rho)),
"Phalanx$_{1/100}$": compute_log_cost(phalanx_1_100(rho)),
"Phalanx$_{1/10}$": compute_log_cost(phalanx_1_10(rho)),
"Phalanx$_{max}$": compute_log_cost(phalanx_max(rho)),
}

# Color map for plot lines
color_map = {
"Ant Glance Praos": "blue",
"Ant Patrol Praos": "orange",
"Owl Stare Praos": "green",
"Owl Survey Praos": "red",
"Phalanx$_{1/100}$": '#6A5ACD',
"Phalanx$_{1/10}$": '#228B22',
"Phalanx$_{max}$": "#B22222"
}

# Feasibility zones
zones = [
(-10, 4, 'green', 'Trivial'),
(4, 6, 'yellow', 'Feasible'),
(6, 9, '#FFA07A', 'Possible'),
(9, 12, '#FF6347', 'Borderline Infeasible'),
(12, 90, 'red', 'Infeasible')
]

# Plot
plt.figure(figsize=(12, 7))

# Draw curves
for label, log_cost in scenarios.items():
style = "-" if "Praos" in label else "--"
plt.plot(rho, log_cost, label=label, color=color_map[label], linestyle=style, linewidth=2)

# Draw feasibility zones
for y0, y1, color_zone, label in zones:
plt.axhspan(y0, y1, color=color_zone, alpha=0.1, label=label)

# Axis labels and title
plt.xlabel(r'$\rho$ (Grinding Depth)', fontsize=14)
plt.ylabel(r'$\log_{10}(\mathrm{Cost\ (USD)})$', fontsize=14)
plt.title('Cost of Grinding Attacks: Praos vs Phalanx Configurations', fontsize=16)
plt.grid(True, linestyle='--', alpha=0.7)
plt.xlim(0, 256)
y_max = max(np.max(v[np.isfinite(v)]) for v in scenarios.values()) + 5
plt.ylim(-5, y_max)

# Delta annotations
def draw_delta(rho_val, praos_label, phalanx_label, x_offset=3):
idx = np.argmin(np.abs(rho - rho_val))
delta = scenarios[phalanx_label][idx] - scenarios[praos_label][idx]
mid = scenarios[phalanx_label][idx] - delta / 2
plt.annotate('', xy=(rho_val, scenarios[phalanx_label][idx]),
xytext=(rho_val, scenarios[praos_label][idx]),
arrowprops=dict(arrowstyle='<->', color='black', lw=1))
plt.text(rho_val + x_offset, mid, f'$\\Delta \\approx {delta:.1f}$', fontsize=12, color='black',
bbox=dict(facecolor='white', alpha=0.8, edgecolor='none'), verticalalignment='center')

draw_delta(50, "Ant Glance Praos", "Phalanx$_{1/100}$")
draw_delta(125, "Owl Survey Praos", "Phalanx$_{1/100}$")
draw_delta(150, "Owl Survey Praos", "Phalanx$_{1/10}$")
draw_delta(175, "Owl Survey Praos", "Phalanx$_{max}$")

# Legend
legend_elements = [
plt.Line2D([0], [0], color='blue', lw=2, label='Ant Glance Praos'),
plt.Line2D([0], [0], color='orange', lw=2, label='Ant Patrol Praos'),
plt.Line2D([0], [0], color='green', lw=2, label='Owl Stare Praos'),
plt.Line2D([0], [0], color='red', lw=2, label='Owl Survey Praos'),
plt.Line2D([0], [0], color='#6A5ACD', lw=2, linestyle='--', label='Phalanx$_{1/100}$'),
plt.Line2D([0], [0], color='#228B22', lw=2, linestyle='--', label='Phalanx$_{1/10}$'),
plt.Line2D([0], [0], color='#B22222', lw=2, linestyle='--', label='Phalanx$_{max}$'),
*[Patch(facecolor=color, alpha=0.1, label=label) for _, _, color, label in zones]
]
plt.legend(handles=legend_elements, fontsize=10, loc='lower right', bbox_to_anchor=(1, 0), ncol=2)

plt.subplots_adjust(left=0.1, right=0.95, top=0.9, bottom=0.2)
plt.show()
Loading