github.com/ethereum/go-ethereum@v1.16.1/beacon/params/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 params
    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  // ClientConfig contains beacon light client configuration.
    43  type ClientConfig struct {
    44  	ChainConfig
    45  	Apis         []string
    46  	CustomHeader map[string]string
    47  	Threshold    int
    48  	NoFilter     bool
    49  }
    50  
    51  // ChainConfig contains the beacon chain configuration.
    52  type ChainConfig struct {
    53  	GenesisTime           uint64      // Unix timestamp of slot 0
    54  	GenesisValidatorsRoot common.Hash // Root hash of the genesis validator set, used for signature domain calculation
    55  	Forks                 Forks
    56  	Checkpoint            common.Hash
    57  	CheckpointFile        string
    58  }
    59  
    60  // ForkAtEpoch returns the latest active fork at the given epoch.
    61  func (c *ChainConfig) ForkAtEpoch(epoch uint64) Fork {
    62  	for i := len(c.Forks) - 1; i >= 0; i-- {
    63  		if c.Forks[i].Epoch <= epoch {
    64  			return *c.Forks[i]
    65  		}
    66  	}
    67  	return Fork{}
    68  }
    69  
    70  // AddFork adds a new item to the list of forks.
    71  func (c *ChainConfig) AddFork(name string, epoch uint64, version []byte) *ChainConfig {
    72  	knownIndex := slices.Index(knownForks, name)
    73  	if knownIndex == -1 {
    74  		knownIndex = math.MaxInt // assume that the unknown fork happens after the known ones
    75  		if epoch != math.MaxUint64 {
    76  			log.Warn("Unknown fork in config.yaml", "fork name", name, "known forks", knownForks)
    77  		}
    78  	}
    79  	fork := &Fork{
    80  		Name:       name,
    81  		Epoch:      epoch,
    82  		Version:    version,
    83  		knownIndex: knownIndex,
    84  	}
    85  	fork.computeDomain(c.GenesisValidatorsRoot)
    86  	c.Forks = append(c.Forks, fork)
    87  	sort.Sort(c.Forks)
    88  	return c
    89  }
    90  
    91  // LoadForks parses the beacon chain configuration file (config.yaml) and extracts
    92  // the list of forks.
    93  func (c *ChainConfig) LoadForks(path string) error {
    94  	file, err := os.ReadFile(path)
    95  	if err != nil {
    96  		return fmt.Errorf("failed to read beacon chain config file: %v", err)
    97  	}
    98  	config := make(map[string]string)
    99  	if err := yaml.Unmarshal(file, &config); err != nil {
   100  		return fmt.Errorf("failed to parse beacon chain config file: %v", err)
   101  	}
   102  	var (
   103  		versions = make(map[string][]byte)
   104  		epochs   = make(map[string]uint64)
   105  	)
   106  	epochs["GENESIS"] = 0
   107  
   108  	for key, value := range config {
   109  		if strings.HasSuffix(key, "_FORK_VERSION") {
   110  			name := key[:len(key)-len("_FORK_VERSION")]
   111  			if v, err := hexutil.Decode(value); err == nil {
   112  				versions[name] = v
   113  			} else {
   114  				return fmt.Errorf("failed to decode hex fork id %q in beacon chain config file: %v", value, err)
   115  			}
   116  		}
   117  		if strings.HasSuffix(key, "_FORK_EPOCH") {
   118  			name := key[:len(key)-len("_FORK_EPOCH")]
   119  			if v, err := strconv.ParseUint(value, 10, 64); err == nil {
   120  				epochs[name] = v
   121  			} else {
   122  				return fmt.Errorf("failed to parse epoch number %q in beacon chain config file: %v", value, err)
   123  			}
   124  		}
   125  	}
   126  	for name, epoch := range epochs {
   127  		if version, ok := versions[name]; ok {
   128  			delete(versions, name)
   129  			c.AddFork(name, epoch, version)
   130  		} else {
   131  			return fmt.Errorf("fork id missing for %q in beacon chain config file", name)
   132  		}
   133  	}
   134  	for name := range versions {
   135  		return fmt.Errorf("epoch number missing for fork %q in beacon chain config file", name)
   136  	}
   137  	return nil
   138  }
   139  
   140  // Fork describes a single beacon chain fork and also stores the calculated
   141  // signature domain used after this fork.
   142  type Fork struct {
   143  	// Name of the fork in the chain config (config.yaml) file
   144  	Name string
   145  
   146  	// Epoch when given fork version is activated
   147  	Epoch uint64
   148  
   149  	// Fork version, see https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types
   150  	Version []byte
   151  
   152  	// index in list of known forks or MaxInt if unknown
   153  	knownIndex int
   154  
   155  	// calculated by computeDomain, based on fork version and genesis validators root
   156  	domain merkle.Value
   157  }
   158  
   159  // computeDomain returns the signature domain based on the given fork version
   160  // and genesis validator set root.
   161  func (f *Fork) computeDomain(genesisValidatorsRoot common.Hash) {
   162  	var (
   163  		hasher        = sha256.New()
   164  		forkVersion32 merkle.Value
   165  		forkDataRoot  merkle.Value
   166  	)
   167  	copy(forkVersion32[:], f.Version)
   168  	hasher.Write(forkVersion32[:])
   169  	hasher.Write(genesisValidatorsRoot[:])
   170  	hasher.Sum(forkDataRoot[:0])
   171  
   172  	f.domain[0] = syncCommitteeDomain
   173  	copy(f.domain[4:], forkDataRoot[:28])
   174  }
   175  
   176  // Forks is the list of all beacon chain forks in the chain configuration.
   177  type Forks []*Fork
   178  
   179  // domain returns the signature domain for the given epoch (assumes that domains
   180  // have already been calculated).
   181  func (f Forks) domain(epoch uint64) (merkle.Value, error) {
   182  	for i := len(f) - 1; i >= 0; i-- {
   183  		if epoch >= f[i].Epoch {
   184  			return f[i].domain, nil
   185  		}
   186  	}
   187  	return merkle.Value{}, fmt.Errorf("unknown fork for epoch %d", epoch)
   188  }
   189  
   190  // SigningRoot calculates the signing root of the given header.
   191  func (f Forks) SigningRoot(epoch uint64, root common.Hash) (common.Hash, error) {
   192  	domain, err := f.domain(epoch)
   193  	if err != nil {
   194  		return common.Hash{}, err
   195  	}
   196  	var (
   197  		signingRoot common.Hash
   198  		hasher      = sha256.New()
   199  	)
   200  	hasher.Write(root[:])
   201  	hasher.Write(domain[:])
   202  	hasher.Sum(signingRoot[:0])
   203  
   204  	return signingRoot, nil
   205  }
   206  
   207  func (f Forks) Len() int      { return len(f) }
   208  func (f Forks) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
   209  func (f Forks) Less(i, j int) bool {
   210  	if f[i].Epoch != f[j].Epoch {
   211  		return f[i].Epoch < f[j].Epoch
   212  	}
   213  	return f[i].knownIndex < f[j].knownIndex
   214  }
   215  
   216  // SetCheckpointFile sets the checkpoint import/export file name and attempts to
   217  // read the checkpoint from the file if it already exists. It returns true if
   218  // a checkpoint has been loaded.
   219  func (c *ChainConfig) SetCheckpointFile(checkpointFile string) (bool, error) {
   220  	c.CheckpointFile = checkpointFile
   221  	file, err := os.ReadFile(checkpointFile)
   222  	if os.IsNotExist(err) {
   223  		return false, nil // did not load checkpoint
   224  	}
   225  	if err != nil {
   226  		return false, fmt.Errorf("failed to read beacon checkpoint file: %v", err)
   227  	}
   228  	cp, err := hexutil.Decode(string(file))
   229  	if err != nil {
   230  		return false, fmt.Errorf("failed to decode hex string in beacon checkpoint file: %v", err)
   231  	}
   232  	if len(cp) != 32 {
   233  		return false, fmt.Errorf("invalid hex string length in beacon checkpoint file: %d", len(cp))
   234  	}
   235  	copy(c.Checkpoint[:len(cp)], cp)
   236  	return true, nil
   237  }
   238  
   239  // SaveCheckpointToFile saves the given checkpoint to file if a checkpoint
   240  // import/export file has been specified.
   241  func (c *ChainConfig) SaveCheckpointToFile(checkpoint common.Hash) (bool, error) {
   242  	if c.CheckpointFile == "" {
   243  		return false, nil
   244  	}
   245  	err := os.WriteFile(c.CheckpointFile, []byte(checkpoint.Hex()), 0600)
   246  	return err == nil, err
   247  }