Files
papers/lp-219-p3q-framework/sections/02-architectural-decision.tex
zeekayandHanzo Dev 178febad68 papers: reconcile T-Chain -> M-Chain / F-Chain / B-Chain per LP-134
Every rendered "T-Chain" / "ThresholdVM" / "teleportvm" reference across
the paper corpus is rewritten to the correct canonical chain by concern:
threshold signing / MPC / bridge custody / key recovery -> M-Chain (MVM);
FHE / encrypted compute / threshold decryption -> F-Chain (FVM); teleport
/ cross-chain messaging -> B-Chain (bridgevm); consensus Pulsar stays
Q-Chain. Section lux-trust-kernel/03-t-chain-threshold-governance.tex
renamed -> 03-f-chain-encrypted-policy.tex (with \input + xref fixes).
Braided "T-Chain" sections split by concern (M vs F). Only lp-020's
chain-separation rationale and the shared lux/chains/thresholdvm library
path retain the name, to record the removal. All changed papers rebuilt
(pdflatex, rc=0, no undefined refs).

ยง09 of lux-dao-governance-framework reframed: fabricated "production
deployment" metrics ($50M/$240k/$12M, survey %s, "as of Oct 2024")
relabeled as illustrative design configurations with no invented
replacements; reality noted (DAO treasury is a 1/1 Safe); conclusion
echo + one "first production" overclaim softened.

This commit also captures concurrent corpus workstreams on the same tree.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-02 00:57:09 -07:00

192 lines
7.9 KiB
TeX

\section{Architectural Decision: Single Slot, Kind-Byte Dispatch}
\label{sec:architectural-decision}
\subsection{The prior sibling-slots model}
The pre-decomplection draft of LP-219 (dated 2026-05-29)
allocated three EVM precompile slots:
\begin{itemize}[leftmargin=2em,nosep]
\item \texttt{0x012205} -- P3Q-Pulsar, wrapping the rollup-batch
ABI around the Pulsar threshold-FIPS-204 verifier core.
\item \texttt{0x012206} -- P3Q-Corona, wrapping the rollup-batch
ABI around the Corona Module-LWE threshold verifier
core.
\item \texttt{0x012207} -- P3Q-Magnetar, wrapping the rollup-
batch ABI around the Magnetar threshold-SLH-DSA verifier core.
\end{itemize}
Each slot reused the corresponding LP-0120 raw-primitive verifier
as the underlying implementation (\texttt{0x012204} raw Pulsar,
\texttt{0x012206} raw Corona, \texttt{0x012207} raw Magnetar) and
layered a rollup-batch-specific wrapper on top: structural
parsing for the \texttt{(prev\_root, new\_root, batch\_hash)}
tuple, a separate FIPS context string per slot, separate gas
calibration billed inside each wrapper.
The collision between LP-219's rollup-batch wrappers and
LP-0120's raw-primitive verifiers at slots \texttt{0x012206} and
\texttt{0x012207} was the first symptom that the design was
overcomplecting two concerns:
\begin{enumerate}[leftmargin=2em,nosep]
\item ``Verify a threshold signature of family $F$ over a 32-byte
digest'' (the raw primitive concern, owned by LP-0120).
\item ``Verify a rollup-batch envelope of family $F$ where the
digest is $H(\text{prev\_root} \,\|\, \text{new\_root} \,\|\,
\text{batch\_hash})$ and the FIPS context binds the rollup
pattern'' (the rollup-batch concern, owned by LP-218 /
LP-219).
\end{enumerate}
These are two distinct concerns. They deserve two distinct
surfaces. But they do not deserve six precompile slots between
them.
\subsection{The 2026-06-03 unification}
The decomplection allocates the rollup-batch concern exactly one
slot -- \texttt{0x012205} -- and reuses the existing raw-primitive
slots (\texttt{0x012204}, \texttt{0x012206}, \texttt{0x012207})
for the raw-primitive concern. The rollup-batch slot dispatches
on a leading \texttt{kind} byte to per-primitive verifier
implementations:
\begin{table}[h]
\centering
\small
\begin{tabular}{@{}llll@{}}
\toprule
\textbf{Slot} & \textbf{Concern} & \textbf{Wire} & \textbf{Owner} \\
\midrule
\texttt{0x012202} & Raw ML-DSA single-party & FIPS-204 & LP-0120 \\
\texttt{0x012203} & Raw SLH-DSA single-party & FIPS-205 & LP-0120 \\
\texttt{0x012204} & Raw Pulsar (Module-LWE threshold) & FIPS-204 ctx + sig & LP-0120 \\
\textbf{\texttt{0x012205}} & \textbf{P3Q family} (rollup-batch ABI) & \textbf{kind-byte dispatch} & \textbf{LP-218 + LP-219} \\
\texttt{0x012206} & Raw Corona (Module-LWE threshold) & Corona ctx + sig & LP-0120 \\
\texttt{0x012207} & Raw Magnetar (SLH-DSA threshold) & FIPS-205 ctx + sig & LP-0120 \\
\bottomrule
\end{tabular}
\caption{Precompile slot ownership after the 2026-06-03
decomplection. Slots \texttt{0x012204}, \texttt{0x012206},
\texttt{0x012207} remain owned by LP-0120 for the raw-primitive
non-rollup-batch use case; LP-218 + LP-219 own \texttt{0x012205}
as the P3Q rollup-batch family.}
\label{tab:slot-ownership}
\end{table}
A Solidity contract that needs Corona-kind verify for a
rollup-batch envelope calls
\texttt{P3Q.verify(0x02, sig, h, pk)} at \texttt{0x012205}. A
Solidity contract that needs raw Corona primitive verify -- a
non-rollup-batch use case, e.g.\ a custody attestation contract
verifying an off-chain Corona-signed certificate -- calls the
LP-0120 raw-primitive verifier at \texttt{0x012206} directly. Two
distinct callers, two distinct surfaces, two distinct slots,
zero overlap.
The P3Q family ABI is the single canonical entry point for
rollup-batch verification across all PQ kinds. This honors
LP-200~\S~``One and only one way to do everything''~\cite{lp200}.
\subsection{Kind-byte dispatch}
The unified \texttt{0x012205} precompile reads a 1-byte
\texttt{kind} discriminator at offset~0 of calldata and routes
to one of three (today) registered verifier implementations. The
dispatch is at runtime, populated by package
\texttt{init()} functions; the precompile entry-point is one
function in one file. The full dispatch logic from the reference
implementation at
\texttt{lux/precompile/p3q/contract.go::Run}:
\begin{lstlisting}[language={}]
kind := input[0]
switch kind {
case KindPulsar: // 0x01
// fall through to FIPS 204 ML-DSA verify path
case KindCorona, // 0x02
KindMagnetar: // 0x03
// Reserved kinds -- verifier not yet wired. Return abiFalse
// (the LP-218 "no revert on cryptographic failure" contract)
// so callers still see a consistent surface; once the
// lattice/hash threshold libraries land, this branch routes
// to its own verifier.
return abiFalse(), remaining, nil
default:
// Unknown kind -- abiFalse, same as any malformed input.
return abiFalse(), remaining, nil
}
\end{lstlisting}
Three properties hold by construction:
\begin{itemize}[leftmargin=2em,nosep]
\item \textbf{Constant-time over the kind byte.} The switch is on
a PUBLIC field (the calldata kind byte is observable on-chain).
Branching on it does not constitute a timing side channel
against any secret.
\item \textbf{No revert on cryptographic failure.} An unknown
kind, an unwired reserved kind, a malformed sig, a FIPS-204
reject -- all return \texttt{abiFalse} (a 32-byte
zero-encoded EVM \texttt{bool}) with no Go error. The Solidity
caller observes \texttt{false} via the return word and
decides whether to revert. This matches LP-218~\S~``Failure
modes.''
\item \textbf{Uniform gas billing.} The \texttt{RequiredGas}
function is monotonic in input length and does NOT depend on
the kind. A short malformed input pays the base gas, a
well-formed N-byte input pays \texttt{base + N * PerByteGas}.
No reduced price for early rejection -- uniform billing
prevents a gas-side channel from leaking which validation
stage rejected the input.
\end{itemize}
Per-kind gas \emph{rates} differ (Pulsar \SI{50000}{gas}, Corona
\SI{5000000}{gas}, Magnetar \SI{30000}{gas}); the resolution
happens inside the per-kind \texttt{P3QVerifier.GasCost()} method
described in section~\ref{sec:verifier-interface}. The
\texttt{0x012205} precompile multiplexes those rates over a
single observable address.
\subsection{What this is not}
It is worth being explicit about two design decisions LP-219
does \emph{not} make:
\begin{enumerate}[leftmargin=2em,nosep]
\item \textbf{It is not a generic PQ-signature precompile.} The
three kinds are not interchangeable. A Pulsar signature
submitted under \texttt{kind=0x02} (Corona) will fail
structurally -- Pulsar sigs are \SI{3309}{B}, Corona sigs are
$\sim\SI{33}{KB}$, and the verifier core for kind $0x02$ is the
Corona Module-LWE verifier (which does not parse FIPS-204
output). The kind byte selects a specific verifier; it does
not abstract over them.
\item \textbf{It is not a backwards-compatible upgrade of the
sibling-slots draft.} There is no LP-219-v1 deployment in the
wild; the activation marker
\texttt{2025-12-25T16:20:00-08:00} places the unified-slot
ABI at network genesis. Existing test corpora that target
\texttt{0x012206} / \texttt{0x012207} for rollup-batch wrappers
are obsolete (those slots are now LP-0120 raw-primitive
surfaces with different wire format); they must be re-targeted
to \texttt{0x012205} with the appropriate kind byte. No
compatibility shim exists.
\end{enumerate}
\subsection{Activation marker}
\begin{verbatim}
activates: 2025-12-25T16:20:00-08:00
activates-unix: 1766708400
\end{verbatim}
The P3Q family ABI at slot \texttt{0x012205} with kinds $0x01$,
$0x02$, $0x03$ registered in the dispatch table is callable from
the genesis block of the new final Lux network. Rollup
deployments that target PQ-strict or PQ-heavy mode call
\texttt{P3Q.verify(kind, ...)} from height~0 with the appropriate
kind byte (subject to per-kind verifier wiring, see
LP-220--LP-222).