diff --git a/design/appendix/attack-scenarios.tex b/design/appendix/attack-scenarios.tex index f78baa3..efe475f 100644 --- a/design/appendix/attack-scenarios.tex +++ b/design/appendix/attack-scenarios.tex @@ -39,9 +39,11 @@ \subsection{Honest vote splitting}\label{sec:honest vote splitting} \end{enumerate} Condition~\ref{enumi:honest vote splitting:a} guarantees that $V_C\neq V_D$, and condition~\ref{enumi:honest vote splitting:b} makes sure that $D$ is preferrable to $C$. -\begin{lemma} +We stress that this is not the only scenario where the attack can be executed; for example, we have not considered tiebreakers and honest short forks. + +\begin{lemma}\label{lemma:honest vote splitting prob} For an adversary with stake $\alpha$ the probability that the events~\ref{enumi:honest vote splitting:a} and~\ref{enumi:honest vote splitting:b} occur before some Peras round is given by \[ P(H\le A) \cdot P(A' \ge 1) \] - where $H\sim\operatorname{Binom}(L,\phi(1-\alpha))$, $A\sim\operatorname{Binom}(L,\phi(\alpha))$, $A' \sim \operatorname{Binom}(H',\phi(\alpha))$, $H' \sim \operatorname{Geom}(\phi(1-\alpha))$, and where $L= \perasBlockMinSlots$ and $\phi(\sigma) = 1-{(1-\asc)}^\sigma$ \parencite[(1)]{david2018ouroboros}. + where $H\sim\operatorname{Binom}(L,\phi(1-\alpha))$, $A\sim\operatorname{Binom}(L,\phi(\alpha))$, $A' \sim \operatorname{Binom}(H',\phi(\alpha))$, $H' \sim \operatorname{Geom}(\phi(1-\alpha))$\footnote{Here, we use the convention of counting the number of failures until the first success.}, and where $L= \perasBlockMinSlots$ and $\phi(\sigma) = 1-{(1-\asc)}^\sigma$ \parencite[(1)]{david2018ouroboros}. \end{lemma} \begin{proof} Note that~\ref{enumi:honest vote splitting:a} and~\ref{enumi:honest vote splitting:b} are independent as they refer to disjoint sets of slots. @@ -52,8 +54,18 @@ \subsection{Honest vote splitting}\label{sec:honest vote splitting} Thus, $P(\enquote{\ref{enumi:honest vote splitting:b}}) = P(A'\ge 1)$. \end{proof} -\medskip -We stress that this is not the only scenario where the attack can be executed; for example, we have not considered tiebreakers and honest short forks. +In \cref{lemma:honest vote splitting prob}, we assume that the gap $H'$, i.e.\ the length of the interval $(h,s-\perasBlockMinSlots)$, is sampled geometrically due to the leader schedule. +However, it can also be instructive to set it to a concrete value (letting $H'$ have a one-point distribution), especially if an adversary could force a long gap in some other way. + +\begin{figure}[h] + % see honest-vote-splitting.py + \includegraphics[width=0.95\textwidth]{./appendix/plot-honest-vote-splitting.png} + \centering + \caption{Probabilities (lower bounds) for \cref{lemma:honest vote splitting prob} for realistic parameters. The right plot assumes a slot length of one second and $\perasRoundSlots = 90$.}\label{fig:honest vote splitting prob} +\end{figure} +We plot the propabilities of \cref{lemma:honest vote splitting prob} for realistic parameters in \cref{fig:honest vote splitting prob}. +We observe that for small values for \perasBlockMinSlots{}, even relatively weak adversaries can execute a vote splitting attack rather frequently. +This is relevant in particular as it allows them to trigger a cooldown phase. \section{Attacks on a variant of the block creation rule} diff --git a/design/appendix/plot-honest-vote-splitting.png b/design/appendix/plot-honest-vote-splitting.png new file mode 100644 index 0000000..f8a5a12 Binary files /dev/null and b/design/appendix/plot-honest-vote-splitting.png differ diff --git a/scripts/honest-vote-splitting.py b/scripts/honest-vote-splitting.py index 2914509..a687ee6 100644 --- a/scripts/honest-vote-splitting.py +++ b/scripts/honest-vote-splitting.py @@ -1,5 +1,7 @@ # See "Honest vote splitting" in the design doc for context +import matplotlib.pyplot as plt +import numpy as np from scipy import stats asc = 1 / 20 @@ -15,7 +17,7 @@ def Psuccess(alpha, perasBlockMinSlots): Hp = stats.geom(phi(1 - alpha)) # P(Ap ≥ 1) where Ap ~ Binom(Hp, phi(alpha)) cutoff = int(Hp.ppf(1 - 1e-10)) - Apge1 = sum([Hp.pmf(x) * stats.binom(x, phi(alpha)).sf(0) for x in range(cutoff)]) + Apge1 = sum([Hp.pmf(x + 1) * stats.binom(x, phi(alpha)).sf(0) for x in range(cutoff)]) return HleA * Apge1 @@ -24,3 +26,27 @@ def Psuccess(alpha, perasBlockMinSlots): for perasBlockMinSlots in [30, 60, 90, 120, 150, 300]: p = Psuccess(alpha, perasBlockMinSlots) print(f"alpha = {alpha}, L = {perasBlockMinSlots}: {p}") + +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4)) + +alpha = np.linspace(0, 0.25, num=20) + +for perasBlockMinSlots in [30, 60, 90, 120, 150, 300]: + Ps = np.vectorize(lambda a: Psuccess(a, perasBlockMinSlots))(alpha) + ax1.plot(alpha, Ps, label=f"perasBlockMinSlots = {perasBlockMinSlots}") + expected = 90 / Ps / 3600 + ax2.plot(alpha, expected, label=f"perasBlockMinSlots = {perasBlockMinSlots}") + +ax1.legend() +ax1.grid(True) +ax1.set_xlabel("adversarial stake") +ax1.set_ylabel("attack success probability") + +ax2.legend() +ax2.grid(True) +ax2.set_ylim(0, 100) +ax2.set_xlabel("adversarial stake") +ax2.set_ylabel("expected time until successful attack [h]") + +fig.tight_layout() +fig.savefig("plot-honest-vote-splitting.png", dpi=300, bbox_inches="tight")