github.com/amazechain/amc@v0.1.3/internal/forkchoice.go (about) 1 // Copyright 2023 The AmazeChain Authors 2 // This file is part of the AmazeChain library. 3 // 4 // The AmazeChain 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 AmazeChain 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 AmazeChain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package internal 18 19 import ( 20 crand "crypto/rand" 21 "github.com/holiman/uint256" 22 "math" 23 "math/big" 24 mrand "math/rand" 25 26 block2 "github.com/amazechain/amc/common/block" 27 "github.com/amazechain/amc/common/types" 28 "github.com/amazechain/amc/log" 29 ) 30 31 // ChainReader defines a small collection of methods needed to access the local 32 // blockchain during header verification. It's implemented by both blockchain 33 // and lightchain. 34 type ChainReader interface { 35 // Config retrieves the header chain's chain configuration. 36 //Config() *params.ChainConfig 37 38 // GetTd returns the total difficulty of a local block. 39 GetTd(types.Hash, *uint256.Int) *uint256.Int 40 } 41 42 // ForkChoice is the fork chooser based on the highest total difficulty of the 43 // chain(the fork choice used in the eth1) and the external fork choice (the fork 44 // choice used in the eth2). This main goal of this ForkChoice is not only for 45 // offering fork choice during the eth1/2 merge phase, but also keep the compatibility 46 // for all other proof-of-work networks. 47 type ForkChoice struct { 48 chain ChainReader 49 rand *mrand.Rand 50 51 // preserve is a helper function used in td fork choice. 52 // Miners will prefer to choose the local mined block if the 53 // local td is equal to the extern one. It can be nil for light 54 // client 55 preserve func(header block2.IHeader) bool 56 } 57 58 func NewForkChoice(chainReader ChainReader, preserve func(header block2.IHeader) bool) *ForkChoice { 59 // Seed a fast but crypto originating random generator 60 seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) 61 if err != nil { 62 log.Error("Failed to initialize random seed", "err", err) 63 } 64 return &ForkChoice{ 65 chain: chainReader, 66 rand: mrand.New(mrand.NewSource(seed.Int64())), 67 preserve: preserve, 68 } 69 } 70 71 // ReorgNeeded returns whether the reorg should be applied 72 // based on the given external header and local canonical chain. 73 // In the td mode, the new head is chosen if the corresponding 74 // total difficulty is higher. In the extern mode, the trusted 75 // header is always selected as the head. 76 func (f *ForkChoice) ReorgNeeded(current block2.IHeader, header block2.IHeader) (bool, error) { 77 var ( 78 localTD = f.chain.GetTd(current.Hash(), current.Number64()) 79 externTd = f.chain.GetTd(header.Hash(), header.Number64()) 80 ) 81 log.Tracef("ForkChoice.ReorgNeeded: localID = %d, externTd = %d", localTD.Uint64(), externTd.Uint64()) 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.ToBig()) <= 0 { 89 // return true, nil 90 //} 91 // If the total difficulty is higher than our known, add it to the canonical chain 92 // Second clause in the if statement reduces the vulnerability to selfish mining. 93 // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf 94 reorg := externTd.Cmp(localTD) > 0 95 if !reorg && externTd.Cmp(localTD) == 0 { 96 number, headNumber := header.Number64().Uint64(), current.Number64().Uint64() 97 if number < headNumber { 98 reorg = true 99 } else if number == headNumber { 100 var currentPreserve, externPreserve bool 101 if f.preserve != nil { 102 currentPreserve, externPreserve = f.preserve(current), f.preserve(header) 103 } 104 reorg = !currentPreserve && (externPreserve || f.rand.Float64() < 0.5) 105 } 106 } 107 return reorg, nil 108 }