params: new param system

This commit is contained in:
Felix Lange 2025-07-18 13:00:36 +02:00
parent 6b26791752
commit b25e44a841
3 changed files with 103 additions and 168 deletions

View file

@ -9,79 +9,40 @@ import (
) )
// ChainID // ChainID
type ChainID big.Int var ChainID = Define(T[*big.Int]{
Name: "chainId",
func NewChainID(input string) *ChainID { Validate: func(v *big.Int, cfg *Config2) error {
b, ok := new(big.Int).SetString(input, 0) if v.Sign() <= 0 {
if !ok { return fmt.Errorf("invalid chainID value %v", v)
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 return nil
} },
})
// TerminalTotalDifficulty (TTD) is the total difficulty value where // TerminalTotalDifficulty (TTD) is the total difficulty value where
type TerminalTotalDifficulty big.Int var TerminalTotalDifficulty = Define(T[*big.Int]{
Name: "terminalTotalDifficulty",
func NewTerminalTotalDifficulty(input string) *TerminalTotalDifficulty { Optional: true,
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()
}
func (v *TerminalTotalDifficulty) UnmarshalText(input []byte) error {
return (*big.Int)(v).UnmarshalText(input)
}
func (v *TerminalTotalDifficulty) BigInt() *big.Int {
return (*big.Int)(v)
}
func (v *TerminalTotalDifficulty) Validate(cfg *Config2) error {
return nil
}
// DepositContractAddress configures the location of the deposit contract. // DepositContractAddress configures the location of the deposit contract.
type DepositContractAddress common.Address var DepositContractAddress = Define(T[common.Address]{
Name: "depositContractAddress",
func (v DepositContractAddress) Validate(cfg *Config2) error { Optional: true,
return nil })
}
// This configures the EIP-4844 parameters across forks. // This configures the EIP-4844 parameters across forks.
// There must be an entry for each fork // There must be an entry for each fork
type BlobSchedule map[forks.Fork]BlobConfig var BlobSchedule = Define(T[map[forks.Fork]BlobConfig]{
Name: "blobSchedule",
Optional: true,
Validate: validateBlobSchedule,
})
// Validate checks that all forks with blobs explicitly define the blob schedule configuration. func validateBlobSchedule(schedule map[forks.Fork]BlobConfig, cfg *Config2) error {
func (v BlobSchedule) Validate(cfg *Config2) error { // Check that all forks with blobs explicitly define the blob schedule configuration.
for _, f := range forks.CanonOrder { for _, f := range forks.CanonOrder {
if f.HasBlobs() { if f.HasBlobs() {
schedule := Get[BlobSchedule](cfg)
bcfg, defined := schedule[f] bcfg, defined := schedule[f]
if cfg.Scheduled(f) && !defined { if cfg.Scheduled(f) && !defined {
return fmt.Errorf("invalid chain configuration: missing entry for fork %q in blobSchedule", f) return fmt.Errorf("invalid chain configuration: missing entry for fork %q in blobSchedule", f)
@ -99,32 +60,7 @@ func (v BlobSchedule) Validate(cfg *Config2) error {
// DAOForkSupport is the chain parameter that configures the DAO fork. // DAOForkSupport is the chain parameter that configures the DAO fork.
// true=supports or false=opposes the fork. // true=supports or false=opposes the fork.
// The default value is true. // The default value is true.
type DAOForkSupport bool var DAOForkSupport = Define(T[bool]{
func (v DAOForkSupport) Validate(cfg *Config2) error {
return nil
}
func init() {
Define(Parameter[*ChainID]{
Name: "chainId",
Optional: false,
})
Define(Parameter[*TerminalTotalDifficulty]{
Name: "terminalTotalDifficulty",
Optional: false,
})
Define(Parameter[DAOForkSupport]{
Name: "daoForkSupport",
Optional: true, Optional: true,
Default: true, Default: true,
}) })
Define(Parameter[BlobSchedule]{
Name: "blobSchedule",
Optional: true,
})
Define(Parameter[DepositContractAddress]{
Name: "depositContractAddress",
Optional: true,
})
}

View file

@ -22,7 +22,6 @@ import (
"fmt" "fmt"
"maps" "maps"
"math/big" "math/big"
"reflect"
"slices" "slices"
"strings" "strings"
@ -35,21 +34,17 @@ type Activations map[forks.Fork]uint64
// Config2 represents the chain configuration. // Config2 represents the chain configuration.
type Config2 struct { type Config2 struct {
activation Activations activation Activations
param map[reflect.Type]ParameterType param map[int]any
} }
func NewConfig2(activations Activations, param ...ParameterType) *Config2 { func NewConfig2(activations Activations, param ...ParamValue) *Config2 {
cfg := &Config2{ cfg := &Config2{
activation: maps.Clone(activations), activation: maps.Clone(activations),
param: make(map[reflect.Type]ParameterType, len(param)), param: make(map[int]any, len(param)),
} }
cfg.activation[forks.Frontier] = 0 cfg.activation[forks.Frontier] = 0
for _, pv := range param { for _, pv := range param {
info, ok := findParam(pv) cfg.param[pv.id] = pv.value
if !ok {
panic(fmt.Sprintf("undefined chain parameter type %T", pv))
}
cfg.param[info.rtype] = pv
} }
return cfg return cfg
} }
@ -109,12 +104,12 @@ func (cfg *Config2) LatestFork(time uint64) forks.Fork {
func (cfg *Config2) MarshalJSON() ([]byte, error) { func (cfg *Config2) MarshalJSON() ([]byte, error) {
m := make(map[string]any) m := make(map[string]any)
// params // params
for _, p := range cfg.param { for id, value := range cfg.param {
info, ok := findParam(p) info, ok := paramRegistry[id]
if !ok { if !ok {
panic(fmt.Sprintf("unknown chain parameter %T", p)) panic(fmt.Sprintf("unknown chain parameter id %v", id))
} }
m[info.name] = p m[info.name] = value
} }
// forks // forks
for f, act := range cfg.activation { for f, act := range cfg.activation {
@ -187,15 +182,15 @@ func (cfg *Config2) decodeActivation(key string, dec *json.Decoder) error {
} }
func (cfg *Config2) decodeParameter(key string, dec *json.Decoder) error { func (cfg *Config2) decodeParameter(key string, dec *json.Decoder) error {
info, ok := paramRegistryByName[key] id, ok := paramRegistryByName[key]
if !ok { if !ok {
return fmt.Errorf("unknown chain parameter %q", key) return fmt.Errorf("unknown chain parameter %q", key)
} }
v := reflect.New(info.rtype).Interface() v := paramRegistry[id].new()
if err := dec.Decode(v); err != nil { if err := dec.Decode(v); err != nil {
return err return err
} }
cfg.param[info.rtype] = v.(ParameterType) cfg.param[id] = v
return nil return nil
} }
@ -235,15 +230,15 @@ func (cfg *Config2) Validate() error {
} }
// Check parameters. // Check parameters.
for rtype, info := range paramRegistry { for id, info := range paramRegistry {
v, isSet := cfg.param[rtype] v, isSet := cfg.param[id]
if !isSet { if !isSet {
if !info.optional { if !info.optional {
return fmt.Errorf("required chain parameter %s is not set", info.name) return fmt.Errorf("required chain parameter %s is not set", info.name)
} }
v = info.defaultVal v = info.defaultValue
} }
if err := v.Validate(cfg); err != nil { if err := info.validate(v, cfg); err != nil {
return fmt.Errorf("invalid %s: %w", info.name, err) return fmt.Errorf("invalid %s: %w", info.name, err)
} }
} }
@ -296,12 +291,10 @@ func (cfg *Config2) checkCompatible(newcfg *Config2, num uint64, time uint64) *C
} }
} }
if cfg.Active(forks.DAO, num, time) && Get[DAOForkSupport](cfg) != Get[DAOForkSupport](newcfg) { if cfg.Active(forks.DAO, num, time) && DAOForkSupport.Get(cfg) != DAOForkSupport.Get(newcfg) {
return newBlockCompatError2("DAO fork support flag", forks.DAO, cfg, newcfg) return newBlockCompatError2("DAO fork support flag", forks.DAO, cfg, newcfg)
} }
chainID := (*big.Int)(Get[*ChainID](cfg)) if cfg.Active(forks.TangerineWhistle, num, time) && !configBlockEqual(ChainID.Get(cfg), ChainID.Get(newcfg)) {
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 newBlockCompatError2("EIP158 chain ID", forks.TangerineWhistle, cfg, newcfg)
} }

View file

@ -18,72 +18,78 @@ package params
import ( import (
"fmt" "fmt"
"reflect"
) )
type ParameterType interface{ type Parameter[V any] struct{
Validate(*Config2) error info regInfo
} }
type paramInfo struct { func (p Parameter[V]) Get(cfg *Config2) V {
rtype reflect.Type v, ok := cfg.param[p.info.id]
name string if ok {
optional bool return v.(V)
defaultVal ParameterType }
return p.info.defaultValue.(V)
}
func (p Parameter[V]) V(v V) ParamValue {
return ParamValue{p.info.id, v}
}
type ParamValue struct{
id int
value any
} }
var ( var (
paramRegistry = map[reflect.Type]*paramInfo{} paramCounter int
paramRegistryByName = map[string]*paramInfo{} paramRegistry = map[int]regInfo{}
paramRegistryByName = map[string]int{}
) )
// Get retrieves the value of a chain parameter. type T[V any] struct{
func Get[T ParameterType](cfg *Config2) T {
for _, p := range cfg.param {
if v, ok := p.(T); ok {
return v
}
}
// get default
var z T
info, ok := findParam(z)
if !ok {
panic(fmt.Sprintf("unknown parameter type %T", z))
}
return info.defaultVal.(T)
}
// Parameter is the definition of a chain parameter.
type Parameter[T ParameterType] struct {
Name string Name string
Optional bool Optional bool
Default T Default V
Validate func(v V, cfg *Config2) error
}
type regInfo struct{
id int
name string
optional bool
defaultValue any
new func() any
validate func(any, *Config2) error
} }
// Define creates a chain parameter in the registry. // Define creates a chain parameter in the registry.
func Define[T ParameterType](def Parameter[T]) { func Define[V any](def T[V]) Parameter[V] {
var z T if id, defined := paramRegistryByName[def.Name]; defined {
info, defined := paramRegistryByName[def.Name] info := paramRegistry[id]
if defined { panic(fmt.Sprintf("chain parameter %q already registered with type %T", def.Name, info.defaultValue))
panic(fmt.Sprintf("chain parameter %q already registered with type %v", info.name, info.rtype))
} }
rtype := reflect.TypeOf(z)
info, defined = paramRegistry[rtype] id := paramCounter
if defined { paramCounter++
panic(fmt.Sprintf("chain parameter of type %v already registered with name %q", rtype, info.name))
} regInfo := regInfo{
info = &paramInfo{ id: id,
rtype: rtype,
name: def.Name, name: def.Name,
optional: def.Optional, optional: def.Optional,
defaultVal: def.Default, defaultValue: def.Default,
new: func() any {
var z V
return z
},
validate: func(v any, cfg *Config2) error {
if def.Validate == nil {
return nil
} }
paramRegistry[rtype] = info return def.Validate(v.(V), cfg)
paramRegistryByName[def.Name] = info },
} }
paramRegistry[id] = regInfo
func findParam(v any) (*paramInfo, bool) { paramRegistryByName[def.Name] = id
rtype := reflect.TypeOf(v) return Parameter[V]{info: regInfo}
info, ok := paramRegistry[rtype]
return info, ok
} }