mirror of
https://github.com/luxfi/chains.git
synced 2026-09-24 00:26:00 +00:00
chains/zkvm: Z-Chain rollup proof verifier switched Groth16 -> STARK/FRI (P3Q)
Make the Z-Chain MLDSA-rollup verification path post-quantum. The rollup aggregates per-validator ML-DSA-65 sigs over the round digest; the proof was classical Groth16/bn254 (pairing-based, quantum-breakable) and is now a strict-PQ STARK/FRI proof (P3Q: Plonky3 fork, cSHAKE256 Merkle over Goldilocks, FRI; no KZG, no pairings, no trusted setup). VERIFIER (the clean half, done): - precompiles/zk_verifiers.go: STARKVerifier stub -> delegates to precompile/starkfri.Verify. Parses [proof_len][proof][pub_len][pub]; proof must carry the 'P3Q1' magic. FAILS CLOSED (errVerifierUnavailable, never resultValid) when no FRI binding is registered (CGO_ENABLED=0 / no -tags starkfri_p3q) — no forgery oracle. The classical Groth16Verifier/PLONKVerifier are KEPT intact at their own addresses. - precompiles/crosschain.go: route VerifierTypeSTARK to STARKVerifierAddr (was errNotImplemented). - tests: STARK now proven to delegate to starkfri (accept->0x01, reject->0x00, bad-magic rejected before callback, unbound fails closed) and to route cross-chain; Groth16/PLONK tests unchanged. PROVER (the honest remaining step, FLAGGED — NOT faked): - zwitness.go: MLDSAGroth16 -> MLDSAStark rename; doc now states the proof is STARK/FRI. Body stays ErrZWitnessNotImplemented: ~/work/lux/p3q exposes a Rust prover but NO p3q_prove C ABI, and has NO MLDSA-rollup AIR. Emitting fake proof bytes on the finality path would be a forgery oracle, so the prover binding is left as the outstanding step. go build ./zkvm/... + go test ./zkvm/precompiles/ green (CGO_ENABLED=0, GOWORK=off). Real cgo-tagged starkfri binding (-tags starkfri_p3q) compiles + KAT passes against the in-tree p3q staticlib.
This commit is contained in:
@@ -75,9 +75,11 @@ func (v *CrossChainZKVerifier) Run(input []byte) ([]byte, error) {
|
||||
|
||||
// Validate verifier type
|
||||
switch verifierType {
|
||||
case VerifierTypeGroth16, VerifierTypePLONK:
|
||||
// supported — route to Z-Chain
|
||||
case VerifierTypeSTARK, VerifierTypeHalo2, VerifierTypeNova:
|
||||
case VerifierTypeGroth16, VerifierTypePLONK, VerifierTypeSTARK:
|
||||
// supported — route to Z-Chain. STARK is the strict-PQ
|
||||
// (quantum-safe) rollup-proof path; Groth16/PLONK are the
|
||||
// classical pairing-based paths kept for non-strict-PQ chains.
|
||||
case VerifierTypeHalo2, VerifierTypeNova:
|
||||
return resultInvalid, errNotImplemented
|
||||
default:
|
||||
return resultInvalid, fmt.Errorf("unknown verifier type: 0x%02x", verifierType)
|
||||
@@ -91,6 +93,8 @@ func (v *CrossChainZKVerifier) Run(input []byte) ([]byte, error) {
|
||||
targetAddr = Groth16VerifierAddr
|
||||
case VerifierTypePLONK:
|
||||
targetAddr = PLONKVerifierAddr
|
||||
case VerifierTypeSTARK:
|
||||
targetAddr = STARKVerifierAddr
|
||||
}
|
||||
|
||||
msg := encodeWarpPayload(v.ZChainID, targetAddr, proofData)
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
|
||||
"github.com/consensys/gnark-crypto/ecc/bn254"
|
||||
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
|
||||
|
||||
"github.com/luxfi/precompile/starkfri"
|
||||
)
|
||||
|
||||
// Precompile addresses for Z-Chain ZK verifiers.
|
||||
@@ -35,12 +37,13 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
errInputTooShort = errors.New("input too short")
|
||||
errInvalidPoint = errors.New("invalid elliptic curve point")
|
||||
errSubgroupCheck = errors.New("point not in correct subgroup")
|
||||
errPairingFailed = errors.New("pairing computation failed")
|
||||
errProofInvalid = errors.New("proof verification failed")
|
||||
errNotImplemented = errors.New("verifier not yet available")
|
||||
errInputTooShort = errors.New("input too short")
|
||||
errInvalidPoint = errors.New("invalid elliptic curve point")
|
||||
errSubgroupCheck = errors.New("point not in correct subgroup")
|
||||
errPairingFailed = errors.New("pairing computation failed")
|
||||
errProofInvalid = errors.New("proof verification failed")
|
||||
errNotImplemented = errors.New("verifier not yet available")
|
||||
errVerifierUnavailable = errors.New("strict-PQ STARK verifier binding not registered (build with -tags starkfri_p3q); failing closed")
|
||||
)
|
||||
|
||||
// result bytes returned by precompiles
|
||||
@@ -198,10 +201,34 @@ func (v *PLONKVerifier) Run(input []byte) ([]byte, error) {
|
||||
return resultValid, nil
|
||||
}
|
||||
|
||||
// --- STARK Verifier (stub) ---
|
||||
// --- STARK Verifier (strict-PQ STARK / FRI via P3Q) ---
|
||||
|
||||
// STARKVerifier will verify STARK proofs. Currently returns an error
|
||||
// indicating the verifier is not yet available.
|
||||
// STARKVerifier verifies post-quantum STARK / FRI proofs on Z-Chain by
|
||||
// delegating to the strict-PQ STARK verifier in precompile/starkfri
|
||||
// (a Plonky3 fork: cSHAKE256 Merkle commitments over the Goldilocks
|
||||
// 64-bit prime field, FRI low-degree test — NO KZG, NO pairings, NO
|
||||
// trusted setup). This is the quantum-safe replacement for the
|
||||
// pairing-based Groth16Verifier on the Z-Chain MLDSA-rollup path: a
|
||||
// quantum adversary that breaks bn254 cannot forge a STARK/FRI proof,
|
||||
// whose soundness rests only on the collision resistance of cSHAKE256
|
||||
// and the Reed–Solomon proximity gap (no algebraic-group assumption).
|
||||
//
|
||||
// Input format:
|
||||
//
|
||||
// proof_len (4 bytes, big-endian)
|
||||
// proof (proof_len bytes) — must begin with MagicHeader "P3Q1"
|
||||
// pub_len (4 bytes, big-endian)
|
||||
// inputs (pub_len bytes) — serialized public inputs
|
||||
//
|
||||
// Output: 0x01 if valid, 0x00 if invalid.
|
||||
//
|
||||
// Verifier binding. The actual FRI verifier runs out-of-band (Rust,
|
||||
// behind the `starkfri_p3q` cgo build tag) and self-registers via
|
||||
// starkfri.RegisterVerifier. When no verifier is bound (CGO_ENABLED=0
|
||||
// or the tag is absent) starkfri.Verify returns ErrVerifierNotRegistered
|
||||
// and this precompile FAILS CLOSED — it returns errVerifierUnavailable
|
||||
// and NEVER accepts an unverified proof. There is no forgery oracle in
|
||||
// the unbound configuration.
|
||||
type STARKVerifier struct{}
|
||||
|
||||
func (v *STARKVerifier) RequiredGas(input []byte) uint64 {
|
||||
@@ -209,7 +236,41 @@ func (v *STARKVerifier) RequiredGas(input []byte) uint64 {
|
||||
}
|
||||
|
||||
func (v *STARKVerifier) Run(input []byte) ([]byte, error) {
|
||||
return resultInvalid, errNotImplemented
|
||||
// Parse [proof_len(4)][proof][pub_len(4)][pub].
|
||||
if len(input) < 8 {
|
||||
return resultInvalid, errInputTooShort
|
||||
}
|
||||
proofLen := binary.BigEndian.Uint32(input[:4])
|
||||
off := uint32(4)
|
||||
if uint32(len(input)) < off+proofLen+4 {
|
||||
return resultInvalid, errInputTooShort
|
||||
}
|
||||
proof := input[off : off+proofLen]
|
||||
off += proofLen
|
||||
|
||||
pubLen := binary.BigEndian.Uint32(input[off : off+4])
|
||||
off += 4
|
||||
if uint32(len(input)) < off+pubLen {
|
||||
return resultInvalid, errInputTooShort
|
||||
}
|
||||
pub := input[off : off+pubLen]
|
||||
|
||||
ok, err := starkfri.Verify(proof, pub)
|
||||
if err != nil {
|
||||
// ErrVerifierNotRegistered (no cgo binding) or an FFI/decode
|
||||
// failure. Either way the proof is NOT verified: fail closed.
|
||||
// We distinguish "binding pending" from a malformed proof so an
|
||||
// operator can tell a deployment-config gap from a bad proof,
|
||||
// but in neither case do we return resultValid.
|
||||
if errors.Is(err, starkfri.ErrVerifierNotRegistered) {
|
||||
return resultInvalid, errVerifierUnavailable
|
||||
}
|
||||
return resultInvalid, nil // malformed / non-verifying proof is not an execution error
|
||||
}
|
||||
if !ok {
|
||||
return resultInvalid, nil
|
||||
}
|
||||
return resultValid, nil
|
||||
}
|
||||
|
||||
// --- Halo2 Verifier (stub) ---
|
||||
|
||||
@@ -6,12 +6,14 @@ package precompiles
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/consensys/gnark-crypto/ecc/bn254"
|
||||
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/precompile/starkfri"
|
||||
)
|
||||
|
||||
func TestGroth16VerifierGas(t *testing.T) {
|
||||
@@ -106,8 +108,8 @@ func TestGroth16VerifierValidProof(t *testing.T) {
|
||||
proofC.ScalarMultiplication(&pubLC, &gammaInvDeltaBI)
|
||||
proofC.Neg(&proofC)
|
||||
|
||||
proofA := vkAlpha // alpha * g1
|
||||
proofB := vkBeta // beta * g2
|
||||
proofA := vkAlpha // alpha * g1
|
||||
proofB := vkBeta // beta * g2
|
||||
|
||||
// Verify the pairing equation holds (sanity check before serializing)
|
||||
{
|
||||
@@ -187,11 +189,99 @@ func TestPLONKVerifierGas(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSTARKVerifierNotImplemented(t *testing.T) {
|
||||
// buildSTARKInput serialises [proof_len(4)][proof][pub_len(4)][pub] in
|
||||
// the wire format the STARKVerifier precompile expects.
|
||||
func buildSTARKInput(proof, pub []byte) []byte {
|
||||
out := make([]byte, 0, 4+len(proof)+4+len(pub))
|
||||
var l [4]byte
|
||||
binary.BigEndian.PutUint32(l[:], uint32(len(proof)))
|
||||
out = append(out, l[:]...)
|
||||
out = append(out, proof...)
|
||||
binary.BigEndian.PutUint32(l[:], uint32(len(pub)))
|
||||
out = append(out, l[:]...)
|
||||
out = append(out, pub...)
|
||||
return out
|
||||
}
|
||||
|
||||
// TestSTARKVerifierFailsClosedWhenUnbound proves the Z-Chain STARK
|
||||
// rollup verifier delegates to precompile/starkfri and FAILS CLOSED
|
||||
// when no verifier binding is registered (the CGO_ENABLED=0 / no
|
||||
// starkfri_p3q build). A structurally well-formed P3Q1 proof must NOT
|
||||
// be accepted in the unbound configuration — there is no forgery
|
||||
// oracle. This is the post-quantum-safe posture: absent the FRI
|
||||
// verifier, the precompile refuses rather than rubber-stamps.
|
||||
func TestSTARKVerifierFailsClosedWhenUnbound(t *testing.T) {
|
||||
starkfri.RegisterVerifier(nil) // ensure no binding
|
||||
v := &STARKVerifier{}
|
||||
_, err := v.Run([]byte{0x00})
|
||||
if err != errNotImplemented {
|
||||
t.Fatalf("expected errNotImplemented, got: %v", err)
|
||||
|
||||
proof := append([]byte(starkfri.MagicHeader), []byte("strict-PQ-FRI-Goldilocks-payload")...)
|
||||
pub := []byte{0x01, 0x02, 0x03, 0x04}
|
||||
res, err := v.Run(buildSTARKInput(proof, pub))
|
||||
|
||||
if !errors.Is(err, errVerifierUnavailable) {
|
||||
t.Fatalf("expected errVerifierUnavailable (fail-closed), got: %v", err)
|
||||
}
|
||||
if len(res) != 1 || res[0] != 0x00 {
|
||||
t.Fatalf("unbound STARK verifier must return 0x00 (invalid), got: %x", res)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSTARKVerifierDelegatesToStarkFRI proves the verifier-side switch:
|
||||
// the Z-Chain rollup STARK path routes through starkfri.Verify (NOT the
|
||||
// pairing-based Groth16 path). We register a fake FRI verifier and
|
||||
// assert (a) it is invoked with the proof/pub bytes the precompile
|
||||
// parsed, (b) an accept yields 0x01, (c) a reject yields 0x00, and
|
||||
// (d) a proof without the "P3Q1" magic is rejected by starkfri's
|
||||
// structural pre-filter BEFORE the verifier callback runs.
|
||||
func TestSTARKVerifierDelegatesToStarkFRI(t *testing.T) {
|
||||
defer starkfri.RegisterVerifier(nil)
|
||||
v := &STARKVerifier{}
|
||||
|
||||
proof := append([]byte(starkfri.MagicHeader), []byte("trace-root|fri-root|queries")...)
|
||||
pub := []byte{0xaa, 0xbb, 0xcc}
|
||||
|
||||
// (a)+(b): accept path, capture what starkfri.Verify received.
|
||||
var sawProof, sawPub []byte
|
||||
starkfri.RegisterVerifier(func(_ byte, p, pi []byte) (bool, error) {
|
||||
sawProof = append([]byte(nil), p...)
|
||||
sawPub = append([]byte(nil), pi...)
|
||||
return true, nil
|
||||
})
|
||||
res, err := v.Run(buildSTARKInput(proof, pub))
|
||||
if err != nil {
|
||||
t.Fatalf("accept path returned error: %v", err)
|
||||
}
|
||||
if len(res) != 1 || res[0] != 0x01 {
|
||||
t.Fatalf("accept path must return 0x01 (valid), got: %x", res)
|
||||
}
|
||||
if string(sawProof) != string(proof) {
|
||||
t.Fatalf("starkfri verifier saw wrong proof bytes: %x != %x", sawProof, proof)
|
||||
}
|
||||
if string(sawPub) != string(pub) {
|
||||
t.Fatalf("starkfri verifier saw wrong public-input bytes: %x != %x", sawPub, pub)
|
||||
}
|
||||
|
||||
// (c): reject path.
|
||||
starkfri.RegisterVerifier(func(byte, []byte, []byte) (bool, error) { return false, nil })
|
||||
res, err = v.Run(buildSTARKInput(proof, pub))
|
||||
if err != nil {
|
||||
t.Fatalf("reject path must not surface an execution error, got: %v", err)
|
||||
}
|
||||
if len(res) != 1 || res[0] != 0x00 {
|
||||
t.Fatalf("reject path must return 0x00 (invalid), got: %x", res)
|
||||
}
|
||||
|
||||
// (d): bad-magic proof is rejected by starkfri's structural filter
|
||||
// before the callback runs (the callback would say true otherwise).
|
||||
called := false
|
||||
starkfri.RegisterVerifier(func(byte, []byte, []byte) (bool, error) { called = true; return true, nil })
|
||||
badProof := append([]byte("BAD!"), []byte("not-a-p3q1-proof")...)
|
||||
res, _ = v.Run(buildSTARKInput(badProof, pub))
|
||||
if len(res) != 1 || res[0] != 0x00 {
|
||||
t.Fatalf("bad-magic proof must return 0x00 (invalid), got: %x", res)
|
||||
}
|
||||
if called {
|
||||
t.Fatal("starkfri callback must not run on a proof missing the P3Q1 magic header")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,7 +381,9 @@ func TestCrossChainVerifierRejectsTooShort(t *testing.T) {
|
||||
|
||||
func TestCrossChainVerifierRejectsStubs(t *testing.T) {
|
||||
v := &CrossChainZKVerifier{ZChainID: ids.ID{}}
|
||||
for _, vtype := range []byte{VerifierTypeSTARK, VerifierTypeHalo2, VerifierTypeNova} {
|
||||
// STARK is no longer a stub — it routes to the strict-PQ STARK
|
||||
// verifier on Z-Chain. Only Halo2/Nova remain unimplemented.
|
||||
for _, vtype := range []byte{VerifierTypeHalo2, VerifierTypeNova} {
|
||||
result, err := v.Run([]byte{vtype, 0x00})
|
||||
if err != errNotImplemented {
|
||||
t.Fatalf("type 0x%02x: expected errNotImplemented, got %v", vtype, err)
|
||||
@@ -302,6 +394,33 @@ func TestCrossChainVerifierRejectsStubs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestCrossChainVerifierRoutesSTARK proves the cross-chain router now
|
||||
// forwards strict-PQ STARK rollup proofs to the Z-Chain STARKVerifier
|
||||
// (target address STARKVerifierAddr), instead of rejecting them as
|
||||
// not-implemented. The quantum-safe rollup path is reachable from any
|
||||
// EVM chain via Warp.
|
||||
func TestCrossChainVerifierRoutesSTARK(t *testing.T) {
|
||||
var zID ids.ID
|
||||
rand.Read(zID[:])
|
||||
v := &CrossChainZKVerifier{ZChainID: zID}
|
||||
|
||||
proofData := []byte("p3q-stark-proof-bytes")
|
||||
msg, err := v.Run(append([]byte{VerifierTypeSTARK}, proofData...))
|
||||
if err != nil {
|
||||
t.Fatalf("STARK routing returned error: %v", err)
|
||||
}
|
||||
_, gotAddr, gotPayload, err := DecodeWarpPayload(msg)
|
||||
if err != nil {
|
||||
t.Fatalf("decode warp payload: %v", err)
|
||||
}
|
||||
if gotAddr != STARKVerifierAddr {
|
||||
t.Fatalf("STARK must route to STARKVerifierAddr 0x%02x, got 0x%02x", STARKVerifierAddr, gotAddr)
|
||||
}
|
||||
if string(gotPayload) != string(proofData) {
|
||||
t.Fatalf("STARK routed payload mismatch: %x != %x", gotPayload, proofData)
|
||||
}
|
||||
}
|
||||
|
||||
// --- helpers ---
|
||||
|
||||
func serializeGroth16Input(vk *groth16VK, proof *groth16Proof, witness []fr.Element) []byte {
|
||||
|
||||
+55
-22
@@ -1,7 +1,7 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
//
|
||||
// Z-Chain MLDSAGroth16 witness producer for Quasar parallel-witness
|
||||
// Z-Chain MLDSAStark witness producer for Quasar parallel-witness
|
||||
// finality (LP-020 §2 Consensus Modes, witness set bit WitnessZ).
|
||||
//
|
||||
// The Z-Chain VM is the parallel finality witness for the Z lane. Each
|
||||
@@ -9,15 +9,43 @@
|
||||
// the canonical validator ML-DSA-65 public-key list (rooted in
|
||||
// pchain_validator_root) to this adapter. The adapter collects per-
|
||||
// validator ML-DSA-65 signatures over the digest and produces a single
|
||||
// Groth16/bn254 proof attesting:
|
||||
// post-quantum STARK / FRI proof attesting:
|
||||
//
|
||||
// ∀ i ∈ [N]: ML-DSA.Verify(pk_i, digest, σ_i) = 1
|
||||
//
|
||||
// The proof is verified on-chain by the existing Groth16 precompile in
|
||||
// chains/zkvm/precompiles/zk_verifiers.go (Groth16Verifier). The circuit
|
||||
// itself is NOT implemented in this pass; producing real witnesses
|
||||
// requires circuit design, trusted-setup ceremony, and prover infra
|
||||
// (LP-020 §6, proofs/quasar-cert-soundness.tex App. B).
|
||||
// The proof is a strict-PQ STARK (a Plonky3 fork: cSHAKE256 Merkle
|
||||
// commitments over the Goldilocks 64-bit prime field, FRI low-degree
|
||||
// test — NO KZG, NO pairings, NO trusted setup). It is verified on-chain
|
||||
// by the STARK verifier (chains/zkvm/precompiles/zk_verifiers.go
|
||||
// STARKVerifier, which delegates to precompile/starkfri), NOT by the
|
||||
// pairing-based Groth16 precompile. This keeps the Z-lane witness
|
||||
// quantum-safe: a quantum adversary that breaks bn254 cannot forge the
|
||||
// aggregation proof, whose soundness rests only on cSHAKE256 collision
|
||||
// resistance and the Reed–Solomon proximity gap.
|
||||
//
|
||||
// PROVER STATUS — NOT YET WIRED (the honest remaining step).
|
||||
// ----------------------------------------------------------
|
||||
// Producing real witnesses requires:
|
||||
//
|
||||
// (1) the MLDSA-rollup AIR/circuit: an arithmetization of
|
||||
// "∀ i: ML-DSA.Verify(pk_i, digest, σ_i) = 1" over the Goldilocks
|
||||
// field. This circuit does NOT exist in ~/work/lux/p3q today
|
||||
// (the p3q-zchain crate has DKG / committee / rotation logic but
|
||||
// no validator-signature-aggregation AIR), and
|
||||
// (2) a prover C ABI: the p3q workspace exposes a Rust-level prover
|
||||
// (p3q-stark::Prover trait, p3q-fri), but its C ABI (p3q-c-abi)
|
||||
// exports ONLY `p3q_verify` — there is NO `p3q_prove` entry point
|
||||
// for the Go consensus layer to invoke over cgo.
|
||||
//
|
||||
// Until both land, Witness returns ErrZWitnessNotImplemented and the
|
||||
// round driver finalizes at the next-lower witness level (PolicyQuorum
|
||||
// or PolicyPQ). The VERIFIER side and the proof-type plumbing are
|
||||
// already STARK/FRI (see STARKVerifier); only the prover binding is
|
||||
// outstanding. We intentionally do NOT fake a prover: emitting bytes
|
||||
// that "look like" a proof but carry no soundness would be a forgery
|
||||
// oracle on the finality path. See LP-020 §6 and
|
||||
// proofs/quasar-cert-soundness.tex App. B for the AIR constraint-count
|
||||
// and prover-cost analysis.
|
||||
//
|
||||
// The method signature matches consensus/protocol/quasar.ZWitnessProducer
|
||||
// (Witness(ctx, [32]byte, [][]byte) ([]byte, error)) so this adapter
|
||||
@@ -31,13 +59,14 @@ import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ZWitnessAdapter adapts the Z-Chain MLDSAGroth16 prover to the consensus
|
||||
// ZWitnessAdapter adapts the Z-Chain MLDSAStark prover to the consensus
|
||||
// ZWitnessProducer interface used by the Quasar round driver.
|
||||
//
|
||||
// TODO(pqz-circuit): implement the MLDSAGroth16 R1CS circuit, run trusted
|
||||
// setup, integrate the prover. Until then Witness returns
|
||||
// ErrZWitnessNotImplemented and the round driver finalizes at the next
|
||||
// lower witness level (PolicyQuorum or PolicyPQ).
|
||||
// TODO(pqz-circuit): implement the MLDSAStark rollup AIR (Goldilocks)
|
||||
// and add a `p3q_prove` C ABI to ~/work/lux/p3q, then integrate the
|
||||
// prover here. Until then Witness returns ErrZWitnessNotImplemented and
|
||||
// the round driver finalizes at the next lower witness level
|
||||
// (PolicyQuorum or PolicyPQ). The verifier side is already STARK/FRI.
|
||||
type ZWitnessAdapter struct {
|
||||
vm *VM
|
||||
}
|
||||
@@ -49,20 +78,24 @@ func NewZWitnessAdapter(vm *VM) *ZWitnessAdapter {
|
||||
}
|
||||
|
||||
// ErrZWitnessNotImplemented is returned by ZWitnessAdapter.Witness until
|
||||
// the MLDSAGroth16 circuit, trusted setup, and prover ship.
|
||||
var ErrZWitnessNotImplemented = errors.New("Z-Chain MLDSAGroth16 prover not implemented (LP-020 §6, paper App. B)")
|
||||
// the MLDSAStark rollup AIR and the p3q prover C ABI ship. The verifier
|
||||
// side (STARKVerifier → precompile/starkfri) is already wired; this is
|
||||
// the prover-binding gap only.
|
||||
var ErrZWitnessNotImplemented = errors.New("Z-Chain MLDSAStark prover not implemented: needs the MLDSA-rollup AIR + a p3q_prove C ABI (LP-020 §6, paper App. B)")
|
||||
|
||||
// Witness produces a Groth16 proof aggregating per-validator ML-DSA-65
|
||||
// signatures over the round digest. Signature matches
|
||||
// consensus/protocol/quasar.ZWitnessProducer.
|
||||
// Witness produces a post-quantum STARK / FRI proof aggregating per-
|
||||
// validator ML-DSA-65 signatures over the round digest. Signature
|
||||
// matches consensus/protocol/quasar.ZWitnessProducer.
|
||||
//
|
||||
// validatorMLDSAPubs is the canonical ML-DSA-65 public-key list rooted in
|
||||
// pchain_validator_root for the round; the Groth16 circuit takes this
|
||||
// list as a public input.
|
||||
// pchain_validator_root for the round; the STARK circuit takes this list
|
||||
// as a public input.
|
||||
//
|
||||
// Returns ErrZWitnessNotImplemented today; the round driver treats this
|
||||
// as the witness being unavailable and finalizes at the next-lower
|
||||
// witness level.
|
||||
// Returns ErrZWitnessNotImplemented today (prover binding outstanding —
|
||||
// see the package comment); the round driver treats this as the witness
|
||||
// being unavailable and finalizes at the next-lower witness level. We do
|
||||
// NOT return fabricated proof bytes: an unsound "proof" on the finality
|
||||
// path would be a forgery oracle.
|
||||
func (a *ZWitnessAdapter) Witness(ctx context.Context, digest [32]byte, validatorMLDSAPubs [][]byte) ([]byte, error) {
|
||||
_ = ctx
|
||||
_ = digest
|
||||
|
||||
Reference in New Issue
Block a user