mirror of
https://github.com/lux-apps/papers.git
synced 2026-10-30 18:15:21 +00:00
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.
163 lines
9.6 KiB
TeX
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.
|