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
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
}
var ChainID = Define(T[*big.Int]{
Name: "chainId",
Validate: func(v *big.Int, cfg *Config2) error {
if v.Sign() <= 0 {
return fmt.Errorf("invalid chainID value %v", v)
}
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()
}
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
}
var TerminalTotalDifficulty = Define(T[*big.Int]{
Name: "terminalTotalDifficulty",
Optional: true,
})
// DepositContractAddress configures the location of the deposit contract.
type DepositContractAddress common.Address
func (v DepositContractAddress) Validate(cfg *Config2) error {
return nil
}
var DepositContractAddress = Define(T[common.Address]{
Name: "depositContractAddress",
Optional: true,
})
// This configures the EIP-4844 parameters across forks.
// 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 (v BlobSchedule) Validate(cfg *Config2) error {
func validateBlobSchedule(schedule map[forks.Fork]BlobConfig, cfg *Config2) error {
// Check that all forks with blobs explicitly define the blob schedule configuration.
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)
@ -99,32 +60,7 @@ func (v BlobSchedule) Validate(cfg *Config2) error {
// 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",
Optional: false,
})
Define(Parameter[*TerminalTotalDifficulty]{
Name: "terminalTotalDifficulty",
Optional: false,
})
Define(Parameter[DAOForkSupport]{
Name: "daoForkSupport",
Optional: true,
Default: true,
})
Define(Parameter[BlobSchedule]{
Name: "blobSchedule",
Optional: true,
})
Define(Parameter[DepositContractAddress]{
Name: "depositContractAddress",
Optional: true,
})
}
var DAOForkSupport = Define(T[bool]{
Optional: true,
Default: true,
})

View file

@ -22,7 +22,6 @@ import (
"fmt"
"maps"
"math/big"
"reflect"
"slices"
"strings"
@ -35,21 +34,17 @@ type Activations map[forks.Fork]uint64
// Config2 represents the chain configuration.
type Config2 struct {
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{
activation: maps.Clone(activations),
param: make(map[reflect.Type]ParameterType, len(param)),
param: make(map[int]any, len(param)),
}
cfg.activation[forks.Frontier] = 0
for _, pv := range param {
info, ok := findParam(pv)
if !ok {
panic(fmt.Sprintf("undefined chain parameter type %T", pv))
}
cfg.param[info.rtype] = pv
cfg.param[pv.id] = pv.value
}
return cfg
}
@ -109,12 +104,12 @@ func (cfg *Config2) LatestFork(time uint64) forks.Fork {
func (cfg *Config2) MarshalJSON() ([]byte, error) {
m := make(map[string]any)
// params
for _, p := range cfg.param {
info, ok := findParam(p)
for id, value := range cfg.param {
info, ok := paramRegistry[id]
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
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 {
info, ok := paramRegistryByName[key]
id, ok := paramRegistryByName[key]
if !ok {
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 {
return err
}
cfg.param[info.rtype] = v.(ParameterType)
cfg.param[id] = v
return nil
}
@ -235,15 +230,15 @@ func (cfg *Config2) Validate() error {
}
// Check parameters.
for rtype, info := range paramRegistry {
v, isSet := cfg.param[rtype]
for id, info := range paramRegistry {
v, isSet := cfg.param[id]
if !isSet {
if !info.optional {
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)
}
}
@ -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)
}
chainID := (*big.Int)(Get[*ChainID](cfg))
newChainID := (*big.Int)(Get[*ChainID](newcfg))
if cfg.Active(forks.TangerineWhistle, num, time) && !configBlockEqual(chainID, newChainID) {
if cfg.Active(forks.TangerineWhistle, num, time) && !configBlockEqual(ChainID.Get(cfg), ChainID.Get(newcfg)) {
return newBlockCompatError2("EIP158 chain ID", forks.TangerineWhistle, cfg, newcfg)
}

View file

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