github.com/theQRL/go-zond@v0.1.1/core/forkchoice.go (about) 1 // Copyright 2021 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 core 18 19 import ( 20 crand "crypto/rand" 21 "errors" 22 "math/big" 23 mrand "math/rand" 24 25 "github.com/theQRL/go-zond/common" 26 "github.com/theQRL/go-zond/common/math" 27 "github.com/theQRL/go-zond/core/types" 28 "github.com/theQRL/go-zond/log" 29 "github.com/theQRL/go-zond/params" 30 ) 31 32 // ChainReader defines a small collection of methods needed to access the local 33 // blockchain during header verification. It's implemented by both blockchain 34 // and lightchain. 35 type ChainReader interface { 36 // Config retrieves the header chain's chain configuration. 37 Config() *params.ChainConfig 38 39 // GetTd returns the total difficulty of a local block. 40 GetTd(common.Hash, uint64) *big.Int 41 } 42 43 // ForkChoice is the fork chooser based on the highest total difficulty of the 44 // chain(the fork choice used in the eth1) and the external fork choice (the fork 45 // choice used in the eth2). This main goal of this ForkChoice is not only for 46 // offering fork choice during the eth1/2 merge phase, but also keep the compatibility 47 // for all other proof-of-work networks. 48 type ForkChoice struct { 49 chain ChainReader 50 rand *mrand.Rand 51 52 // preserve is a helper function used in td fork choice. 53 // Miners will prefer to choose the local mined block if the 54 // local td is equal to the extern one. It can be nil for light 55 // client 56 preserve func(header *types.Header) bool 57 } 58 59 func NewForkChoice(chainReader ChainReader, preserve func(header *types.Header) bool) *ForkChoice { 60 // Seed a fast but crypto originating random generator 61 seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) 62 if err != nil { 63 log.Crit("Failed to initialize random seed", "err", err) 64 } 65 return &ForkChoice{ 66 chain: chainReader, 67 rand: mrand.New(mrand.NewSource(seed.Int64())), 68 preserve: preserve, 69 } 70 } 71 72 // ReorgNeeded returns whether the reorg should be applied 73 // based on the given external header and local canonical chain. 74 // In the td mode, the new head is chosen if the corresponding 75 // total difficulty is higher. In the extern mode, the trusted 76 // header is always selected as the head. 77 func (f *ForkChoice) ReorgNeeded(current *types.Header, extern *types.Header) (bool, error) { 78 var ( 79 localTD = f.chain.GetTd(current.Hash(), current.Number.Uint64()) 80 externTd = f.chain.GetTd(extern.Hash(), extern.Number.Uint64()) 81 ) 82 if localTD == nil || externTd == nil { 83 return false, errors.New("missing td") 84 } 85 // Accept the new header as the chain head if the transition 86 // is already triggered. We assume all the headers after the 87 // transition come from the trusted consensus layer. 88 if ttd := f.chain.Config().TerminalTotalDifficulty; ttd != nil && ttd.Cmp(externTd) <= 0 { 89 return true, nil 90 } 91 92 // If the total difficulty is higher than our known, add it to the canonical chain 93 if diff := externTd.Cmp(localTD); diff > 0 { 94 return true, nil 95 } else if diff < 0 { 96 return false, nil 97 } 98 // Local and external difficulty is identical. 99 // Second clause in the if statement reduces the vulnerability to selfish mining. 100 // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf 101 reorg := false 102 externNum, localNum := extern.Number.Uint64(), current.Number.Uint64() 103 if externNum < localNum { 104 reorg = true 105 } else if externNum == localNum { 106 var currentPreserve, externPreserve bool 107 if f.preserve != nil { 108 currentPreserve, externPreserve = f.preserve(current), f.preserve(extern) 109 } 110 reorg = !currentPreserve && (externPreserve || f.rand.Float64() < 0.5) 111 } 112 return reorg, nil 113 }