mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
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).
90 lines
3.8 KiB
Go
90 lines
3.8 KiB
Go
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||
// See the file LICENSE for licensing terms.
|
||
|
||
package fhe
|
||
|
||
import (
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/require"
|
||
)
|
||
|
||
// TestDoSAuditTable_QuasarEditionMainnetSafety walks every row in the
|
||
// cross-precompile DoS audit table and asserts the row's declared
|
||
// SafeAt12M matches the value computed from the row's gas + wall-clock
|
||
// budget at gasLimit=12M (real C-Chain mainnet from
|
||
// ~/work/lux/genesis/configs/mainnet/cchain.json).
|
||
//
|
||
// This is the regression guard against silent gas-cost drift. If a
|
||
// precompile's gas is later lowered such that it no longer fits the
|
||
// 12M-mainnet budget, this test surfaces the divergence loudly.
|
||
func TestDoSAuditTable_QuasarEditionMainnetSafety(t *testing.T) {
|
||
for _, row := range DoSAuditTable {
|
||
got := row.IsSafeAtGasLimit(MainnetCChainGasLimit)
|
||
require.Equal(t, row.SafeAt12M, got,
|
||
"%s: declared SafeAt12M=%v but computed %v "+
|
||
"(GasPerOp=%d, PerOpMs=%d, maxOpsPerBlock=%d, wall-clock-ms=%d, budget=%d)",
|
||
row.Precompile, row.SafeAt12M, got,
|
||
row.GasPerOp, row.PerOpMs,
|
||
MainnetCChainGasLimit/row.GasPerOp,
|
||
(MainnetCChainGasLimit/row.GasPerOp)*row.PerOpMs,
|
||
BlockComputeBudgetMs)
|
||
require.True(t, got,
|
||
"row %q must remain DoS-safe at 12M mainnet gasLimit; "+
|
||
"audit drift detected", row.Precompile)
|
||
}
|
||
}
|
||
|
||
// TestDoSAuditTable_PostFeeManagerSafety verifies the 20M
|
||
// post-feeConfigManagerConfig gasLimit also keeps every row within
|
||
// the per-block compute budget.
|
||
func TestDoSAuditTable_PostFeeManagerSafety(t *testing.T) {
|
||
for _, row := range DoSAuditTable {
|
||
require.True(t, row.IsSafeAtGasLimit(PostFeeManagerGasLimit),
|
||
"row %q must remain DoS-safe at 20M post-fee-manager gasLimit",
|
||
row.Precompile)
|
||
}
|
||
}
|
||
|
||
// TestDoSAuditTable_FHEActivationStillRefuses pins the inverse: the
|
||
// FHE rows look "safe" only because GasMul ≥ MainnetCChainGasLimit
|
||
// (their per-op gas exceeds the entire block budget), which means
|
||
// zero ops fit per block. The activation gate must still refuse
|
||
// rather than ship an unusable precompile silently.
|
||
//
|
||
// This is the regression that catches someone "fixing" the audit by
|
||
// lowering GasMul to make ops actually fit — that would make the row
|
||
// genuinely unsafe at 12M, and this test would flag the regression
|
||
// before the chain stalled.
|
||
func TestDoSAuditTable_FHEActivationStillRefuses(t *testing.T) {
|
||
// The activation gate refuses when gasLimit < GasMul × MinSafeGasMulRatio.
|
||
gateThreshold := GasMul * MinSafeGasMulRatio
|
||
require.Greater(t, gateThreshold, MainnetCChainGasLimit,
|
||
"FHE activation gate must refuse at 12M mainnet; "+
|
||
"otherwise a single Mul that fits in a block stalls the chain "+
|
||
"for %d ms wall-clock", WallClockMsMulUint8)
|
||
require.Greater(t, gateThreshold, PostFeeManagerGasLimit,
|
||
"FHE activation gate must refuse at 20M post-fee-manager")
|
||
require.Greater(t, gateThreshold, ReferenceGasLimit30M,
|
||
"FHE activation gate must refuse at 30M reference")
|
||
}
|
||
|
||
// TestDoSAuditTable_RowGasMatchesDeclaration cross-checks the rows
|
||
// against the actual constants. If GasMul is changed in contract.go
|
||
// without updating the audit table, this test fails.
|
||
func TestDoSAuditTable_RowGasMatchesDeclaration(t *testing.T) {
|
||
rowsByName := map[string]DoSAuditRow{}
|
||
for _, r := range DoSAuditTable {
|
||
rowsByName[r.Precompile] = r
|
||
}
|
||
|
||
require.Equal(t, GasAdd, rowsByName["fhe/add-uint8"].GasPerOp,
|
||
"fhe/add-uint8 row gas must equal the GasAdd constant")
|
||
require.Equal(t, GasMul, rowsByName["fhe/mul-uint8"].GasPerOp,
|
||
"fhe/mul-uint8 row gas must equal the GasMul constant")
|
||
require.Equal(t, WallClockMsAddUint8, rowsByName["fhe/add-uint8"].PerOpMs,
|
||
"fhe/add-uint8 row wall-clock ms must equal the measured constant")
|
||
require.Equal(t, WallClockMsMulUint8, rowsByName["fhe/mul-uint8"].PerOpMs,
|
||
"fhe/mul-uint8 row wall-clock ms must equal the measured constant")
|
||
}
|