From cab21e0b0ac1d8ad31b83f4ca2d2fb81deb9f390 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 16 Jul 2025 01:15:31 +0200 Subject: [PATCH] params: update --- params/chainparam.go | 69 +++++++++++++++++ params/config2.go | 61 ++++++++------- params/config2_presets.go | 115 ---------------------------- params/presets/presets.go | 134 +++++++++++++++++++++++++++++++++ params/presets/presets_test.go | 12 +++ params/registry.go | 20 +++-- 6 files changed, 262 insertions(+), 149 deletions(-) delete mode 100644 params/config2_presets.go create mode 100644 params/presets/presets.go create mode 100644 params/presets/presets_test.go diff --git a/params/chainparam.go b/params/chainparam.go index 225558f3f7..925e5b0d52 100644 --- a/params/chainparam.go +++ b/params/chainparam.go @@ -1,6 +1,7 @@ package params import ( + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -10,9 +11,46 @@ import ( // ChainID type ChainID big.Int +func NewChainID(input string) *ChainID { + b, ok := new(big.Int).SetString(input, 0) + if !ok { + panic("invalid chainID: " + input) + } + return (*ChainID)(b) +} + +func (v *ChainID) MarshalText() ([]byte, error) { + return (*big.Int)(v).MarshalText() +} + +func (v *ChainID) UnmarshalText(input []byte) error { + return (*big.Int)(v).UnmarshalText(input) +} + +func (v *ChainID) BigInt() *big.Int { + return (*big.Int)(v) +} + +func (v *ChainID) Validate(cfg *Config2) error { + b := (*big.Int)(v) + if b.Sign() <= 0 { + return fmt.Errorf("invalid chainID value %v", b) + } + + return nil +} + // TerminalTotalDifficulty (TTD) is the total difficulty value where type TerminalTotalDifficulty big.Int +func NewTerminalTotalDifficulty(input string) *TerminalTotalDifficulty { + b, ok := new(big.Int).SetString(input, 0) + if !ok { + panic("invalid terminal total difficulty: " + input) + } + return (*TerminalTotalDifficulty)(b) +} + func (v *TerminalTotalDifficulty) MarshalText() ([]byte, error) { return (*big.Int)(v).MarshalText() } @@ -21,18 +59,49 @@ func (v *TerminalTotalDifficulty) UnmarshalText(input []byte) error { return (*big.Int)(v).UnmarshalText(input) } +func (v *TerminalTotalDifficulty) Validate(cfg *Config2) error { + return nil +} + // DepositContractAddress configures the location of the deposit contract. type DepositContractAddress common.Address +func (v DepositContractAddress) Validate(cfg *Config2) error { + return nil +} + // This configures the EIP-4844 parameters across forks. // There must be an entry for each fork type BlobSchedule map[forks.Fork]BlobConfig +// Validate checks that all forks with blobs explicitly define the blob schedule configuration. +func (v BlobSchedule) Validate(cfg *Config2) error { + for _, f := range forks.CanonOrder { + if f.HasBlobs() { + schedule := Get[BlobSchedule](cfg) + bcfg, defined := schedule[f] + if cfg.Scheduled(f) && !defined { + return fmt.Errorf("invalid chain configuration: missing entry for fork %q in blobSchedule", f) + } + if defined { + if err := bcfg.validate(); err != nil { + return fmt.Errorf("invalid chain configuration in blobSchedule for fork %q: %v", f, err) + } + } + } + } + return nil +} + // DAOForkSupport is the chain parameter that configures the DAO fork. // true=supports or false=opposes the fork. // The default value is true. type DAOForkSupport bool +func (v DAOForkSupport) Validate(cfg *Config2) error { + return nil +} + func init() { Define(Parameter[*ChainID]{ Name: "chainId", diff --git a/params/config2.go b/params/config2.go index 3e4aeb3844..79c7406081 100644 --- a/params/config2.go +++ b/params/config2.go @@ -43,6 +43,7 @@ func NewConfig2(activations Activations, param ...ParameterType) *Config2 { activation: maps.Clone(activations), param: make(map[reflect.Type]ParameterType, len(param)), } + cfg.activation[forks.Frontier] = 0 for _, pv := range param { info, ok := findParam(pv) if !ok { @@ -62,6 +63,16 @@ func (cfg *Config2) Active(f forks.Fork, block, timestamp uint64) bool { return ok && timestamp >= activation } +// ActiveAtBlock reports whether the given fork is active for a block number/time. +func (cfg *Config2) ActiveAtBlock(f forks.Fork, block *big.Int) bool { + if !f.BlockBased() { + panic(fmt.Sprintf("fork %v has time-based scheduling", f)) + } + activation, ok := cfg.activation[f] + return ok && block.Uint64() >= activation +} + +// Activation returns the activation block/number of a fork. func (cfg *Config2) Activation(f forks.Fork) (uint64, bool) { a, ok := cfg.activation[f] return a, ok @@ -80,11 +91,6 @@ func (cfg *Config2) SetActivations(forks Activations) *Config2 { return &Config2{activation: newA, param: cfg.param} } -// SetParam returns a new configuration with parameters modified. -func (cfg *Config2) SetParam(p ParameterType) *Config2 { - return &Config2{activation: cfg.activation} -} - // LatestFork returns the latest time-based fork that would be active for the given time. func (cfg *Config2) LatestFork(time uint64) forks.Fork { londonBlock := cfg.activation[forks.London] @@ -189,7 +195,7 @@ func (cfg *Config2) decodeParameter(key string, dec *json.Decoder) error { if err := dec.Decode(v); err != nil { return err } - cfg.param[info.rtype] = v + cfg.param[info.rtype] = v.(ParameterType) return nil } @@ -198,6 +204,7 @@ func (cfg *Config2) decodeParameter(key string, dec *json.Decoder) error { func (cfg *Config2) Validate() error { sanityCheckCanonOrder() + // Check forks. lastFork := forks.CanonOrder[0] for _, f := range forks.CanonOrder[1:] { act := "timestamp" @@ -225,21 +232,22 @@ func (cfg *Config2) Validate() error { if !f.Optional() || cfg.Scheduled(f) { lastFork = f } + } - // Check that all forks with blobs explicitly define the blob schedule configuration. - if f.HasBlobs() { - schedule := Get[BlobSchedule](cfg) - bcfg, defined := schedule[f] - if cfg.Scheduled(f) && !defined { - return fmt.Errorf("invalid chain configuration: missing entry for fork %q in blobSchedule", f) - } - if defined { - if err := bcfg.validate(); err != nil { - return fmt.Errorf("invalid chain configuration in blobSchedule for fork %q: %v", f, err) - } + // Check parameters. + for rtype, info := range paramRegistry { + v, isSet := cfg.param[rtype] + if !isSet { + if !info.optional { + return fmt.Errorf("required chain parameter %s is not set", info.name) } + v = info.defaultVal + } + if err := v.Validate(cfg); err != nil { + return fmt.Errorf("invalid %s: %w", info.name, err) } } + return nil } @@ -286,16 +294,17 @@ func (cfg *Config2) checkCompatible(newcfg *Config2, num uint64, time uint64) *C } return newTimestampCompatError2("%v fork timestamp", f, cfg, newcfg) } - // Specialty checks: - if cfg.Active(forks.DAO, num, time) && Get[DAOForkSupport](cfg) != Get[DAOForkSupport](newcfg) { - return newBlockCompatError2("DAO fork support flag", f, cfg, newcfg) - } - chainID := (*big.Int)(Get[*ChainID](cfg)) - newChainID := (*big.Int)(Get[*ChainID](newcfg)) - if cfg.Active(forks.TangerineWhistle, num, time) && !configBlockEqual(chainID, newChainID) { - return newBlockCompatError2("EIP158 chain ID", f, cfg, newcfg) - } } + + if cfg.Active(forks.DAO, num, time) && Get[DAOForkSupport](cfg) != Get[DAOForkSupport](newcfg) { + return newBlockCompatError2("DAO fork support flag", forks.DAO, cfg, newcfg) + } + chainID := (*big.Int)(Get[*ChainID](cfg)) + newChainID := (*big.Int)(Get[*ChainID](newcfg)) + if cfg.Active(forks.TangerineWhistle, num, time) && !configBlockEqual(chainID, newChainID) { + return newBlockCompatError2("EIP158 chain ID", forks.TangerineWhistle, cfg, newcfg) + } + return nil } diff --git a/params/config2_presets.go b/params/config2_presets.go deleted file mode 100644 index 557cb5d0ac..0000000000 --- a/params/config2_presets.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2025 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package params - -import ( - "math" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/params/forks" -) - -var ( - MainnetConfig2 *Config2 - AllEthashProtocolChanges2 *Config2 - MergedTestChainConfig2 *Config2 -) - -func init() { - MainnetConfig2 = NewConfig2( - Activations{ - forks.Homestead: 1_150_000, - forks.DAO: 1_920_000, - forks.TangerineWhistle: 2_463_000, - forks.SpuriousDragon: 2_675_000, - forks.Byzantium: 4_370_000, - forks.Constantinople: 7_280_000, - forks.Petersburg: 7_280_000, - forks.Istanbul: 9_069_000, - forks.MuirGlacier: 9_200_000, - forks.Berlin: 12_244_000, - forks.London: 12_965_000, - forks.ArrowGlacier: 13_773_000, - forks.GrayGlacier: 15_050_000, - // time-based forks - forks.Shanghai: 1681338455, - forks.Cancun: 1710338135, - forks.Prague: 1746612311, - }, - (*ChainID)(big.NewInt(1)), - (*TerminalTotalDifficulty)(MainnetTerminalTotalDifficulty), - DepositContractAddress(common.HexToAddress("0x00000000219ab540356cbb839cbe05303d7705fa")), - DAOForkSupport(true), - BlobSchedule{ - forks.Cancun: *DefaultCancunBlobConfig, - forks.Prague: *DefaultPragueBlobConfig, - }, - ) - - // AllEthashProtocolChanges2 contains every protocol change (EIPs) introduced - // and accepted by the Ethereum core developers into the Ethash consensus. - AllEthashProtocolChanges2 = NewConfig2( - Activations{ - forks.Homestead: 0, - forks.TangerineWhistle: 0, - forks.SpuriousDragon: 0, - forks.Byzantium: 0, - forks.Constantinople: 0, - forks.Petersburg: 0, - forks.Istanbul: 0, - forks.MuirGlacier: 0, - forks.Berlin: 0, - forks.London: 0, - forks.ArrowGlacier: 0, - forks.GrayGlacier: 0, - }, - (*ChainID)(big.NewInt(1337)), - (*TerminalTotalDifficulty)(big.NewInt(math.MaxInt64)), - ) - - // MergedTestChainConfig2 contains every protocol change (EIPs) introduced - // and accepted by the Ethereum core developers for testing purposes. - MergedTestChainConfig2 = NewConfig2( - Activations{ - forks.Homestead: 0, - forks.TangerineWhistle: 0, - forks.SpuriousDragon: 0, - forks.Byzantium: 0, - forks.Constantinople: 0, - forks.Petersburg: 0, - forks.Istanbul: 0, - forks.MuirGlacier: 0, - forks.Berlin: 0, - forks.London: 0, - forks.ArrowGlacier: 0, - forks.GrayGlacier: 0, - forks.Paris: 0, - forks.Shanghai: 0, - forks.Cancun: 0, - forks.Prague: 0, - forks.Osaka: 0, - }, - (*ChainID)(big.NewInt(1)), - (*TerminalTotalDifficulty)(big.NewInt(0)), - BlobSchedule{ - forks.Cancun: *DefaultCancunBlobConfig, - forks.Prague: *DefaultPragueBlobConfig, - forks.Osaka: *DefaultOsakaBlobConfig, - }, - ) -} diff --git a/params/presets/presets.go b/params/presets/presets.go new file mode 100644 index 0000000000..8af1aab366 --- /dev/null +++ b/params/presets/presets.go @@ -0,0 +1,134 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package presets + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/forks" +) + +var Mainnet = params.NewConfig2( + params.Activations{ + forks.Homestead: 1_150_000, + forks.DAO: 1_920_000, + forks.TangerineWhistle: 2_463_000, + forks.SpuriousDragon: 2_675_000, + forks.Byzantium: 4_370_000, + forks.Constantinople: 7_280_000, + forks.Petersburg: 7_280_000, + forks.Istanbul: 9_069_000, + forks.MuirGlacier: 9_200_000, + forks.Berlin: 12_244_000, + forks.London: 12_965_000, + forks.ArrowGlacier: 13_773_000, + forks.GrayGlacier: 15_050_000, + forks.Paris: 15_537_393, + // time-based forks + forks.Shanghai: 1681338455, + forks.Cancun: 1710338135, + forks.Prague: 1746612311, + }, + params.NewChainID("1"), + params.NewTerminalTotalDifficulty("58_750_000_000_000_000_000_000"), + params.DepositContractAddress(common.HexToAddress("0x00000000219ab540356cbb839cbe05303d7705fa")), + params.DAOForkSupport(true), + params.BlobSchedule{ + forks.Cancun: *params.DefaultCancunBlobConfig, + forks.Prague: *params.DefaultPragueBlobConfig, + }, +) + +// SepoliaChainConfig contains the chain parameters to run a node on the Sepolia test network. +var Sepolia = params.NewConfig2( + params.Activations{ + forks.Homestead: 0, + forks.TangerineWhistle: 0, + forks.SpuriousDragon: 0, + forks.Byzantium: 0, + forks.Constantinople: 0, + forks.Petersburg: 0, + forks.Istanbul: 0, + forks.MuirGlacier: 0, + forks.Berlin: 0, + forks.London: 0, + forks.Paris: 1735371, + // time-based forks + forks.Shanghai: 1677557088, + forks.Cancun: 1706655072, + forks.Prague: 1741159776, + }, + params.NewChainID("11155111"), + params.NewTerminalTotalDifficulty("17_000_000_000_000_000"), + params.DepositContractAddress(common.HexToAddress("0x7f02c3e3c98b133055b8b348b2ac625669ed295d")), + params.BlobSchedule{ + forks.Cancun: *params.DefaultCancunBlobConfig, + forks.Prague: *params.DefaultPragueBlobConfig, + }, +) + +// AllEthashProtocolChanges2 contains every protocol change (EIPs) introduced +// and accepted by the Ethereum core developers into the Ethash consensus. +var AllEthashProtocolChanges = params.NewConfig2( + params.Activations{ + forks.Homestead: 0, + forks.TangerineWhistle: 0, + forks.SpuriousDragon: 0, + forks.Byzantium: 0, + forks.Constantinople: 0, + forks.Petersburg: 0, + forks.Istanbul: 0, + forks.MuirGlacier: 0, + forks.Berlin: 0, + forks.London: 0, + forks.ArrowGlacier: 0, + forks.GrayGlacier: 0, + }, + params.NewChainID("1337"), + params.NewTerminalTotalDifficulty("0xffffffffffffff"), +) + +// MergedTestChainConfig2 contains every protocol change (EIPs) introduced +// and accepted by the Ethereum core developers for testing purposes. +var MergedTestChainConfig = params.NewConfig2( + params.Activations{ + forks.Homestead: 0, + forks.TangerineWhistle: 0, + forks.SpuriousDragon: 0, + forks.Byzantium: 0, + forks.Constantinople: 0, + forks.Petersburg: 0, + forks.Istanbul: 0, + forks.MuirGlacier: 0, + forks.Berlin: 0, + forks.London: 0, + forks.ArrowGlacier: 0, + forks.GrayGlacier: 0, + forks.Paris: 0, + forks.Shanghai: 0, + forks.Cancun: 0, + forks.Prague: 0, + forks.Osaka: 0, + }, + params.NewChainID("1"), + params.NewTerminalTotalDifficulty("0"), + params.BlobSchedule{ + forks.Cancun: *params.DefaultCancunBlobConfig, + forks.Prague: *params.DefaultPragueBlobConfig, + forks.Osaka: *params.DefaultOsakaBlobConfig, + }, +) diff --git a/params/presets/presets_test.go b/params/presets/presets_test.go new file mode 100644 index 0000000000..e077e9494a --- /dev/null +++ b/params/presets/presets_test.go @@ -0,0 +1,12 @@ +package presets + +import "testing" + +func TestPresetsValidate(t *testing.T) { + if err := Mainnet.Validate(); err != nil { + t.Fatal("mainnet is invalid:", err) + } + if err := Sepolia.Validate(); err != nil { + t.Fatal("sepolia is invalid:", err) + } +} diff --git a/params/registry.go b/params/registry.go index aed4888b8b..1fd71c7179 100644 --- a/params/registry.go +++ b/params/registry.go @@ -21,13 +21,15 @@ import ( "reflect" ) -type ParameterType any +type ParameterType interface{ + Validate(*Config2) error +} type paramInfo struct { rtype reflect.Type name string optional bool - defaultVal any + defaultVal ParameterType } var ( @@ -35,12 +37,6 @@ var ( paramRegistryByName = map[string]*paramInfo{} ) -type Parameter[T ParameterType] struct { - Name string - Optional bool - Default T -} - // Get retrieves the value of a chain parameter. func Get[T ParameterType](cfg *Config2) T { for _, p := range cfg.param { @@ -57,6 +53,14 @@ func Get[T ParameterType](cfg *Config2) T { return info.defaultVal.(T) } +// Parameter is the definition of a chain parameter. +type Parameter[T ParameterType] struct { + Name string + Optional bool + Default T +} + +// Define creates a chain parameter in the registry. func Define[T ParameterType](def Parameter[T]) { var z T info, defined := paramRegistryByName[def.Name]