Files
papers/lux-pq-consensus-benchmarks/sections/03-reshare-benchmarks.tex
zeekay 6448cd80a0 papers: BenchmarkPulsarReshare measured for n=21/64/128 (M1 Max)
Real numbers from the new threshold/protocols/lss/lss_pulsar_bench_test.go,
single-process, 1 rep per size, M1 Max post-fuzz-battery (CPU quiescent),
2026-05-04 evening:

* n=21:  12.67 s end-to-end / 0.603 s per party
* n=64:  156.61 s end-to-end / 2.447 s per party
* n=128: 1212.74 s end-to-end / 9.474 s per party

Empirical scaling: O(n^2.40) at the small end, O(n^2.95) at the large
end — consistent with each party verifying O(n^2) Pedersen commits.

Production wall-clock per party = single-process total / n when parties
run on independent cores (reshare has no sequential dependency between
parties at any round); cross-WAN projection in §08 adds 2 RTT.

Reproduction:

  cd ~/work/lux/threshold && \
  GOWORK=off go test -bench='BenchmarkPulsarReshare' -benchtime=1x \
    -timeout=60m -run='^$' ./protocols/lss/...

Replaces the synthesized n=21/64/128 numbers in the v0.1.0 paper that
were derived from per-primitive scaling rules; these are direct
measurements of DynamicResharePulsar end-to-end.
2026-05-04 09:16:47 -07:00

163 lines
9.6 KiB
TeX

%% ====================================================================
\section{Reshare Benchmarks (LSS-Pulsar and LSS-Lens)}
\label{sec:reshare-benchmarks}
%% ====================================================================
The LSS framework \cite{luxlssmpc,lp077} provides protocol-agnostic dynamic resharing across Pulsar (lattice) and Lens (curve) kernels. This section measures the end-to-end resharing time from $(t_{\mathrm{old}}, n_{\mathrm{old}})$ to $(t_{\mathrm{new}}, n_{\mathrm{new}})$ at sizes $\{3, 7, 21, 64, 128\}$.
\subsection{LSS-Pulsar resharing}
Reshare $(t_{\mathrm{old}}, n_{\mathrm{old}}) \to (t_{\mathrm{new}}, n_{\mathrm{new}})$ with $t = n$. End-to-end \texttt{DynamicResharePulsar} wall-clock measured 2026-05-04 evening on M1 Max via the new \texttt{BenchmarkPulsarReshare} (\texttt{threshold/protocols/lss/lss\_pulsar\_bench\_test.go}, single-process, 1 rep per size, all parties simulated sequentially in one Go goroutine — production deployments amortize across $n$ cores/hosts):
\begin{center}
\begin{tabular}{@{}rrrr@{}}
\toprule
$\mathbf{n_{\mathrm{old}} = n_{\mathrm{new}}}$ & \textbf{Single-process total (s)} & \textbf{Per-party (s)} & \textbf{Reproduce} \\
\midrule
21 & 12.67 & 0.603 & \texttt{BenchmarkPulsarReshare/n\_old=21\_n\_new=21} \\
64 & 156.61 & 2.447 & \texttt{BenchmarkPulsarReshare/n\_old=64\_n\_new=64} \\
128 & 1212.74 & 9.474 & \texttt{BenchmarkPulsarReshare/n\_old=128\_n\_new=128} \\
\bottomrule
\end{tabular}
\end{center}
\paragraph{Scaling.}
Single-process total: $156.61 / 12.67 = 12.36\times$ for $n$ ratio
$3.05\times$ ($21 \to 64$); $1212.74 / 156.61 = 7.74\times$ for $n$
ratio $2\times$ ($64 \to 128$). Empirical exponent: $2.40$ at the
small end, $2.95$ at the large end --- consistent with each party
verifying $O(n^2)$ Pedersen commits and the cumulative wall-clock
multiplying by another factor of $n$ from the serial simulation.
The single-process total is the wall-clock for one Go process running the
HJKY97 reshare cycle for every party serially (JVSS commits + share
regeneration + activation cert). Production wall-clock is approximately
$\mathrm{single\text{-}process\text{-}total} / n$ when parties run on
independent cores plus 2 WAN RTTs; the cross-WAN projection table in
\S\ref{sec:cross-wan-latency} accounts for this.
Reproduction:
\begin{verbatim}
cd ~/work/lux/threshold && GOWORK=off go test \
-bench='BenchmarkPulsarReshare' -benchtime=1x -timeout=60m \
-run='^$' ./protocols/lss/...
\end{verbatim}
\paragraph{What scales with $n$.} The per-party Reshare cost scales near-linearly with $n_{\mathrm{old}}$ (each old qualified-subset member emits one $g_i(X)$ polynomial; each new party aggregates $|Q|$ polynomials). The activation cert cost scales near-linearly with $K = n_{\mathrm{new}}$ via the Sign1 + Sign2 + Combine pipeline.
\paragraph{What does not scale with $n$.} The GroupKey itself ($\mathbf{A}$, $\tilde{\mathbf{b}}$ bytes) remains byte-identical across reshare (Lemma~\ref{lem:gk-byte-identical} of \texttt{proofs/lss/lifecycle-safety.tex}). The lattice math is independent of validator-set turnover within an era; only the share distribution rotates.
\subsection{LSS-Lens resharing}
Lens resharing is the curve-side analog. Per-party time scales with curve scalar-mul cost ($\sim 75$ \textmu s per party on Ed25519 / secp256k1 / Ristretto255 baseline):
\begin{tabular}{@{}rrrr@{}}
\toprule
$\mathbf{n_{\mathrm{old}}}$ & $\mathbf{n_{\mathrm{new}}}$ & \textbf{Per-party Reshare (\textmu s)} & \textbf{Total wall-clock (ms)} \\
\midrule
3 & 3 & 250 & 0.7 \\
7 & 7 & 580 & 1.8 \\
21 & 21 & 1740 & 5.2 \\
64 & 64 & 5300 & 16.5 \\
128 & 128 & 10600 & 33.0 \\
\bottomrule
\end{tabular}
Lens reshare is $\sim 100$--$200\times$ faster than Pulsar reshare per party. The relative overhead is consistent with the Lens vs Pulsar single-sig ratio (Section~\ref{sec:microbenchmarks}). Reproduction: \texttt{cd threshold \&\& go test -bench=BenchmarkLensReshare/n=N ./protocols/lss/...}.
\subsection{Reshare with set rotation ($n_{\mathrm{old}} \neq n_{\mathrm{new}}$)}
A common deployment: a chain expands its validator set from $n=21$ to $n=64$ at an epoch boundary. The reshare runs from old qualified subset $|Q| = t_{\mathrm{old}}$ to the new $n_{\mathrm{new}}$ parties:
\begin{tabular}{@{}rrrr@{}}
\toprule
\textbf{Transition} & \textbf{Pulsar (ms)} & \textbf{Lens (ms)} & \textbf{Notes} \\
\midrule
$21 \to 21$ (refresh, same set) & 720 & 4.0 & HJKY97 zero-poly path \\
$21 \to 21$ (reshare, new set) & 960 & 5.2 & Desmedt--Jajodia path \\
$21 \to 64$ & 1820 & 11.0 & Expansion \\
$64 \to 21$ & 1080 & 6.5 & Contraction; only old $Q \subseteq O$ runs \\
$64 \to 128$ & 3460 & 22.0 & Expansion; large $n_{\mathrm{new}}$ \\
$128 \to 64$ & 1820 & 11.0 & Contraction \\
\bottomrule
\end{tabular}
Reproduction: \texttt{cd threshold \&\& go test -run TestDynamicResharePulsar/$\langle$transition$\rangle$ ./protocols/lss/...}.
\paragraph{The $K=21$ activation-cert latency line.} The activation cert at $K=21$ takes the same Sign1 + Sign2 + Combine pipeline as a routine $K=21$ Pulse (Section~\ref{sec:activation-cert-latency}). The path is identical; the message is canonical \texttt{QUASAR-PULSAR-ACTIVATE-v1} (Definition \texttt{def:pulsar-activate} of \texttt{proofs/definitions/algorithms.tex}).
\paragraph{Measured Pulsar end-to-end at $K=21$ (M1 Max, 2026-05-04 evening).}
A direct measurement of the Sign1 + Sign2 + Combine pipeline at $K = 21$
via \texttt{cd pulsar \&\& GOWORK=off go run . l 5 21}
(\texttt{LocalRun(5)}, $K = 21$):
\begin{center}
\begin{tabular}{@{}lrr@{}}
\toprule
\textbf{Phase} & \textbf{Mean (ms)} & \textbf{Median (ms)} \\
\midrule
\texttt{Gen} & 8.475 & 8.475 \\
\texttt{SignRound1} (per party) & 40.789 & 40.742 \\
\texttt{SignRound2Preprocess} (per party) & 112.869 & 112.541 \\
\texttt{SignRound2} (per party) & 6.532 & 6.532 \\
\texttt{SignFinalize} & 0.090 & --- \\
\texttt{Verify} & 0.950 & --- \\
\texttt{Total Signing} (per party) & 160.190 & 159.987 \\
\bottomrule
\end{tabular}
\end{center}
The activation-cert latency at $K = 21$ is therefore $\approx 160$ ms
of single-core CPU time per party plus the standard $\sim 0.95$ ms
verify, dominated by Round 2 Preprocess. With 21 parties signing in
parallel on independent cores, the wall-clock equals one party's
total signing time plus the Combine step ($\sim 0.09$ ms) plus 2 WAN
RTTs (Section~\ref{sec:cross-wan-latency}). Stability check: a
second run at $K=21$ produced \texttt{Total Signing} mean $159.879$ ms
(\,$\Delta < 0.5\%$).
\subsection{KAT regeneration time}
The pulsar canonical regen-kats script (\texttt{pulsar/scripts/regen-kats.sh}) regenerates every KAT consumed by the C++ / GPU ports. The full run takes \textbf{58.86\,s wall-clock on M1 Max} (Darwin 25.4.0 arm64, Go 1.26.2, 2026-05-04 evening measurement, post-fuzz battery, CPU quiescent; \texttt{75.26\,s user + 7.00\,s system, 139\% CPU}), dominated by the 16-entry \texttt{corona\_oracle\_v2} KAT batch (\texttt{TestEmitDeterministic} + \texttt{TestSignVerifyEntries} + \texttt{TestShamirRoundTrip}):
\begin{center}
\begin{tabular}{@{}lr@{}}
\toprule
\textbf{KAT step} & \textbf{Wall-clock (M1 Max, 2026-05-04 evening)} \\
\midrule
reshare\_oracle (1 KAT) & $\sim 4$ s \\
dkg2\_oracle (1 KAT) & $\sim 8$ s \\
activation\_oracle (1 KAT) & $\sim 6$ s \\
corona\_oracle\_v2 emit + tests (7 + sign-verify-e2e at 16 reps) & $\sim 41$ s \\
hash NIST KAT verification & $< 1$ s \\
manifest sha256 & $< 0.1$ s \\
\midrule
\textbf{Total wall-clock} & \textbf{58.86 s} \\
\bottomrule
\end{tabular}
\end{center}
The \texttt{corona\_oracle\_v2} run is the long pole. The 7 emitted entries plus the 16-rep sign-verify-e2e ceremony cover the Pulsar wire format end-to-end: PRNG / BLAKE2-XOF derivation, discrete-Gaussian samples, Montgomery NTT round-trip, structs Matrix wire bytes, transcript hash, Shamir share, sign-verify-e2e. Each entry is byte-deterministic; the entire run produces output that hashes byte-equal across runs (manifest \texttt{regen-kats.manifest.sha256} re-derives identically).
Reproduction: \texttt{cd pulsar \&\& ./scripts/regen-kats.sh}; verification: \texttt{./scripts/regen-kats.sh --verify}.
\subsection{Snapshot and rollback}
LSS snapshot management (\texttt{threshold/protocols/lss/rollback.go}) persists a configurable rolling window of share states. Snapshot save and rollback latencies:
\begin{tabular}{@{}lrr@{}}
\toprule
\textbf{Operation} & \textbf{$n=21$} & \textbf{$n=128$} \\
\midrule
Snapshot save (encrypt + write to disk) & 3 ms & 18 ms \\
Rollback (read + decrypt) & 4 ms & 22 ms \\
Snapshot retention window (default) & 8 generations & 8 generations \\
\bottomrule
\end{tabular}
The snapshot manager retains 8 generations by default (configurable via chain governance). Rollback time is dominated by the share-state encryption decrypt + JSON unmarshal; the rollback marker bookkeeping is sub-millisecond.
\subsection{Wall-clock vs CPU time}
The reshare benchmarks here measure local-loop simulation (no WAN delay). Production resharing on Lux mainnet runs over WAN with $\sim 200\,\mathrm{ms}$ inter-party RTT; the Pulsar Sign1 + Sign2 + Combine pipeline requires 2 WAN crossings, so production wall-clock is local-loop wall-clock plus $\sim 400\,\mathrm{ms}$ for $K=21$. The cross-WAN projection table (Section~\ref{sec:cross-wan-latency}) accounts for this.