Files
zeekay aadb570cbd precompile/fhe: close Mul DoS — re-derive gas at real 12M mainnet limit + activation gate
The pre-2026 adversarial test sized the FHE Mul gas (750_000) against a
30M block gas limit, but the real Lux primary-network C-Chain genesis
gasLimit is 12_000_000 (genesis/configs/mainnet/cchain.json line 16).
At 750k gas/Mul + 12M block limit, 16 Muls fit per tx; at ~78s per Mul
on commodity ARM (Apple M1 Max, measured 2026-06-01), one tx stalls a
validator for >20 minutes against a 2s block-target. Sustained: chain
halt at <0.4 LUX cost.

Fix is two-axis and bench-driven:

1. Re-derive every FHE Gas* constant against measured wall-clock and a
   1ms-per-(12_000-gas) calibration. New GasMul = 936_000_000 (78s ×
   12k); GasAdd = 180_000_000 (15s × 12k); etc. These exceed the 12M
   block-gas-limit, which is the protection: zero Muls fit per block,
   so no DoS surface remains.

2. Add an activation gate in fhe.module.Configure that refuses fheConfig
   when the chain's effective gasLimit < GasMul × MinSafeGasMulRatio.
   The gate uses a new feature-detection interface
   contract.FeeConfigReporter (mirrors the StrictPQReporter pattern):
   ChainConfigs that implement it engage the gate; those that don't
   (cross-chain integrators) remain permissive. The error type is
   contract.ErrFHEUnsafeGasLimit.

Tests:
- adversarial_test.go reworked into three explicit fixtures:
  TestFHEGasCost_Mul_DoSResistance_{30M_Reference,12M_Mainnet,
  20M_PostFeeManager}. Each asserts max ops/block × per-op-ms <= budget.
- module_test.go: 7 activation-gate cases (refuses at 12M/20M/30M;
  permits at >= GasMul; permissive without FeeConfigReporter; nil chain
  config; wrong config type).
- dos_audit.go + dos_audit_test.go: in-tree cross-precompile DoS audit
  table walked by tests at both 12M and 20M gasLimit. ML-DSA, SLH-DSA,
  Magnetar, Pulsar, BLS12-381 pairing, ZK Groth16, FROST, CGGMP21, VRF,
  HQC, P3Q all measured safe; FHE rows priced out (the activation gate
  is what surfaces them explicitly).
- bench_test.go: BenchmarkFHE{Mul,Add}_Uint8 — the canonical
  measurement-derivation hooks. Re-run on each arch to regenerate
  WallClockMs* constants.
- contract/feeconfig_test.go: 8 cases for RequireGasLimit (no reporter;
  nil chainConfig; reporter above/at/below threshold; reporter
  abstains; time-aware feeConfigManagerConfig flip; nil block context).

The e2e/fhe_private_auction test was pinning the OLD gas constants
verbatim; updated to assert the bench-derived calibration so a future
constant drift surfaces as test failure rather than silent regression.

NO CAVEAT. Either the gas is sufficient (gasLimit >= GasMul ×
MinSafeGasMulRatio) OR the precompile refuses to activate. The FHE
precompile is currently in the "refuses to activate" regime on real
mainnet C-Chain (gasLimit=12M < 936M required). Recovery is operator-
side: raise gasLimit via feeConfigManagerConfig OR land a faster TFHE
evaluator (see memory/vulkan_tfhe_pbs_fast_path.md — 20.4x landed,
need 78x to close the gap).
2026-06-01 21:25:19 -07:00

55 lines
1.6 KiB
Go

//go:build cgo
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Benchmark suite measuring real wall-clock per FHE op on commodity hardware.
// Used to derive gas-cost floors for DoS resistance under realistic mainnet
// fee config (12M gas limit on Lux C-Chain).
//
// Re-run on each target arch to validate the gas-cost re-derivation:
// go test -run NONE -bench BenchmarkFHE -benchtime=1x ./fhe/...
package fhe
import (
"math/big"
"testing"
)
// BenchmarkFHEMul_Uint8 — schoolbook O(n^2) bootstrapping multiply.
// On Apple M1 Max (high-end ARM commodity): ~78 s/op. This sets the
// upper bound on per-op wall-clock and dominates the gas-floor derivation.
func BenchmarkFHEMul_Uint8(b *testing.B) {
if err := initTFHE(); err != nil {
b.Fatal(err)
}
ctA := tfheTrivialEncrypt(big.NewInt(3), TypeEuint8)
ctB := tfheTrivialEncrypt(big.NewInt(4), TypeEuint8)
b.ResetTimer()
for i := 0; i < b.N; i++ {
result := tfheMul(ctA, ctB, TypeEuint8)
if result == nil {
b.Fatal("nil result")
}
}
}
// BenchmarkFHEAdd_Uint8 — linear bootstrapping add. Two orders of magnitude
// faster than mul on the same parameters, but still dominated by FHE
// bootstrap latency.
func BenchmarkFHEAdd_Uint8(b *testing.B) {
if err := initTFHE(); err != nil {
b.Fatal(err)
}
ctA := tfheTrivialEncrypt(big.NewInt(3), TypeEuint8)
ctB := tfheTrivialEncrypt(big.NewInt(4), TypeEuint8)
b.ResetTimer()
for i := 0; i < b.N; i++ {
result := tfheAdd(ctA, ctB, TypeEuint8)
if result == nil {
b.Fatal("nil result")
}
}
}