Files
zeekay 6ca658f915 fix(accel): eliminate CPU/GPU verdict divergence in verify precompiles (consensus-split + forgery)
Under the enable-everything precompile policy these verify-only precompiles are
no longer refused under strict-PQ, so any divergence between the luxfi/accel
GPU/SIMD path and the pure-CPU path is a LIVE consensus split: GPU-equipped and
CPU-only validators would return different verify verdicts on the same input and
the chain forks. Several accel paths were also outright wrong-primitive forgery
surfaces.

LAW applied: the GPU verdict must be byte-identical to the CPU verdict on every
input, or the accel path is a consensus hazard and is removed. CPU (pure-Go,
CGO_ENABLED=0) is now the single source of truth for every on-chain verdict.
None of these GPU kernels can be proven byte-identical here, and several are
provably NOT, so every verdict-trusting accel branch is deleted and routed to
the canonical CPU verifier. Determinism over speed.

Active forgery/wrong-output in the DEFAULT build (accelcrypto CPU fallback
leaked non-CPU semantics, err==nil):
- sr25519: verified via the accel Ed25519 kernel -> a valid Ed25519 signature
  was ACCEPTED as a valid sr25519/Schnorrkel signature (wrong-curve forgery).
- blake3: accelcrypto.Hash(HashBlake3) returns BLAKE2b, not BLAKE3; the "real
  blake3" fallback was dead code. OpHash256 + MerkleRoot emitted BLAKE2b.

Latent splits (reachable only under -tags accel + a GPU backend):
- frost: FROST-Schnorr verified through the SigECDSA kernel (wrong primitive +
  32B x-only key mis-framed as a 33B ECDSA key).
- cggmp21: SigECDSA kernel truncated the 65B key to 33B and skipped the CPU
  path's recover-and-compare binding (ignored the v byte).
- ed25519: GPU Ed25519 kernel not proven byte-identical to the canonical
  verifier on cofactor / non-canonical-S / small-order edge cases.
- secp256r1: P-256 inputs sent to the fixed-curve secp256k1 ECDSA kernel.
- mldsa: GPU verified with EMPTY context; precompile binds the FIPS-204 ctx
  "lux-evm-precompile-mldsa-v1" -> different mu, different verdict.
- bls12381 / zk(groth16): GPU MSM result trusted without re-verification and
  with no curve binding carried to the kernel.
- kzg4844: GPU commitment used the backend's OWN embedded trusted setup (empty
  SRS) instead of the canonical EIP-4844 SRS -> breaks KZG binding.
- ai_mining (BatchVerifyMLDSA/BatchAccumulator): accel batch kernel truncates
  messages to 32 bytes and is mode-fixed -> diverges from VerifyMLDSA.
- fhe: GPU computed BFV arithmetic while CPU computes TFHE (different scheme,
  incompatible ciphertext, nil relin key) -> corrupt + divergent ciphertext.

Cleanups (no split, vestigial): removed dead mlkem encapsulateGPU; removed the
hpke GPU pre-warm that discarded its result and returned the CPU output anyway.
slhdsa was already CPU-only (prior identical fix).

Tests (differential / forgery proofs, all green CGO_ENABLED=0 and CGO -race):
- sr25519: a valid Ed25519 sig is now REJECTED; real sr25519 still verifies.
- blake3: OpHash256/MerkleRoot output == BLAKE3 and != BLAKE2b.
- cggmp21: flipped recovery byte is rejected (recover-compare authoritative).
- frost: a valid ECDSA sig is rejected as FROST-Schnorr.
- ed25519: verdict matches the canonical ed25519.Verify oracle over 64x4 vectors.
- mldsa: empty-ctx sig (the accel framing) rejected; precompileCtx sig accepted.

Net -593 LOC. No consensus code touched.
2026-06-27 19:46:11 -07:00
..

ML-KEM Precompile

Post-quantum key encapsulation mechanism precompile implementing FIPS 203 (ML-KEM).

Address

0x0200000000000000000000000000000000000007

Specification

See LP-4318: ML-KEM Post-Quantum Key Encapsulation

Operations

Encapsulate (0x01)

Generate a shared secret and ciphertext from a public key.

Input:

Offset Size Description
0 1 Operation (0x01)
1 1 Mode (0x00=512, 0x01=768, 0x02=1024)
2 varies Public key

Output:

Offset Size Description
0 varies Ciphertext
varies 32 Shared secret

Decapsulate (0x02)

Recover the shared secret from a ciphertext using a private key.

Input:

Offset Size Description
0 1 Operation (0x02)
1 1 Mode (0x00=512, 0x01=768, 0x02=1024)
2 varies Private key
varies varies Ciphertext

Output:

Offset Size Description
0 32 Shared secret

Key Sizes

Mode Public Key Private Key Ciphertext Shared Secret
ML-KEM-512 800 1632 768 32
ML-KEM-768 1184 2400 1088 32
ML-KEM-1024 1568 3168 1568 32

Gas Costs

Operation ML-KEM-512 ML-KEM-768 ML-KEM-1024
Encapsulate 50,000 75,000 100,000
Decapsulate 60,000 90,000 120,000

Security Levels

  • ML-KEM-512: 128-bit security (NIST Level 1)
  • ML-KEM-768: 192-bit security (NIST Level 3) - Recommended
  • ML-KEM-1024: 256-bit security (NIST Level 5)

Usage Example (Solidity)

import {MLKEMCaller} from "./IMLKEM.sol";

contract QuantumSecureExchange {
    using MLKEMCaller for *;

    function establishSecret(bytes calldata recipientPubKey)
        external view
        returns (bytes memory ciphertext, bytes32 sharedSecret)
    {
        return MLKEMCaller.encapsulate768(recipientPubKey);
    }

    function recoverSecret(
        bytes calldata privateKey,
        bytes calldata ciphertext
    ) external view returns (bytes32 sharedSecret) {
        return MLKEMCaller.decapsulate768(privateKey, ciphertext);
    }
}

References