github.com/ethereum/go-ethereum@v1.14.3/beacon/types/config.go (about)

     1  // Copyright 2022 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package types
    18  
    19  import (
    20  	"crypto/sha256"
    21  	"fmt"
    22  	"math"
    23  	"os"
    24  	"slices"
    25  	"sort"
    26  	"strconv"
    27  	"strings"
    28  
    29  	"github.com/ethereum/go-ethereum/beacon/merkle"
    30  	"github.com/ethereum/go-ethereum/common"
    31  	"github.com/ethereum/go-ethereum/common/hexutil"
    32  	"github.com/ethereum/go-ethereum/log"
    33  	"gopkg.in/yaml.v3"
    34  )
    35  
    36  // syncCommitteeDomain specifies the signatures specific use to avoid clashes
    37  // across signing different data structures.
    38  const syncCommitteeDomain = 7
    39  
    40  var knownForks = []string{"GENESIS", "ALTAIR", "BELLATRIX", "CAPELLA", "DENEB"}
    41  
    42  // Fork describes a single beacon chain fork and also stores the calculated
    43  // signature domain used after this fork.
    44  type Fork struct {
    45  	// Name of the fork in the chain config (config.yaml) file
    46  	Name string
    47  
    48  	// Epoch when given fork version is activated
    49  	Epoch uint64
    50  
    51  	// Fork version, see https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types
    52  	Version []byte
    53  
    54  	// index in list of known forks or MaxInt if unknown
    55  	knownIndex int
    56  
    57  	// calculated by computeDomain, based on fork version and genesis validators root
    58  	domain merkle.Value
    59  }
    60  
    61  // computeDomain returns the signature domain based on the given fork version
    62  // and genesis validator set root.
    63  func (f *Fork) computeDomain(genesisValidatorsRoot common.Hash) {
    64  	var (
    65  		hasher        = sha256.New()
    66  		forkVersion32 merkle.Value
    67  		forkDataRoot  merkle.Value
    68  	)
    69  	copy(forkVersion32[:], f.Version)
    70  	hasher.Write(forkVersion32[:])
    71  	hasher.Write(genesisValidatorsRoot[:])
    72  	hasher.Sum(forkDataRoot[:0])
    73  
    74  	f.domain[0] = syncCommitteeDomain
    75  	copy(f.domain[4:], forkDataRoot[:28])
    76  }
    77  
    78  // Forks is the list of all beacon chain forks in the chain configuration.
    79  type Forks []*Fork
    80  
    81  // domain returns the signature domain for the given epoch (assumes that domains
    82  // have already been calculated).
    83  func (f Forks) domain(epoch uint64) (merkle.Value, error) {
    84  	for i := len(f) - 1; i >= 0; i-- {
    85  		if epoch >= f[i].Epoch {
    86  			return f[i].domain, nil
    87  		}
    88  	}
    89  	return merkle.Value{}, fmt.Errorf("unknown fork for epoch %d", epoch)
    90  }
    91  
    92  // SigningRoot calculates the signing root of the given header.
    93  func (f Forks) SigningRoot(header Header) (common.Hash, error) {
    94  	domain, err := f.domain(header.Epoch())
    95  	if err != nil {
    96  		return common.Hash{}, err
    97  	}
    98  	var (
    99  		signingRoot common.Hash
   100  		headerHash  = header.Hash()
   101  		hasher      = sha256.New()
   102  	)
   103  	hasher.Write(headerHash[:])
   104  	hasher.Write(domain[:])
   105  	hasher.Sum(signingRoot[:0])
   106  
   107  	return signingRoot, nil
   108  }
   109  
   110  func (f Forks) Len() int      { return len(f) }
   111  func (f Forks) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
   112  func (f Forks) Less(i, j int) bool {
   113  	if f[i].Epoch != f[j].Epoch {
   114  		return f[i].Epoch < f[j].Epoch
   115  	}
   116  	return f[i].knownIndex < f[j].knownIndex
   117  }
   118  
   119  // ChainConfig contains the beacon chain configuration.
   120  type ChainConfig struct {
   121  	GenesisTime           uint64      // Unix timestamp of slot 0
   122  	GenesisValidatorsRoot common.Hash // Root hash of the genesis validator set, used for signature domain calculation
   123  	Forks                 Forks
   124  }
   125  
   126  // ForkAtEpoch returns the latest active fork at the given epoch.
   127  func (c *ChainConfig) ForkAtEpoch(epoch uint64) Fork {
   128  	for i := len(c.Forks) - 1; i >= 0; i-- {
   129  		if c.Forks[i].Epoch <= epoch {
   130  			return *c.Forks[i]
   131  		}
   132  	}
   133  	return Fork{}
   134  }
   135  
   136  // AddFork adds a new item to the list of forks.
   137  func (c *ChainConfig) AddFork(name string, epoch uint64, version []byte) *ChainConfig {
   138  	knownIndex := slices.Index(knownForks, name)
   139  	if knownIndex == -1 {
   140  		knownIndex = math.MaxInt // assume that the unknown fork happens after the known ones
   141  		if epoch != math.MaxUint64 {
   142  			log.Warn("Unknown fork in config.yaml", "fork name", name, "known forks", knownForks)
   143  		}
   144  	}
   145  	fork := &Fork{
   146  		Name:       name,
   147  		Epoch:      epoch,
   148  		Version:    version,
   149  		knownIndex: knownIndex,
   150  	}
   151  	fork.computeDomain(c.GenesisValidatorsRoot)
   152  	c.Forks = append(c.Forks, fork)
   153  	sort.Sort(c.Forks)
   154  	return c
   155  }
   156  
   157  // LoadForks parses the beacon chain configuration file (config.yaml) and extracts
   158  // the list of forks.
   159  func (c *ChainConfig) LoadForks(path string) error {
   160  	file, err := os.ReadFile(path)
   161  	if err != nil {
   162  		return fmt.Errorf("failed to read beacon chain config file: %v", err)
   163  	}
   164  	config := make(map[string]string)
   165  	if err := yaml.Unmarshal(file, &config); err != nil {
   166  		return fmt.Errorf("failed to parse beacon chain config file: %v", err)
   167  	}
   168  	var (
   169  		versions = make(map[string][]byte)
   170  		epochs   = make(map[string]uint64)
   171  	)
   172  	epochs["GENESIS"] = 0
   173  
   174  	for key, value := range config {
   175  		if strings.HasSuffix(key, "_FORK_VERSION") {
   176  			name := key[:len(key)-len("_FORK_VERSION")]
   177  			if v, err := hexutil.Decode(value); err == nil {
   178  				versions[name] = v
   179  			} else {
   180  				return fmt.Errorf("failed to decode hex fork id %q in beacon chain config file: %v", value, err)
   181  			}
   182  		}
   183  		if strings.HasSuffix(key, "_FORK_EPOCH") {
   184  			name := key[:len(key)-len("_FORK_EPOCH")]
   185  			if v, err := strconv.ParseUint(value, 10, 64); err == nil {
   186  				epochs[name] = v
   187  			} else {
   188  				return fmt.Errorf("failed to parse epoch number %q in beacon chain config file: %v", value, err)
   189  			}
   190  		}
   191  	}
   192  	for name, epoch := range epochs {
   193  		if version, ok := versions[name]; ok {
   194  			delete(versions, name)
   195  			c.AddFork(name, epoch, version)
   196  		} else {
   197  			return fmt.Errorf("fork id missing for %q in beacon chain config file", name)
   198  		}
   199  	}
   200  	for name := range versions {
   201  		return fmt.Errorf("epoch number missing for fork %q in beacon chain config file", name)
   202  	}
   203  	return nil
   204  }