From d24c33a5b57acebe84c710403aad0590f2fa7409 Mon Sep 17 00:00:00 2001 From: Daniel Liu <139250065@qq.com> Date: Fri, 13 Mar 2026 09:53:40 +0800 Subject: [PATCH] fix(eth): initialize engine with finalized chain config, fix #2138 Reorder initialization in eth.New so the effective chain config is resolved and persisted via core.SetupGenesisBlock before creating the consensus engine. Background: - On first sync from genesis, LoadChainConfig could return a stored/incomplete config (notably with XDPoS.V2 unset in legacy testnet setups). - Creating XDPoS before finalizing genesis config could trigger mainnet V2 fallback and log/behavior mismatches around SwitchBlock. What changed: - Replace early LoadChainConfig usage with SetupGenesisBlock in eth.New. - Keep ConfigCompatError semantics consistent with core/blockchain initialization: fail only on non-compat errors. - Initialize consensus engine after finalized chainConfig is available. Result: - Consensus engine now starts with finalized network config on first boot. - Avoids incorrect V2 fallback parameters (e.g. wrong SwitchBlock) during initial testnet sync. Tests: - Added regression tests in eth/backend_test.go to cover legacy missing-V2 repair and SetupGenesisBlock idempotency. --- eth/backend.go | 13 ++++----- eth/backend_test.go | 64 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/eth/backend.go b/eth/backend.go index df68a1a8f76e..5ce5da1c3ec4 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -129,11 +129,11 @@ func New(stack *node.Node, config *ethconfig.Config, XDCXServ *XDCx.XDCX, lendin if err != nil { return nil, err } - // Here we determine genesis hash and active ChainConfig. - // We need these to figure out the consensus parameters and to set up history pruning. - chainConfig, _, err := core.LoadChainConfig(chainDb, config.Genesis) - if err != nil { - return nil, err + // Resolve the effective chain config (and persist it when compatible) + // before constructing the consensus engine so it initializes with final network settings. + chainConfig, _, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis) + if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { + return nil, genesisErr } // Set networkID to chainID by default. @@ -142,6 +142,7 @@ func New(stack *node.Node, config *ethconfig.Config, XDCXServ *XDCx.XDCX, lendin networkID = chainConfig.ChainID.Uint64() } common.CopyConstants(networkID) + engine := CreateConsensusEngine(stack, chainConfig, chainDb) // Assemble the Ethereum object. eth := &Ethereum{ @@ -149,7 +150,7 @@ func New(stack *node.Node, config *ethconfig.Config, XDCXServ *XDCx.XDCX, lendin chainDb: chainDb, eventMux: stack.EventMux(), accountManager: stack.AccountManager(), - engine: CreateConsensusEngine(stack, chainConfig, chainDb), + engine: engine, shutdownChan: make(chan bool), networkId: networkID, gasPrice: config.Miner.GasPrice, diff --git a/eth/backend_test.go b/eth/backend_test.go index dd787d7986cc..f30e23c09338 100644 --- a/eth/backend_test.go +++ b/eth/backend_test.go @@ -4,6 +4,8 @@ import ( "math/big" "testing" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/eth/util" "github.com/XinFinOrg/XDPoSChain/params" ) @@ -27,3 +29,65 @@ func TestRewardInflation(t *testing.T) { } } } + +func TestSetupGenesisBlockRepairsMissingV2Config(t *testing.T) { + db := rawdb.NewMemoryDatabase() + + legacyGenesis := legacyTestnetGenesisWithoutV2() + legacyGenesis.MustCommit(db) + + loadedCfg, _, err := core.LoadChainConfig(db, core.DefaultTestnetGenesisBlock()) + if err != nil { + t.Fatalf("LoadChainConfig failed: %v", err) + } + if loadedCfg.XDPoS == nil { + t.Fatal("expected XDPoS config in loaded chain config") + } + if loadedCfg.XDPoS.V2 != nil { + t.Fatal("expected stored legacy chain config to have nil XDPoS.V2 before setup") + } + + finalCfg, _, err := core.SetupGenesisBlock(db, core.DefaultTestnetGenesisBlock()) + if err != nil { + t.Fatalf("SetupGenesisBlock failed: %v", err) + } + if finalCfg.XDPoS == nil || finalCfg.XDPoS.V2 == nil { + t.Fatal("expected SetupGenesisBlock to return a config with XDPoS.V2") + } + if finalCfg.XDPoS.V2.SwitchBlock.Cmp(params.TestnetChainConfig.XDPoS.V2.SwitchBlock) != 0 { + t.Fatalf("unexpected switch block after setup: have %v want %v", finalCfg.XDPoS.V2.SwitchBlock, params.TestnetChainConfig.XDPoS.V2.SwitchBlock) + } +} + +func TestSetupGenesisBlockIsIdempotentForTestnet(t *testing.T) { + db := rawdb.NewMemoryDatabase() + genesis := core.DefaultTestnetGenesisBlock() + + cfg1, hash1, err := core.SetupGenesisBlock(db, genesis) + if err != nil { + t.Fatalf("first SetupGenesisBlock failed: %v", err) + } + cfg2, hash2, err := core.SetupGenesisBlock(db, genesis) + if err != nil { + t.Fatalf("second SetupGenesisBlock failed: %v", err) + } + if hash1 != hash2 { + t.Fatalf("genesis hash changed across SetupGenesisBlock calls: first %v second %v", hash1, hash2) + } + if cfg1.XDPoS == nil || cfg2.XDPoS == nil || cfg1.XDPoS.V2 == nil || cfg2.XDPoS.V2 == nil { + t.Fatal("expected both returned configs to include XDPoS.V2") + } + if cfg1.XDPoS.V2.SwitchBlock.Cmp(cfg2.XDPoS.V2.SwitchBlock) != 0 { + t.Fatalf("switch block changed across SetupGenesisBlock calls: first %v second %v", cfg1.XDPoS.V2.SwitchBlock, cfg2.XDPoS.V2.SwitchBlock) + } +} + +func legacyTestnetGenesisWithoutV2() *core.Genesis { + legacyGenesis := *core.DefaultTestnetGenesisBlock() + legacyChainConfig := *params.TestnetChainConfig + legacyXDPoS := *params.TestnetChainConfig.XDPoS + legacyXDPoS.V2 = nil + legacyChainConfig.XDPoS = &legacyXDPoS + legacyGenesis.Config = &legacyChainConfig + return &legacyGenesis +}