mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
The AlwaysOn DEX settlement precompile (0x9999) had its EXTCODESIZE marker baked into EVERY chain's genesis state unconditionally (parent==nil in ApplyPrecompileActivations), mutating the genesis state root. This changed the genesis hash of every chain genesised before the DEX activation, breaking RLP re-import of the real archived Lux mainnet (0x3f4fa2a0), Lux testnet (0x1c5fe377) and Zoo mainnet (0x7c548af4) — all re-genesised Nov 2024, before the Dec-2025 activation. Fix: an AlwaysOn system precompile carries a protocol ActivationTime (Dec 2025 for 0x9999, set by the registry bridge — a fork-like constant, not per-net config). Its genesis marker is installed ONLY IF ActivationTime <= the genesis timestamp; otherwise it activates later, on the block transition that crosses ActivationTime during history replay. Dispatch (GetExtrasRules) is gated on the same timestamp so a codeless address never dispatches. A chain born before activation reproduces its ORIGINAL genesis byte-for-byte; a fresh chain born at/after activation still gets 0x9999 at genesis. Verified against the real RLP block-0 of all three chains (byte-identical reproduction) plus a fresh post-activation chain. Corrects two pre-existing tests that had hardcoded the buggy with-0x9999 hashes as "canonical". Co-authored-by: Hanzo Dev <[email protected]>
180 lines
5.7 KiB
Go
180 lines
5.7 KiB
Go
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package core
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/luxfi/geth/common"
|
|
)
|
|
|
|
// TestSkipPostMergeFieldsGenesisHash pins the canonical Lux mainnet
|
|
// C-Chain genesis hash 0x3f4fa2a0b0ce089f52bf0ae9199c75ffdd76ecafc987794050cb0d286f1ec61e.
|
|
//
|
|
// The genesis JSON sets cancunTime=0, so without SkipPostMergeFields
|
|
// the genesis block would carry ParentBeaconRoot/ExcessBlobGas/BlobGasUsed
|
|
// header fields that did not exist when the historic chain was launched.
|
|
// Honoring the JSON flag preserves hash continuity with the historic
|
|
// 1.08M-block RLP export at lux-mainnet-96369.rlp (block 0 hash above).
|
|
func TestSkipPostMergeFieldsGenesisHash(t *testing.T) {
|
|
const luxMainnetGenesis = `{
|
|
"alloc": {
|
|
"0200000000000000000000000000000000000005": {
|
|
"balance": "0x0",
|
|
"code": "0x01",
|
|
"nonce": "0x1"
|
|
},
|
|
"0x9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
|
"balance": "0x193e5939a08ce9dbd480000000"
|
|
}
|
|
},
|
|
"baseFeePerGas": "0x5d21dba00",
|
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
|
"config": {
|
|
"arrowGlacierBlock": 0,
|
|
"berlinBlock": 0,
|
|
"byzantiumBlock": 0,
|
|
"cancunTime": 0,
|
|
"chainId": 96369,
|
|
"constantinopleBlock": 0,
|
|
"eip150Block": 0,
|
|
"eip155Block": 0,
|
|
"eip158Block": 0,
|
|
"evmTimestamp": 0,
|
|
"feeConfig": {
|
|
"baseFeeChangeDenominator": 36,
|
|
"blockGasCostStep": 200000,
|
|
"gasLimit": 12000000,
|
|
"maxBlockGasCost": 1000000,
|
|
"minBaseFee": 25000000000,
|
|
"minBlockGasCost": 0,
|
|
"targetBlockRate": 2,
|
|
"targetGas": 500000000
|
|
},
|
|
"grayGlacierBlock": 0,
|
|
"homesteadBlock": 0,
|
|
"istanbulBlock": 0,
|
|
"londonBlock": 0,
|
|
"mergeNetsplitBlock": 0,
|
|
"muirGlacierBlock": 0,
|
|
"petersburgBlock": 0,
|
|
"shanghaiTime": 0,
|
|
"terminalTotalDifficulty": 0,
|
|
"durangoTimestamp": 0,
|
|
"quasarTimestamp": 0,
|
|
"fortunaTimestamp": 0,
|
|
"graniteTimestamp": 0
|
|
},
|
|
"difficulty": "0x0",
|
|
"extraData": "0x",
|
|
"gasLimit": "0xb71b00",
|
|
"gasUsed": "0x0",
|
|
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
"nonce": "0x0",
|
|
"number": "0x0",
|
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
"skipPostMergeFields": true,
|
|
"stateRoot": "0x2d1cedac263020c5c56ef962f6abe0da1f5217bdc6468f8c9258a0ea23699e80",
|
|
"timestamp": "0x672485c2"
|
|
}`
|
|
// The genesis timestamp (0x672485c2, Nov 2024) predates the DEX settlement (0x9999)
|
|
// activation (registry.DexSettleActivationTime, Dec 2025), so 0x9999 is NOT in genesis
|
|
// state and this is the TRUE canonical Lux mainnet genesis hash — byte-identical to the
|
|
// lux-mainnet-96369.rlp block-0 import target. (Before the genesis-injection fix this
|
|
// test asserted 0x5609692a…, the value the unconditional 0x9999 marker mutated it to;
|
|
// that was the bug, cross-checked here against the sibling test's `canonical` constant.)
|
|
const wantHash = "0x3f4fa2a0b0ce089f52bf0ae9199c75ffdd76ecafc987794050cb0d286f1ec61e"
|
|
|
|
var g Genesis
|
|
if err := json.Unmarshal([]byte(luxMainnetGenesis), &g); err != nil {
|
|
t.Fatalf("unmarshal genesis: %v", err)
|
|
}
|
|
if !g.SkipPostMergeFields {
|
|
t.Fatalf("SkipPostMergeFields not honored from JSON")
|
|
}
|
|
got := g.ToBlock().Hash()
|
|
if got != common.HexToHash(wantHash) {
|
|
t.Fatalf("genesis hash mismatch:\n got %s\n want %s", got.Hex(), wantHash)
|
|
}
|
|
}
|
|
|
|
// TestPostMergeFieldsActiveWhenFlagFalse ensures that without
|
|
// SkipPostMergeFields the genesis hash flips to a different value
|
|
// (i.e. the flag is the gate, not a no-op). Uses the same chain spec
|
|
// minus the flag.
|
|
func TestPostMergeFieldsActiveWhenFlagFalse(t *testing.T) {
|
|
const baseGenesis = `{
|
|
"alloc": {
|
|
"0200000000000000000000000000000000000005": {
|
|
"balance": "0x0",
|
|
"code": "0x01",
|
|
"nonce": "0x1"
|
|
},
|
|
"0x9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
|
"balance": "0x193e5939a08ce9dbd480000000"
|
|
}
|
|
},
|
|
"baseFeePerGas": "0x5d21dba00",
|
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
|
"config": {
|
|
"arrowGlacierBlock": 0,
|
|
"berlinBlock": 0,
|
|
"byzantiumBlock": 0,
|
|
"cancunTime": 0,
|
|
"chainId": 96369,
|
|
"constantinopleBlock": 0,
|
|
"eip150Block": 0,
|
|
"eip155Block": 0,
|
|
"eip158Block": 0,
|
|
"evmTimestamp": 0,
|
|
"feeConfig": {
|
|
"baseFeeChangeDenominator": 36,
|
|
"blockGasCostStep": 200000,
|
|
"gasLimit": 12000000,
|
|
"maxBlockGasCost": 1000000,
|
|
"minBaseFee": 25000000000,
|
|
"minBlockGasCost": 0,
|
|
"targetBlockRate": 2,
|
|
"targetGas": 500000000
|
|
},
|
|
"grayGlacierBlock": 0,
|
|
"homesteadBlock": 0,
|
|
"istanbulBlock": 0,
|
|
"londonBlock": 0,
|
|
"mergeNetsplitBlock": 0,
|
|
"muirGlacierBlock": 0,
|
|
"petersburgBlock": 0,
|
|
"shanghaiTime": 0,
|
|
"terminalTotalDifficulty": 0,
|
|
"durangoTimestamp": 0,
|
|
"quasarTimestamp": 0,
|
|
"fortunaTimestamp": 0,
|
|
"graniteTimestamp": 0
|
|
},
|
|
"difficulty": "0x0",
|
|
"extraData": "0x",
|
|
"gasLimit": "0xb71b00",
|
|
"gasUsed": "0x0",
|
|
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
"nonce": "0x0",
|
|
"number": "0x0",
|
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
"stateRoot": "0x2d1cedac263020c5c56ef962f6abe0da1f5217bdc6468f8c9258a0ea23699e80",
|
|
"timestamp": "0x672485c2"
|
|
}`
|
|
var g Genesis
|
|
if err := json.Unmarshal([]byte(baseGenesis), &g); err != nil {
|
|
t.Fatalf("unmarshal: %v", err)
|
|
}
|
|
if g.SkipPostMergeFields {
|
|
t.Fatalf("SkipPostMergeFields should default false")
|
|
}
|
|
got := g.ToBlock().Hash()
|
|
canonical := common.HexToHash("0x3f4fa2a0b0ce089f52bf0ae9199c75ffdd76ecafc987794050cb0d286f1ec61e")
|
|
if got == canonical {
|
|
t.Fatalf("expected different hash when post-merge fields are active, got canonical %s", canonical.Hex())
|
|
}
|
|
}
|