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]