Skip to content

Commit

Permalink
Merge pull request #183 from ethpandaops/pk910/dynamic-spec-check
Browse files Browse the repository at this point in the history
add dynamic spec check conditions
  • Loading branch information
pk910 authored Dec 2, 2024
2 parents 123290f + 7ceafe8 commit 7a69b2e
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 16 deletions.
87 changes: 73 additions & 14 deletions clients/consensus/chainspec.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package consensus

import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"sync"
"time"

"github.com/attestantio/go-eth2-client/spec/phase0"
"gopkg.in/Knetic/govaluate.v3"
)

type ForkVersion struct {
Expand All @@ -17,7 +21,7 @@ type ForkVersion struct {
// https://github.com/ethereum/consensus-specs/blob/dev/configs/mainnet.yaml
type ChainSpec struct {
PresetBase string `yaml:"PRESET_BASE"`
ConfigName string `yaml:"CONFIG_NAME" nocheck:"true"`
ConfigName string `yaml:"CONFIG_NAME" check-if:"false"`
MinGenesisTime time.Time `yaml:"MIN_GENESIS_TIME"`
GenesisForkVersion phase0.Version `yaml:"GENESIS_FORK_VERSION"`
AltairForkVersion phase0.Version `yaml:"ALTAIR_FORK_VERSION"`
Expand All @@ -28,10 +32,10 @@ type ChainSpec struct {
CapellaForkEpoch *uint64 `yaml:"CAPELLA_FORK_EPOCH"`
DenebForkVersion phase0.Version `yaml:"DENEB_FORK_VERSION"`
DenebForkEpoch *uint64 `yaml:"DENEB_FORK_EPOCH"`
ElectraForkVersion phase0.Version `yaml:"ELECTRA_FORK_VERSION"`
ElectraForkVersion phase0.Version `yaml:"ELECTRA_FORK_VERSION" check-if-fork:"ElectraForkEpoch"`
ElectraForkEpoch *uint64 `yaml:"ELECTRA_FORK_EPOCH"`
Eip7594ForkVersion phase0.Version `yaml:"EIP7594_FORK_VERSION" nocheck:"true"`
Eip7594ForkEpoch *uint64 `yaml:"EIP7594_FORK_EPOCH" nocheck:"true"`
Eip7594ForkVersion phase0.Version `yaml:"EIP7594_FORK_VERSION" check-if-fork:"Eip7594ForkEpoch"`
Eip7594ForkEpoch *uint64 `yaml:"EIP7594_FORK_EPOCH"`
SecondsPerSlot time.Duration `yaml:"SECONDS_PER_SLOT"`
SlotsPerEpoch uint64 `yaml:"SLOTS_PER_EPOCH"`
EpochsPerHistoricalVector uint64 `yaml:"EPOCHS_PER_HISTORICAL_VECTOR"`
Expand All @@ -40,7 +44,7 @@ type ChainSpec struct {
MinSeedLookahead uint64 `yaml:"MIN_SEED_LOOKAHEAD"`
ShuffleRoundCount uint64 `yaml:"SHUFFLE_ROUND_COUNT"`
MaxEffectiveBalance uint64 `yaml:"MAX_EFFECTIVE_BALANCE"`
MaxEffectiveBalanceElectra uint64 `yaml:"MAX_EFFECTIVE_BALANCE_ELECTRA"`
MaxEffectiveBalanceElectra uint64 `yaml:"MAX_EFFECTIVE_BALANCE_ELECTRA" check-if-fork:"ElectraForkEpoch"`
TargetCommitteeSize uint64 `yaml:"TARGET_COMMITTEE_SIZE"`
MaxCommitteesPerSlot uint64 `yaml:"MAX_COMMITTEES_PER_SLOT"`
MinPerEpochChurnLimit uint64 `yaml:"MIN_PER_EPOCH_CHURN_LIMIT"`
Expand All @@ -50,32 +54,59 @@ type ChainSpec struct {
DomainSyncCommittee phase0.DomainType `yaml:"DOMAIN_SYNC_COMMITTEE"`
SyncCommitteeSize uint64 `yaml:"SYNC_COMMITTEE_SIZE"`
DepositContractAddress []byte `yaml:"DEPOSIT_CONTRACT_ADDRESS"`
MaxConsolidationRequestsPerPayload uint64 `yaml:"MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD"`
MaxWithdrawalRequestsPerPayload uint64 `yaml:"MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD"`
MaxConsolidationRequestsPerPayload uint64 `yaml:"MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD" check-if-fork:"ElectraForkEpoch"`
MaxWithdrawalRequestsPerPayload uint64 `yaml:"MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD" check-if-fork:"ElectraForkEpoch"`
DepositChainId uint64 `yaml:"DEPOSIT_CHAIN_ID"`
MinActivationBalance uint64 `yaml:"MIN_ACTIVATION_BALANCE"`

// EIP7594: PeerDAS
NumberOfColumns *uint64 `yaml:"NUMBER_OF_COLUMNS" nocheck:"true"`
DataColumnSidecarSubnetCount *uint64 `yaml:"DATA_COLUMN_SIDECAR_SUBNET_COUNT" nocheck:"true"`
CustodyRequirement *uint64 `yaml:"CUSTODY_REQUIREMENT" nocheck:"true"`
NumberOfColumns *uint64 `yaml:"NUMBER_OF_COLUMNS" check-if-fork:"Eip7594ForkEpoch"`
DataColumnSidecarSubnetCount *uint64 `yaml:"DATA_COLUMN_SIDECAR_SUBNET_COUNT" check-if-fork:"Eip7594ForkEpoch"`
CustodyRequirement *uint64 `yaml:"CUSTODY_REQUIREMENT" check-if-fork:"Eip7594ForkEpoch"`

// additional dora specific specs
WhiskForkEpoch *uint64
}

var byteType = reflect.TypeOf(byte(0))
var specExpressionCache = map[string]*govaluate.EvaluableExpression{}
var specExpressionCacheMutex sync.Mutex

func (chain *ChainSpec) CheckMismatch(chain2 *ChainSpec) []string {
func (chain *ChainSpec) CheckMismatch(chain2 *ChainSpec) ([]string, error) {
mismatches := []string{}

chainT := reflect.ValueOf(chain).Elem()
chain2T := reflect.ValueOf(chain2).Elem()

genericSpecValues := map[string]any{}
specData, err := json.Marshal(chain)
if err != nil {
return nil, fmt.Errorf("error marshalling chain spec: %v", err)
}
err = json.Unmarshal(specData, &genericSpecValues)
if err != nil {
return nil, fmt.Errorf("error unmarshalling chain spec: %v", err)
}

for i := 0; i < chainT.NumField(); i++ {
fieldT := chainT.Type().Field(i)
if fieldT.Tag.Get("nocheck") == "true" {
continue

// Check both types of conditions
checkIfExpression := fieldT.Tag.Get("check-if")
checkIfFork := fieldT.Tag.Get("check-if-fork")

if checkIfFork != "" {
checkIfExpression = fmt.Sprintf("(%s ?? 18446744073709551615) < 18446744073709551615", checkIfFork)
}

if checkIfExpression != "" {
ok, err := chain.checkIf(checkIfExpression, genericSpecValues)
if err != nil {
return nil, fmt.Errorf("error checking if expression: %v", err)
}
if !ok {
continue
}
}

fieldV := chainT.Field(i)
Expand Down Expand Up @@ -107,7 +138,35 @@ func (chain *ChainSpec) CheckMismatch(chain2 *ChainSpec) []string {
}
}

return mismatches
return mismatches, nil
}

func (chain *ChainSpec) checkIf(expressionStr string, genericSpecValues map[string]any) (bool, error) {
specExpressionCacheMutex.Lock()
expression, ok := specExpressionCache[expressionStr]
if !ok {
var err error
expression, err = govaluate.NewEvaluableExpression(expressionStr)
if err != nil {
specExpressionCacheMutex.Unlock()
return false, fmt.Errorf("error parsing dynamic spec expression: %v", err)
}

specExpressionCache[expressionStr] = expression
}
specExpressionCacheMutex.Unlock()

result, err := expression.Evaluate(genericSpecValues)
if err != nil {
return false, fmt.Errorf("error evaluating dynamic spec expression: %v", err)
}

value, ok := result.(bool)
if ok {
return value, nil
}

return false, nil
}

func (chain *ChainSpec) Clone() *ChainSpec {
Expand Down
10 changes: 8 additions & 2 deletions clients/consensus/chainstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ func (cs *ChainState) setClientSpecs(specValues map[string]interface{}) (error,
var warning error

if cs.specs != nil {
mismatches := cs.specs.CheckMismatch(specs)
mismatches, err := cs.specs.CheckMismatch(specs)
if err != nil {
return nil, err
}
if len(mismatches) > 0 {
return nil, fmt.Errorf("spec mismatch: %v", strings.Join(mismatches, ", "))
}
Expand All @@ -85,7 +88,10 @@ func (cs *ChainState) setClientSpecs(specValues map[string]interface{}) (error,
return nil, err
}

mismatches = cs.specs.CheckMismatch(newSpecs)
mismatches, err = cs.specs.CheckMismatch(newSpecs)
if err != nil {
return nil, err
}
if len(mismatches) > 0 {
warning = fmt.Errorf("spec missing: %v", strings.Join(mismatches, ", "))
}
Expand Down

0 comments on commit 7a69b2e

Please sign in to comment.