L0 Bytecode / FROST
FROST L0 bytecode
FROST L0 bytecode covers a short transcript: nonce sampling, commitment emission, binding-factor computation, Lagrange coefficient computation, partial-signature production and verification, Schnorr aggregation, and transcript sealing.
S_beta = (Sigma, L, W_beta, R_beta, Q_beta); op_beta(C, roots, S_beta) -> (root, S_beta', effects)Intuition: FROST signing collects a one-time nonce commitment from each signer, binds those commitments into a single group nonce, and has each signer return a partial signature that the aggregator sums into one Schnorr signature. The instruction list below is that transcript expanded into per-message micro-ops, so each commitment, binding factor, partial signature, and seal is an explicit step with its own guard. Nonce handles are one-shot: once a nonce is consumed its handle is a terminal state, so a reuse attempt is rejected as a detectable error before a second signature is produced.
Each instruction shows descriptors for locality, phase, and stage. The uppercase badges state what the step does to state; locality remains the security-relevant descriptor for local, p2p, frontier, and evidence movement.
01nonce_i := sample_nonce(C, signer_index, one_shot=true)- Create a one-shot local nonce handle for the signer.
- Bind the nonce witness to signer set, threshold, lane, and execution context.
- Store nonce material inside the local FROST witness store.
02commit_i := Commit(nonce_i); commitment_cid := Sigma.commit(commit_i)- Emit the public nonce commitment artifact.
- Attach the commitment to the nonce_commit_local site output.
- Preserve the local nonce handle for partial signing.
03rho_i := H(binding_ctx, signer_set, commitment_set, message_digest, i)- Compute the message-bound FROST binding factor.
- Bind nonce commitments, signer identity, and challenge into the transcript.
- Feed the signer-local partial-signature equation.
04lambda_i := lagrange_coeff(i, signer_set)- Compute the local interpolation coefficient for the active signer set.
- Bind the coefficient to the threshold set used by the presign.
- Feed signer share weighting for the partial signature.
05part_i := schnorr_partial(e, nonce_i, rho_i, lambda_i, x_i)- Produce one signer partial signature.
- Consume the one-shot nonce handle.
- Commit partial-signature material and local consumed-marker state.
06ok_i := verify_partial(part_i, commitment_i, pubshare_i, e, lambda_i)- Verify one received partial signature against the signer commitment and public share.
- Create verification evidence for the aggregator path.
- Enter the aggregator pending frontier when collection is remote.
07partset := collect_threshold(valid_partials, signer_set, threshold)- Collect threshold-sized verified partial signatures.
- Bind the collection to signer set, threshold, and aggregation role.
- Create the input set for Schnorr aggregation.
08sigloc := finalize_schnorr(R_group, sum(part_i), binding_ctx)- Combine verified partial signatures into local signature material.
- Commit aggregator-local finalization state.
- Prepare the signature artifact consumed by the shared publication gate.
09seal := H_FROST(bind, prior_roots, emitted_root)- Seal nonce, partial-signature, or aggregation terminal roots.
- Provide the terminal projection root for FROST compiled regions.
- Keep freeze_presign[FROST1] and publish_sig as native L1 boundary sites around the L0 trace.
Opcode shape
Each boundary in the FROST transcript — nonce commit, presign binding, partial signature, aggregation, consumed marker, publication — has its own opcode. The bytecode makes those boundaries explicitly reviewable while nonce and share material stay local. Signer-local work produces nonce handles and partial signatures; aggregator work verifies and collects them before finalization; transcript seals close each compiled region.
Nonce handles are one-shot by construction. FROST nonce reuse reveals the secret share, so the architecture enforces single use structurally, ahead of protocol-level detection. Consuming a nonce handle removes it from local state, closing that lifecycle slot to any second use.
Effects
The bytecode creates local nonce handles, public commitment values, transcript binding factors, partial signatures, verified partial-signature sets, aggregate signature material, and transcript seals. freeze_presign[FROST1] and publish_sig remain native L1 boundary sites around the L0 trace, so DVM receipts, publication gating, and evidence packaging apply the same way they do for DKLS23.
One-shot nonce lifecycle
FROST nonce reuse reveals the secret share: reusing a nonce across two signing operations exposes it. The architecture enforces single use structurally, ahead of any protocol-level detection. A nonce handle moves through a fixed lifecycle: Sampled → Committed → Reserved → Consumed or Poisoned. When AdvanceStep consumes the nonce, the handle becomes Consumed and stays closed to any later presentation.
A nonce handle in the Consumed state is terminal. Any attempt to reuse it in a subsequent AdvanceStep produces STEP_PERMIT_REUSED, poisons the local FROST session, and creates an abort evidence record before any computation proceeds. The one-shot invariant lives at the bytecode layer, so the signing algorithm layer relies on it for that guarantee.
Public versus private data
FROST transcript boundaries separate what is public from what is local. Public nonce commitments, verified partial signatures, the Schnorr group nonce R, and the final signature bytes go into replicated state or are disclosed through the publication gate. Local nonce scalars, partial signing shares, and binding factor inputs stay in the FROST witness store behind the kernel boundary.
The transcript seal closes each compiled region — nonce, partial-sign, aggregation — and provides the root consumed by evidence packaging. Aggregators observe a verified partial-signature set; the nonce scalars that produced them stay local. The aggregator attests to the set membership, the group nonce, and the final Schnorr aggregate, which is sufficient for an external verifier.