mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
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.
88 lines
3.3 KiB
Go
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))
|
|
}
|