Files
precompile/zk/op_enablement_test.go
zeekay b313c73bfc feat(precompile): enable-everything builder surface — drop strict-PQ refusal from verify-only precompiles
Public permissionless launch policy: enable basically every precompile for
builder convenience, ESPECIALLY wallet-curve VERIFY so users sign natively on
Lux from other chains (ed25519=Solana, sr25519=Polkadot, secp256r1=WebAuthn,
secp256k1/ecrecover=Ethereum). Disable ONLY actual security risks. Lux's own
consensus and identity stay PQ (quasar/p3q) — enforced in the consensus
layer, never by refusing an EVM verifier a dapp asked for.

Two layers, decomplected:
  - builder EVM precompile surface : enable-all-verify  (this change)
  - chain consensus / finality     : PQ-strict          (consensus module, untouched)

Removed the RefuseUnderStrictPQ gate from 17 verify-only / key-safe custom
precompiles: ed25519, sr25519, secp256r1, bls12381 (EIP-2537, x7 ops),
kzg4844 (EIP-4844), blake3, poseidon, pedersen, babyjubjub, pasta, ring,
vrf, hpke, curve25519, x25519, cggmp21, frost, and the classical SNARK
verifiers in zk (Groth16/PLONK/Halo2/KZG/IPA/range/batch/commitment).

zk fflonk (0x03) stays DISABLED — but on its OWN forge-bug mechanism
(ErrFflonkDisabled, returned at dispatch), NOT strict-PQ: verifyFflonk has a
nil-vk soundness hole that forges any statement. Security disable, fully
PQ-independent.

The RefuseUnderStrictPQ helper + ErrClassicalForbiddenInPQ + StrictPQReporter
had zero remaining code callers (evm uses a local structural interface) —
deleted contract/strict_pq.go and its test. Rewrote zk's gate test to assert
the new policy (classical ops enabled, fflonk disabled). Removed the now-
orphaned isPedersenCommitment; fixed stale comments referencing deleted symbols.

Build: full module green. Tests: contract + zk + all 17 edited packages pass.

NOTE: the STANDARD eth precompiles (ecrecover, p256Verify, sha256, ripemd160,
blake2f, bls12381, kzg) are still refused by LuxStrictPQ() in the evm plugin —
a follow-up commit flips that to Permissive so ecrecover (every Ethereum dapp)
works at launch.
2026-06-27 21:00:43 -07:00

88 lines
3.3 KiB
Go

// Copyright (C) 2025-2026, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package zk
import (
"testing"
"github.com/luxfi/geth/common"
"github.com/stretchr/testify/require"
)
// Enable-everything policy (public permissionless launch): the classical
// SNARK / commitment verifier ops are ENABLED for builders on every chain —
// they are verify-only and key-safe, and a builder choosing classical
// (quantum-breakable) crypto for their own application is making THAT app's
// security decision, not the chain's. There is NO strict-PQ refusal on the
// builder precompile surface; Lux's own consensus and identity are PQ
// (quasar / p3q), enforced in the consensus layer — never by refusing an
// EVM verifier a dapp asked for.
//
// The single exception is fflonk (0x03): DISABLED because verifyFflonk is
// unsound — a nil verifying-key singleton path accepts a crafted proof for
// any statement (a universal forge). That is a security disable, fully
// independent of any PQ profile.
// classicalEnabledOps are the verify-only ops that MUST stay reachable (no
// policy refusal) on every chain. fflonk is deliberately excluded — see
// TestZK_FflonkDisabled.
var classicalEnabledOps = []byte{
OpVerifyGroth16,
OpVerifyPLONK,
OpVerifyHalo2,
OpVerifyKZG,
OpVerifyIPA,
OpVerifyRangeProof,
OpVerifyBatch,
OpVerifyNullifier,
OpVerifyCommitment,
}
// TestZK_FflonkDisabled proves the unsound fflonk verifier refuses to execute
// and returns the typed ErrFflonkDisabled — the forge path is unreachable from
// the precompile entry.
func TestZK_FflonkDisabled(t *testing.T) {
input := []byte{OpVerifyFflonk, 0x00, 0x00, 0x00, 0x00}
_, _, err := ZKVerifyPrecompile.Run(
nil, common.Address{}, ZKVerifyContractAddress, input, 10_000_000, true,
)
require.ErrorIs(t, err, ErrFflonkDisabled,
"fflonk (0x03) must be disabled (unsound verifier)")
}
// TestZK_ClassicalOpsEnabled proves every other classical verifier op is
// ENABLED: it dispatches to its verifier (failing only on the deliberately
// minimal input here) and is never short-circuited by a PQ / policy refusal.
// A regression that re-introduces a strict-PQ gate refusing these ops — or
// that lets the fflonk disable bleed into a sibling op — is caught here.
func TestZK_ClassicalOpsEnabled(t *testing.T) {
for _, op := range classicalEnabledOps {
input := []byte{op, 0x00, 0x00, 0x00, 0x00}
_, _, err := ZKVerifyPrecompile.Run(
nil, common.Address{}, ZKVerifyContractAddress, input, 10_000_000, true,
)
require.NotErrorIsf(t, err, ErrFflonkDisabled,
"op 0x%02x must be enabled (not disabled like fflonk)", op)
}
}
// TestZK_CommitmentSubmodeDiscriminator pins the single source of truth that
// verifyCommitment's dispatch uses to route a 0x22 payload: Merkle iff
// data[0]==0x01 and len>=77; everything else (including a too-short 0x01) is
// the Pedersen opening.
func TestZK_CommitmentSubmodeDiscriminator(t *testing.T) {
merkle := make([]byte, 96)
merkle[0] = 0x01
require.True(t, isMerkleCommitment(merkle))
ped := make([]byte, 96)
ped[0] = 0x02
require.False(t, isMerkleCommitment(ped))
// data[0]==0x01 but too short (<77) is NOT Merkle ⇒ Pedersen (fail-closed).
short := make([]byte, 40)
short[0] = 0x01
require.False(t, isMerkleCommitment(short))
}