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  }