mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 13:46:43 +00:00
fix(pbss): idempotent init cmd if stored db (#54)
* fix(pbss): idempotent init cmd if stored db * rename + nits * fix * handles all cases * remove docs * comments * nit * fix and add unit tests
This commit is contained in:
parent
c46c737d94
commit
186b95619a
6 changed files with 167 additions and 18 deletions
|
|
@ -47,6 +47,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -281,8 +282,19 @@ func initGenesis(ctx *cli.Context) error {
|
||||||
chaindb := utils.MakeChainDatabase(ctx, stack, false)
|
chaindb := utils.MakeChainDatabase(ctx, stack, false)
|
||||||
defer chaindb.Close()
|
defer chaindb.Close()
|
||||||
|
|
||||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
// Berachain: Check if genesis already exists for PBSS databases BEFORE opening trie database.
|
||||||
|
// This prevents the state history truncation that happens during trie database initialization.
|
||||||
|
var triedb *triedb.Database
|
||||||
|
if rawdb.ReadStateScheme(chaindb) == rawdb.PathScheme &&
|
||||||
|
rawdb.ReadChainConfig(chaindb, rawdb.ReadCanonicalHash(chaindb, 0)) != nil {
|
||||||
|
log.Info("PBSS db already initialized with genesis, skipping trie db initialization")
|
||||||
|
} else {
|
||||||
|
// Only create triedb if we're not using PBSS or genesis is empty on disk. Refer to
|
||||||
|
// https://github.com/ethereum/go-ethereum/pull/25523 for why the triedb cannot be
|
||||||
|
// re-created when using PBSS.
|
||||||
|
triedb = utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
}
|
||||||
|
|
||||||
_, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
_, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -97,6 +98,142 @@ func TestCustomGenesis(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPBSSRepeatInitNoTrieOpen verifies that running init twice on a PBSS (path-scheme)
|
||||||
|
// database does not re-initialize the trie database and safely short-circuits.
|
||||||
|
func TestPBSSRepeatInitNoTrieOpen(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Minimal genesis with TTD=0 to keep execution simple
|
||||||
|
genesis := `{
|
||||||
|
"alloc": {"0x0000000000000000000000000000000000000001": {"balance": "0x1"}},
|
||||||
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty": "0x1",
|
||||||
|
"gasLimit": "0x2fefd8",
|
||||||
|
"nonce": "0x0000000000000000",
|
||||||
|
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"timestamp": "0x00",
|
||||||
|
"config": { "terminalTotalDifficulty": 0 }
|
||||||
|
}`
|
||||||
|
|
||||||
|
datadir := t.TempDir()
|
||||||
|
jsonPath := filepath.Join(datadir, "genesis.json")
|
||||||
|
if err := os.WriteFile(jsonPath, []byte(genesis), 0600); err != nil {
|
||||||
|
t.Fatalf("failed to write genesis file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First init should perform normal initialization
|
||||||
|
gethInit1 := runGeth(t, "--datadir", datadir, "init", jsonPath)
|
||||||
|
gethInit1.WaitExit()
|
||||||
|
if gethInit1.Err != nil {
|
||||||
|
t.Fatalf("first init failed: %v", gethInit1.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second init should skip trie db initialization and not truncate freezer
|
||||||
|
gethInit2 := runGeth(t, "--datadir", datadir, "init", jsonPath)
|
||||||
|
gethInit2.WaitExit()
|
||||||
|
if gethInit2.Err != nil {
|
||||||
|
t.Fatalf("second init failed: %v", gethInit2.Err)
|
||||||
|
}
|
||||||
|
stderr := gethInit2.StderrText()
|
||||||
|
if !strings.Contains(stderr, "PBSS db already initialized with genesis, skipping trie db initialization") {
|
||||||
|
t.Fatalf("expected skip-triedb log not found in stderr. got:\n%s", stderr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPBSSConfigUpdate verifies that a repeat init on PBSS writes updated chain
|
||||||
|
// configuration without touching the trie database when the genesis block hash
|
||||||
|
// is unchanged.
|
||||||
|
func TestPBSSConfigUpdate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Base genesis (no Prague1 yet). Include all prior timestamp forks explicitly
|
||||||
|
// with timestamp 0 to satisfy fork ordering: Shanghai -> Cancun -> Prague -> Prague1.
|
||||||
|
baseGenesis := `{
|
||||||
|
"alloc": {"0x0000000000000000000000000000000000000001": {"balance": "0x1"}},
|
||||||
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty": "0x1",
|
||||||
|
"gasLimit": "0x2fefd8",
|
||||||
|
"nonce": "0x0000000000000000",
|
||||||
|
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"timestamp": "0x00",
|
||||||
|
"config": {
|
||||||
|
"terminalTotalDifficulty": 0,
|
||||||
|
"homesteadBlock": 0,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip155Block": 0,
|
||||||
|
"eip158Block": 0,
|
||||||
|
"byzantiumBlock": 0,
|
||||||
|
"constantinopleBlock": 0,
|
||||||
|
"petersburgBlock": 0,
|
||||||
|
"istanbulBlock": 0,
|
||||||
|
"berlinBlock": 0,
|
||||||
|
"londonBlock": 0,
|
||||||
|
"shanghaiTime": 0
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
// Updated config: enable Berachain Prague1 while keeping genesis state identical.
|
||||||
|
// Include all prior timestamp forks to maintain correct ordering.
|
||||||
|
updatedGenesis := `{
|
||||||
|
"alloc": {"0x0000000000000000000000000000000000000001": {"balance": "0x1"}},
|
||||||
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty": "0x1",
|
||||||
|
"gasLimit": "0x2fefd8",
|
||||||
|
"nonce": "0x0000000000000000",
|
||||||
|
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"timestamp": "0x00",
|
||||||
|
"config": {
|
||||||
|
"terminalTotalDifficulty": 0,
|
||||||
|
"homesteadBlock": 0,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip155Block": 0,
|
||||||
|
"eip158Block": 0,
|
||||||
|
"byzantiumBlock": 0,
|
||||||
|
"constantinopleBlock": 0,
|
||||||
|
"petersburgBlock": 0,
|
||||||
|
"istanbulBlock": 0,
|
||||||
|
"berlinBlock": 0,
|
||||||
|
"londonBlock": 0,
|
||||||
|
"shanghaiTime": 0,
|
||||||
|
"berachain": {
|
||||||
|
"prague1": {
|
||||||
|
"time": 1000,
|
||||||
|
"baseFeeChangeDenominator": 48,
|
||||||
|
"poLDistributorAddress": "0x1111111111111111111111111111111111111111"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
datadir := t.TempDir()
|
||||||
|
jsonPath := filepath.Join(datadir, "genesis.json")
|
||||||
|
if err := os.WriteFile(jsonPath, []byte(baseGenesis), 0600); err != nil {
|
||||||
|
t.Fatalf("failed to write base genesis file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First init with base config
|
||||||
|
runGeth(t, "--datadir", datadir, "init", jsonPath).WaitExit()
|
||||||
|
|
||||||
|
// Overwrite with updated config (same genesis state, different chain config)
|
||||||
|
if err := os.WriteFile(jsonPath, []byte(updatedGenesis), 0600); err != nil {
|
||||||
|
t.Fatalf("failed to write updated genesis file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second init should skip trie db init and write the new chain config
|
||||||
|
geth := runGeth(t, "--datadir", datadir, "init", jsonPath)
|
||||||
|
geth.WaitExit()
|
||||||
|
stderr := geth.StderrText()
|
||||||
|
if !strings.Contains(stderr, "PBSS db already initialized with genesis, skipping trie db initialization") {
|
||||||
|
t.Fatalf("expected PBSS skip log not found in stderr. got:\n%s", stderr)
|
||||||
|
}
|
||||||
|
if !strings.Contains(stderr, "Writing new chain config") {
|
||||||
|
t.Fatalf("expected config-update log not found in stderr. got:\n%s", stderr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestCustomBackend that the backend selection and detection (leveldb vs pebble) works properly.
|
// TestCustomBackend that the backend selection and detection (leveldb vs pebble) works properly.
|
||||||
func TestCustomBackend(t *testing.T) {
|
func TestCustomBackend(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
@ -186,9 +323,9 @@ func TestCustomBackend(t *testing.T) {
|
||||||
{ // Reject invalid backend choice
|
{ // Reject invalid backend choice
|
||||||
initArgs: []string{"--db.engine", "mssql"},
|
initArgs: []string{"--db.engine", "mssql"},
|
||||||
initExpect: `Fatal: Invalid choice for db.engine 'mssql', allowed 'leveldb' or 'pebble'`,
|
initExpect: `Fatal: Invalid choice for db.engine 'mssql', allowed 'leveldb' or 'pebble'`,
|
||||||
// Since the init fails, this will return the (default) mainnet genesis
|
// Since the init fails, this will return the (default) berachain mainnet genesis
|
||||||
// block nonce
|
// block nonce
|
||||||
execExpect: `0x0000000000000042`,
|
execExpect: `0x0000000000001234`,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
if err := testfunc(t, tt); err != nil {
|
if err := testfunc(t, tt); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -307,8 +307,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
|
||||||
ghash := rawdb.ReadCanonicalHash(db, 0)
|
ghash := rawdb.ReadCanonicalHash(db, 0)
|
||||||
if (ghash == common.Hash{}) {
|
if (ghash == common.Hash{}) {
|
||||||
if genesis == nil {
|
if genesis == nil {
|
||||||
log.Info("Writing default main-net genesis block")
|
log.Info("Writing default berachain mainnet genesis block")
|
||||||
genesis = DefaultGenesisBlock()
|
genesis = DefaultBerachainGenesisBlock()
|
||||||
} else {
|
} else {
|
||||||
log.Info("Writing custom genesis block")
|
log.Info("Writing custom genesis block")
|
||||||
}
|
}
|
||||||
|
|
@ -332,8 +332,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
|
||||||
// networks must explicitly specify the genesis in the config file, mainnet
|
// networks must explicitly specify the genesis in the config file, mainnet
|
||||||
// genesis will be used as default and the initialization will always fail.
|
// genesis will be used as default and the initialization will always fail.
|
||||||
if genesis == nil {
|
if genesis == nil {
|
||||||
log.Info("Writing default main-net genesis block")
|
log.Info("Writing default berachain mainnet genesis block")
|
||||||
genesis = DefaultGenesisBlock()
|
genesis = DefaultBerachainGenesisBlock()
|
||||||
} else {
|
} else {
|
||||||
log.Info("Writing custom genesis block")
|
log.Info("Writing custom genesis block")
|
||||||
}
|
}
|
||||||
|
|
@ -385,9 +385,11 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
|
||||||
return newCfg, ghash, compatErr, nil
|
return newCfg, ghash, compatErr, nil
|
||||||
}
|
}
|
||||||
// Don't overwrite if the old is identical to the new. It's useful
|
// Don't overwrite if the old is identical to the new. It's useful
|
||||||
// for the scenarios that database is opened in the read-only mode.
|
// for the scenarios that database is opened in the read-only mode
|
||||||
|
// OR if the chain config is updated to include new Berachain fork info.
|
||||||
storedData, _ := json.Marshal(storedCfg)
|
storedData, _ := json.Marshal(storedCfg)
|
||||||
if newData, _ := json.Marshal(newCfg); !bytes.Equal(storedData, newData) {
|
if newData, _ := json.Marshal(newCfg); !bytes.Equal(storedData, newData) {
|
||||||
|
log.Info("Writing new chain config")
|
||||||
rawdb.WriteChainConfig(db, ghash, newCfg)
|
rawdb.WriteChainConfig(db, ghash, newCfg)
|
||||||
}
|
}
|
||||||
return newCfg, ghash, nil, nil
|
return newCfg, ghash, nil, nil
|
||||||
|
|
@ -423,8 +425,8 @@ func LoadChainConfig(db ethdb.Database, genesis *Genesis) (cfg *params.ChainConf
|
||||||
return genesis.Config, ghash, nil
|
return genesis.Config, ghash, nil
|
||||||
}
|
}
|
||||||
// There is no stored chain config and no new config provided,
|
// There is no stored chain config and no new config provided,
|
||||||
// In this case the default chain config(mainnet) will be used
|
// In this case the default chain config(berachain mainnet) will be used
|
||||||
return params.MainnetChainConfig, params.MainnetGenesisHash, nil
|
return params.BerachainChainConfig, params.BerachainGenesisHash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// chainConfigOrDefault retrieves the attached chain configuration. If the genesis
|
// chainConfigOrDefault retrieves the attached chain configuration. If the genesis
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,8 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
|
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
|
||||||
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil)
|
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil)
|
||||||
},
|
},
|
||||||
wantHash: params.MainnetGenesisHash,
|
wantHash: params.BerachainGenesisHash,
|
||||||
wantConfig: params.MainnetChainConfig,
|
wantConfig: params.BerachainChainConfig,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "mainnet block in DB, genesis == nil",
|
name: "mainnet block in DB, genesis == nil",
|
||||||
|
|
|
||||||
|
|
@ -133,13 +133,11 @@ const dnsPrefix = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUD
|
||||||
// KnownDNSNetwork returns the address of a public DNS-based node list for the given
|
// KnownDNSNetwork returns the address of a public DNS-based node list for the given
|
||||||
// genesis hash and protocol. See https://github.com/ethereum/discv4-dns-lists for more
|
// genesis hash and protocol. See https://github.com/ethereum/discv4-dns-lists for more
|
||||||
// information.
|
// information.
|
||||||
|
//
|
||||||
|
// TODO: add support for bepolia and berachain mainnet.
|
||||||
func KnownDNSNetwork(genesis common.Hash, protocol string) string {
|
func KnownDNSNetwork(genesis common.Hash, protocol string) string {
|
||||||
var net string
|
var net string
|
||||||
switch genesis {
|
switch genesis {
|
||||||
case BerachainGenesisHash:
|
|
||||||
net = "berachain"
|
|
||||||
case BepoliaGenesisHash:
|
|
||||||
net = "bepolia"
|
|
||||||
case MainnetGenesisHash:
|
case MainnetGenesisHash:
|
||||||
net = "mainnet"
|
net = "mainnet"
|
||||||
case SepoliaGenesisHash:
|
case SepoliaGenesisHash:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue