Bytecode lowering
Bytecode lowering model
DVM represents each operation as bytecode: an ordered instruction stream that an interpreter advances over typed state. A workflow lowers through three layers — L2 product workflow, L1 semantic score, L0 backend bytecode — and the meaning of the operation lives in the instructions, while the cryptographic construction that realizes it lives one layer below. This representation is what makes an operation replayable, self-instrumenting, formally modelable, and portable across backends.
sign_step[AFF3]customer-facing request and receipt shape
share_mul / open_to / finalize_sig / publish_sigbackend-neutral operation contract
MRR21 -> SoftSpokenOT -> RVOLE -> consistency witnessfamily-specific trace, frontiers, handles, and witnesses
share_mulOT / SoftSpoken / RVOLEL1 share_mul remains stable; OT details are witness trace.nonce / partial / aggregateSchnorr transcript micro-opsNonce and partial signatures stay scoped until publish.share_mul / share_invPaillier-MtA / ZKPaillier details are backend-local; L1 observation remains stable.Why the bytecode representation
A cryptographic operation can be written as a hand-coded procedure or as data — an instruction stream the interpreter reads. DVM chooses the instruction stream, and that single choice yields four properties together. The operation replays from its recorded inputs, because the interpreter is a deterministic function of those inputs. It carries its own audit, evidence, and invariant-check steps inline, because those are instructions in the same stream as the protocol steps. It admits an executable formal semantics, because each instruction is a rewrite rule over the same state. And it tolerates backend substitution, because the instruction contract is fixed while the construction below it varies.
Each property follows from one fact: the instruction stream is the single source of truth for what the operation means. A reviewer reads the same artifact that the interpreter executes, the formal model rewrites, and the evidence chain references.
- Operation-as-data: the instruction stream is the executed, audited, and proven artifact.
- Replay, inline audit, formal semantics, and backend portability share one root cause.
- The construction (OT rounds, Paillier, Schnorr) sits one layer below the contract.
Content-addressed instructions enable replay
The DVM Execution section describes the interpreter as a deterministic step over typed state. The bytecode layer adds the property that makes that determinism useful: every value an instruction reads or writes carries a content-addressed id, so the inputs to a step are fixed by hash. Re-running an instruction over the same content-addressed inputs reproduces the same outputs and the same trace.
This is the mechanism behind replay, inspection, recovery, and evidence export. Each becomes an operation over recorded, hash-identified data: a reviewer reconstructs a step from its input ids, a recovery path resumes from the last committed instruction, and an evidence package references trace roots that any verifier recomputes.
The three layers
L2 names the product workflow: presign batch, signing step, aggregate signature, publish signature, rotate key, or enroll device. L1 names the backend-neutral semantic operations and their visibility rules — it is what the DVM interpreter evaluates. L0 bytecode records which backend path realized the L1 operation, including wire facts, local handles, and witness mechanics.
Each layer answers one question. L2 answers which workflow runs. L1 answers which semantic operation, operands, locality, and visibility apply. L0 answers which backend stages, wire facts, and witnesses realized it. Receipts record the backend profile and trace hashes, so a receipt traces back to its exact L0 path.
- L2: presign_batch, sign_step, agg_sig_at, publish_sig, rotate_key, enroll_device.
- L1: share_rand, share_mul, share_ec_mul, open_to, finalize_sig, publish_sig.
- L0: seed_ot, softspoken_ot, rvole, mul_share_finalize, consistency_check.
Instructions as the unit of composition
Because an operation is an instruction stream, audit-emit, invariant-check, evidence-publish, and log steps are opcodes in that stream alongside the protocol steps. A signing score and a key-refresh score both carry their instrumentation inline, at the round boundaries that matter, and the instrumentation shares the same execution and the same trace as the protocol it observes.
This is the operational reason key lifecycle operations reuse the signing machinery. DKG, refresh, reshare, and rotation are DVM scores, so they inherit step permits, one-shot artifact consumption, ingress validation, publication gating, evidence packaging, and telemetry as composed opcodes. The formal semantics and scenario tests cover a lifecycle score the same way they cover a signing score.
Same layers, family-specific alphabets
The three layers and the transition model are shared across every backend. The L0 opcode alphabet is family-specific. Every L0 instruction, in any family, is one transition over the same state shape — replicated state, local secret state, a family witness store, an ordered trace, and a pending peer frontier — and carries the same locality, phase, and effect taxonomy. The opcodes themselves differ because the constructions differ: DKLS23 names OT and RVOLE steps, FROST names nonce and partial-signature steps, CGGMP24 names Paillier and ZK-check steps.
A concrete example is Lagrange interpolation, the coefficient that weights a threshold share toward the group value. Every family uses it, yet it surfaces as an explicit opcode only in FROST (lagrange_coeff_local), because FROST selects the signer set per session and applies the coefficient inside the partial-signature equation at signing time. DKLS23 and CGGMP24 apply the equivalent weighting earlier — when the (t,n) shares are converted to additive shares for the chosen committee — so by the time their share_mul and MtA opcodes run, operands are already additive and the coefficient has been spent at a different boundary. Same algebra, different boundary, different opcode surface.
The boards below also show different scopes. The DKLS23 board lists the share_mul sub-alphabet (one L1 operation); the FROST board lists a full signing transcript; the CGGMP24 board lists presign plus sign. Reading them side by side compares an inner gadget against whole protocols, which is part of why their opcode lists look so different.
- Shared: the L2/L1/L0 layering, the transition shape, and the locality/phase/effect taxonomy.
- Family-specific: the L0 opcode names and the construction they realize.
- Lagrange weighting exists in all three; it is an explicit opcode only where it is applied online (FROST).
Stable versus variable
Across a backend upgrade, what stays constant: site identity, output visibility rules, publication discipline, receipt phase, policy binding, evidence chain shape. What can change: wire transcript mechanics, OT setup strategy, performance profile, and L0 opcode details. A deployment that upgrades from one backend to another keeps the same API, the same receipts, and the same audit vocabulary — while the cryptographic construction underneath changes.
Where each backend runs
The same L1 score maps onto different L0 backends, and the split between secret kernel and external runtime is what stays fixed. FROST keeps the secret nonce and the partial-signature step in the kernel; signer-set selection, the public transcript, and partial-signature aggregation run in the external runtime, with a nonce_handle as the one-shot artifact. DKLS23 keeps presign / offline OT artifacts and the local ECDSA share step in the kernel; transport, transcript assembly, public-data consistency checks, and final signature assembly run outside, with a presign_handle. CGGMP24 keeps the key and nonce lifecycle, secret MtA / MtM, and the local signing step in the kernel; transcript assembly, ZK-proof verification, and final aggregation run outside, also with a presign_handle.
Whatever the backend, only opaque descriptors and admissible public artifacts cross the boundary. The placement of the secret-bearing step is a property of the profile — fixed in the DomainManifest and constant within an epoch — which is why an L1 instruction like share_mul stays agnostic to the construction that realized it.
One-shot artifacts have a lifecycle
L0 execution consumes resources that are strictly single-use, and each has an explicit lifecycle the runtime can inspect. A presign artifact moves Seeded → Prepared → Reserved → Consumed or Expired. A permit_handle moves Issued → Active → Consumed, Expired, or Revoked. An mpc_session_handle moves Created → Active → PendingRemote, Finalized, Aborted, or Poisoned. Reuse is rejected: a replayed permit or a double-consumed presign poisons the local session and emits evidence — the one-shot invariant made operational at the bytecode layer.