github.com/phillinzzz/newBsc@v1.1.6/core/forkid/forkid.go (about) 1 // Copyright 2019 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 forkid implements EIP-2124 (https://eips.ethereum.org/EIPS/eip-2124). 18 package forkid 19 20 import ( 21 "encoding/binary" 22 "errors" 23 "hash/crc32" 24 "math" 25 "math/big" 26 "reflect" 27 "strings" 28 29 "github.com/phillinzzz/newBsc/common" 30 "github.com/phillinzzz/newBsc/core/types" 31 "github.com/phillinzzz/newBsc/log" 32 "github.com/phillinzzz/newBsc/params" 33 ) 34 35 var ( 36 // ErrRemoteStale is returned by the validator if a remote fork checksum is a 37 // subset of our already applied forks, but the announced next fork block is 38 // not on our already passed chain. 39 ErrRemoteStale = errors.New("remote needs update") 40 41 // ErrLocalIncompatibleOrStale is returned by the validator if a remote fork 42 // checksum does not match any local checksum variation, signalling that the 43 // two chains have diverged in the past at some point (possibly at genesis). 44 ErrLocalIncompatibleOrStale = errors.New("local incompatible or needs update") 45 ) 46 47 // Blockchain defines all necessary method to build a forkID. 48 type Blockchain interface { 49 // Config retrieves the chain's fork configuration. 50 Config() *params.ChainConfig 51 52 // Genesis retrieves the chain's genesis block. 53 Genesis() *types.Block 54 55 // CurrentHeader retrieves the current head header of the canonical chain. 56 CurrentHeader() *types.Header 57 } 58 59 // ID is a fork identifier as defined by EIP-2124. 60 type ID struct { 61 Hash [4]byte // CRC32 checksum of the genesis block and passed fork block numbers 62 Next uint64 // Block number of the next upcoming fork, or 0 if no forks are known 63 } 64 65 // Filter is a fork id filter to validate a remotely advertised ID. 66 type Filter func(id ID) error 67 68 // NewID calculates the Ethereum fork ID from the chain config, genesis hash, and head. 69 func NewID(config *params.ChainConfig, genesis common.Hash, head uint64) ID { 70 // Calculate the starting checksum from the genesis hash 71 hash := crc32.ChecksumIEEE(genesis[:]) 72 73 // Calculate the current fork checksum and the next fork block 74 var next uint64 75 for _, fork := range gatherForks(config) { 76 if fork <= head { 77 // Fork already passed, checksum the previous hash and the fork number 78 hash = checksumUpdate(hash, fork) 79 continue 80 } 81 next = fork 82 break 83 } 84 return ID{Hash: checksumToBytes(hash), Next: next} 85 } 86 87 func NextForkHash(config *params.ChainConfig, genesis common.Hash, head uint64) [4]byte { 88 // Calculate the starting checksum from the genesis hash 89 hash := crc32.ChecksumIEEE(genesis[:]) 90 91 // Calculate the current fork checksum and the next fork block 92 var next uint64 93 for _, fork := range gatherForks(config) { 94 if fork <= head { 95 // Fork already passed, checksum the previous hash and the fork number 96 hash = checksumUpdate(hash, fork) 97 continue 98 } 99 next = fork 100 break 101 } 102 if next == 0 { 103 return checksumToBytes(hash) 104 } else { 105 return checksumToBytes(checksumUpdate(hash, next)) 106 } 107 } 108 109 // NewIDWithChain calculates the Ethereum fork ID from an existing chain instance. 110 func NewIDWithChain(chain Blockchain) ID { 111 return NewID( 112 chain.Config(), 113 chain.Genesis().Hash(), 114 chain.CurrentHeader().Number.Uint64(), 115 ) 116 } 117 118 // NewFilter creates a filter that returns if a fork ID should be rejected or not 119 // based on the local chain's status. 120 func NewFilter(chain Blockchain) Filter { 121 return newFilter( 122 chain.Config(), 123 chain.Genesis().Hash(), 124 func() uint64 { 125 return chain.CurrentHeader().Number.Uint64() 126 }, 127 ) 128 } 129 130 // NewStaticFilter creates a filter at block zero. 131 func NewStaticFilter(config *params.ChainConfig, genesis common.Hash) Filter { 132 head := func() uint64 { return 0 } 133 return newFilter(config, genesis, head) 134 } 135 136 // newFilter is the internal version of NewFilter, taking closures as its arguments 137 // instead of a chain. The reason is to allow testing it without having to simulate 138 // an entire blockchain. 139 func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() uint64) Filter { 140 // Calculate the all the valid fork hash and fork next combos 141 var ( 142 forks = gatherForks(config) 143 sums = make([][4]byte, len(forks)+1) // 0th is the genesis 144 ) 145 hash := crc32.ChecksumIEEE(genesis[:]) 146 sums[0] = checksumToBytes(hash) 147 for i, fork := range forks { 148 hash = checksumUpdate(hash, fork) 149 sums[i+1] = checksumToBytes(hash) 150 } 151 // Add two sentries to simplify the fork checks and don't require special 152 // casing the last one. 153 forks = append(forks, math.MaxUint64) // Last fork will never be passed 154 155 // Create a validator that will filter out incompatible chains 156 return func(id ID) error { 157 // Run the fork checksum validation ruleset: 158 // 1. If local and remote FORK_CSUM matches, compare local head to FORK_NEXT. 159 // The two nodes are in the same fork state currently. They might know 160 // of differing future forks, but that's not relevant until the fork 161 // triggers (might be postponed, nodes might be updated to match). 162 // 1a. A remotely announced but remotely not passed block is already passed 163 // locally, disconnect, since the chains are incompatible. 164 // 1b. No remotely announced fork; or not yet passed locally, connect. 165 // 2. If the remote FORK_CSUM is a subset of the local past forks and the 166 // remote FORK_NEXT matches with the locally following fork block number, 167 // connect. 168 // Remote node is currently syncing. It might eventually diverge from 169 // us, but at this current point in time we don't have enough information. 170 // 3. If the remote FORK_CSUM is a superset of the local past forks and can 171 // be completed with locally known future forks, connect. 172 // Local node is currently syncing. It might eventually diverge from 173 // the remote, but at this current point in time we don't have enough 174 // information. 175 // 4. Reject in all other cases. 176 head := headfn() 177 for i, fork := range forks { 178 // If our head is beyond this fork, continue to the next (we have a dummy 179 // fork of maxuint64 as the last item to always fail this check eventually). 180 if head > fork { 181 continue 182 } 183 // Found the first unpassed fork block, check if our current state matches 184 // the remote checksum (rule #1). 185 if sums[i] == id.Hash { 186 // Fork checksum matched, check if a remote future fork block already passed 187 // locally without the local node being aware of it (rule #1a). 188 if id.Next > 0 && head >= id.Next { 189 return ErrLocalIncompatibleOrStale 190 } 191 // Haven't passed locally a remote-only fork, accept the connection (rule #1b). 192 return nil 193 } 194 // The local and remote nodes are in different forks currently, check if the 195 // remote checksum is a subset of our local forks (rule #2). 196 for j := 0; j < i; j++ { 197 if sums[j] == id.Hash { 198 // Remote checksum is a subset, validate based on the announced next fork 199 if forks[j] != id.Next { 200 return ErrRemoteStale 201 } 202 return nil 203 } 204 } 205 // Remote chain is not a subset of our local one, check if it's a superset by 206 // any chance, signalling that we're simply out of sync (rule #3). 207 for j := i + 1; j < len(sums); j++ { 208 if sums[j] == id.Hash { 209 // Yay, remote checksum is a superset, ignore upcoming forks 210 return nil 211 } 212 } 213 // No exact, subset or superset match. We are on differing chains, reject. 214 return ErrLocalIncompatibleOrStale 215 } 216 log.Error("Impossible fork ID validation", "id", id) 217 return nil // Something's very wrong, accept rather than reject 218 } 219 } 220 221 // checksumUpdate calculates the next IEEE CRC32 checksum based on the previous 222 // one and a fork block number (equivalent to CRC32(original-blob || fork)). 223 func checksumUpdate(hash uint32, fork uint64) uint32 { 224 var blob [8]byte 225 binary.BigEndian.PutUint64(blob[:], fork) 226 return crc32.Update(hash, crc32.IEEETable, blob[:]) 227 } 228 229 // checksumToBytes converts a uint32 checksum into a [4]byte array. 230 func checksumToBytes(hash uint32) [4]byte { 231 var blob [4]byte 232 binary.BigEndian.PutUint32(blob[:], hash) 233 return blob 234 } 235 236 // gatherForks gathers all the known forks and creates a sorted list out of them. 237 func gatherForks(config *params.ChainConfig) []uint64 { 238 // Gather all the fork block numbers via reflection 239 kind := reflect.TypeOf(params.ChainConfig{}) 240 conf := reflect.ValueOf(config).Elem() 241 242 var forks []uint64 243 for i := 0; i < kind.NumField(); i++ { 244 // Fetch the next field and skip non-fork rules 245 field := kind.Field(i) 246 if !strings.HasSuffix(field.Name, "Block") { 247 continue 248 } 249 if field.Type != reflect.TypeOf(new(big.Int)) { 250 continue 251 } 252 // Extract the fork rule block number and aggregate it 253 rule := conf.Field(i).Interface().(*big.Int) 254 if rule != nil { 255 forks = append(forks, rule.Uint64()) 256 } 257 } 258 // Sort the fork block numbers to permit chronological XOR 259 for i := 0; i < len(forks); i++ { 260 for j := i + 1; j < len(forks); j++ { 261 if forks[i] > forks[j] { 262 forks[i], forks[j] = forks[j], forks[i] 263 } 264 } 265 } 266 // Deduplicate block numbers applying multiple forks 267 for i := 1; i < len(forks); i++ { 268 if forks[i] == forks[i-1] { 269 forks = append(forks[:i], forks[i+1:]...) 270 i-- 271 } 272 } 273 // Skip any forks in block 0, that's the genesis ruleset 274 if len(forks) > 0 && forks[0] == 0 { 275 forks = forks[1:] 276 } 277 return forks 278 }