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 }