params/extras_e2e_test: testnet upgrade.json regression gate (task #99)

Mirrors the mainnet UnmarshalJSON regression test for the lux-testnet
primary C-Chain. Embeds testnet_upgrade_v47.json (47 entries) and
asserts every key round-trips through extras.UpgradeConfig.UnmarshalJSON
against the live precompile registry.

If a future luxfi/precompile bump adds a new ConfigKey without the
corresponding RegisterModule(), or if the canonical
~/work/lux/genesis/configs/testnet/upgrade.json re-introduces an
unregistered EIP key (kzg4844Config / secp256r1Config / ed25519Config),
this test trips before the JSON ships to the cluster — closing the same
brick-on-boot footgun Red flagged on mainnet (vector 8 CRITICAL).

Sync rule: re-vendor testnet_upgrade_v47.json whenever the genesis
canonical changes — same pattern as mainnet_upgrade_v46.json.
This commit is contained in:
2026-06-02 18:37:48 -07:00
parent a527029da6
commit cd9d789287
2 changed files with 173 additions and 0 deletions
@@ -0,0 +1,87 @@
// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Testnet-side regression gate. Mirrors mainnet_upgrade_v46.json's
// upgrade_unmarshal_test.go (task #99) for the Lux primary testnet
// C-Chain upgrade schedule. The mainnet test wouldn't catch a testnet
// drift because the canonical fixture vendored there is only mainnet;
// this test pins the testnet shape so future precompile bumps can't
// silently brick a testnet validator that pulled the JSON before the
// matching evm-plugin rebuild reached the cluster.
package extras_e2e_test
import (
_ "embed"
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"github.com/luxfi/evm/params/extras"
// Side-effect import already brought in by upgrade_unmarshal_test.go,
// but we re-declare it here to make the file self-contained for
// readers who diff this file in isolation.
_ "github.com/luxfi/evm/precompile/registry"
)
// canonicalTestnetUpgradeV47 is the byte-for-byte vendored copy of
// luxfi/genesis configs/testnet/upgrade.json at the v47 precompile-set
// freeze (warpConfig + 19 forward-dated 1766708400 net-new + 27 more
// safe-subset cryptography precompiles also at 1766708400). The
// testnet schedule front-loads all activations to the strict-PQ
// timestamp because there is no live testnet validator state to keep
// backwards-compatible against — it's the experimentation lane.
//
// Sync contract: if luxfi/genesis configs/testnet/upgrade.json changes,
// regenerate this file:
//
// cp ../../../../genesis/configs/testnet/upgrade.json testnet_upgrade_v47.json
//
//go:embed testnet_upgrade_v47.json
var canonicalTestnetUpgradeV47 []byte
// TestTestnetUpgradeJSON_UnmarshalsAgainstRegistry mirrors the mainnet
// regression gate. Every key in the canonical testnet upgrade.json
// must round-trip through extras.UpgradeConfig.UnmarshalJSON without
// an "unknown precompile config" error. Any unregistered ConfigKey
// fails the test immediately — surfacing the brick-on-boot footgun
// BEFORE the JSON is shipped to the lux-testnet StatefulSet.
func TestTestnetUpgradeJSON_UnmarshalsAgainstRegistry(t *testing.T) {
raw := readCanonicalTestnetUpgradeJSONRaw(t)
var cfg extras.UpgradeConfig
err := json.Unmarshal(raw, &cfg)
require.NoErrorf(t, err,
"extras.UpgradeConfig.UnmarshalJSON refused the canonical testnet "+
"upgrade.json — luxd would refuse to start the testnet C-Chain VM. "+
"Most likely cause: the canonical added a precompile whose module "+
"init() doesn't call RegisterModule under the pinned "+
"luxfi/precompile version (see ~/work/lux/evm/go.mod). "+
"Underlying error: %v",
err,
)
require.NotEmptyf(t, cfg.PrecompileUpgrades,
"canonical testnet upgrade.json must contain at least one entry in precompileUpgrades",
)
for i, upg := range cfg.PrecompileUpgrades {
key := upg.Key()
ts := upg.Timestamp()
require.NotNilf(t, ts,
"precompileUpgrades[%d] key=%q has nil Timestamp() — activations must specify blockTimestamp",
i, key,
)
}
}
// readCanonicalTestnetUpgradeJSONRaw returns the vendored canonical
// testnet upgrade.json. Mirrors the mainnet helper.
func readCanonicalTestnetUpgradeJSONRaw(t *testing.T) []byte {
t.Helper()
if len(canonicalTestnetUpgradeV47) == 0 {
t.Fatal("embedded canonical testnet upgrade.json is empty — //go:embed testnet_upgrade_v47.json failed at build time")
}
return canonicalTestnetUpgradeV47
}
@@ -0,0 +1,86 @@
{
"networkUpgradeOverrides": {
"evmTimestamp": 0,
"durangoTimestamp": 0,
"etnaTimestamp": 0,
"fortunaTimestamp": 0,
"graniteTimestamp": 0,
"strictPQTimestamp": 1766708400
},
"precompileUpgrades": [
{"warpConfig": {"blockTimestamp": 0, "quorumNumerator": 67, "requirePrimaryNetworkSigners": true}},
{"aiMiningConfig": {"blockTimestamp": 1766708400}},
{"blake3Config": {"blockTimestamp": 1766708400}},
{"cggmp21Verify": {"blockTimestamp": 1766708400}},
{"deadZeroConfig": {"blockTimestamp": 1766708400}},
{"deadConfig": {"blockTimestamp": 1766708400}},
{"deadFullConfig": {"blockTimestamp": 1766708400}},
{"dexConfig": {
"blockTimestamp": 1766708400,
"protocolFeeController": "0x9011E888251AB053B7bD1cdB598Db4f9DEd94714",
"maxPools": 10000,
"enableFlashLoans": true,
"enableHooks": true
}},
{"routerConfig": {
"blockTimestamp": 1766708400,
"v3QuoterAddress": "0x0000000000000000000000000000000000000000",
"v3FactoryAddress": "0x0000000000000000000000000000000000000000",
"v2RouterAddress": "0x0000000000000000000000000000000000000000",
"v2FactoryAddress": "0x0000000000000000000000000000000000000000"
}},
{"fheConfig": {"blockTimestamp": 1766708400}},
{"frostVerify": {"blockTimestamp": 1766708400}},
{"graphConfig": {"blockTimestamp": 1766708400}},
{"hpkeConfig": {"blockTimestamp": 1766708400}},
{"mldsaVerify": {"blockTimestamp": 1766708400}},
{"mlkemConfig": {"blockTimestamp": 1766708400}},
{"pqcryptoConfig": {"blockTimestamp": 1766708400}},
{"ringConfig": {"blockTimestamp": 1766708400}},
{"slhdsaVerify": {"blockTimestamp": 1766708400}},
{"zkConfig": {"blockTimestamp": 1766708400}},
{"feeManagerConfig": {
"blockTimestamp": 900000000,
"adminAddresses": ["0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC"],
"enabledAddresses": [],
"initialFeeConfig": {
"gasLimit": 20000000,
"minBaseFee": 25000000000,
"targetGas": 15000000,
"baseFeeChangeDenominator": 36,
"minBlockGasCost": 0,
"maxBlockGasCost": 10000000,
"targetBlockRate": 2,
"blockGasCostStep": 500000
}
}},
{"pulsarVerify": {"blockTimestamp": 1766708400}},
{"magnetarVerify": {"blockTimestamp": 1766708400}},
{"p3qVerify": {"blockTimestamp": 1766708400}},
{"coronaThreshold": {"blockTimestamp": 1766708400}},
{"hqcEncapsulate": {"blockTimestamp": 1766708400}},
{"bls12381G1AddConfig": {"blockTimestamp": 1766708400}},
{"bls12381G1MulConfig": {"blockTimestamp": 1766708400}},
{"bls12381G1MSMConfig": {"blockTimestamp": 1766708400}},
{"bls12381G2AddConfig": {"blockTimestamp": 1766708400}},
{"bls12381G2MulConfig": {"blockTimestamp": 1766708400}},
{"bls12381G2MSMConfig": {"blockTimestamp": 1766708400}},
{"bls12381PairingConfig": {"blockTimestamp": 1766708400}},
{"curve25519Config": {"blockTimestamp": 1766708400}},
{"x25519Config": {"blockTimestamp": 1766708400}},
{"sr25519Verify": {"blockTimestamp": 1766708400}},
{"babyjubjubConfig": {"blockTimestamp": 1766708400}},
{"pedersenConfig": {"blockTimestamp": 1766708400}},
{"poseidonConfig": {"blockTimestamp": 1766708400}},
{"pastaConfig": {"blockTimestamp": 1766708400}},
{"xwingConfig": {"blockTimestamp": 1766708400}},
{"computeMarketConfig": {"blockTimestamp": 1766708400}},
{"bridgeRegistrarConfig": {"blockTimestamp": 1766708400}},
{"stableSwapConfig": {"blockTimestamp": 1766708400}},
{"vrfConfig": {"blockTimestamp": 1766708400}},
{"attestationConfig": {"blockTimestamp": 1766708400}},
{"anchorConfig": {"blockTimestamp": 1766708400}},
{"fixedPointMathConfig": {"blockTimestamp": 1766708400}}
],
"stateUpgrades": []
}