github.com/dominant-strategies/go-quai@v0.28.2/consensus/blake3pow/consensus.go (about)

     1  package blake3pow
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math/big"
     7  	"runtime"
     8  	"time"
     9  
    10  	mapset "github.com/deckarep/golang-set"
    11  	"github.com/dominant-strategies/go-quai/common"
    12  	"github.com/dominant-strategies/go-quai/consensus"
    13  	"github.com/dominant-strategies/go-quai/consensus/misc"
    14  	"github.com/dominant-strategies/go-quai/core"
    15  	"github.com/dominant-strategies/go-quai/core/state"
    16  	"github.com/dominant-strategies/go-quai/core/types"
    17  	"github.com/dominant-strategies/go-quai/log"
    18  	"github.com/dominant-strategies/go-quai/params"
    19  	"github.com/dominant-strategies/go-quai/trie"
    20  	"modernc.org/mathutil"
    21  )
    22  
    23  // Blake3pow proof-of-work protocol constants.
    24  var (
    25  	maxUncles                     = 2         // Maximum number of uncles allowed in a single block
    26  	allowedFutureBlockTimeSeconds = int64(15) // Max seconds from current time allowed for blocks, before they're considered future blocks
    27  
    28  	ContextTimeFactor = big10
    29  	ZoneBlockReward   = big.NewInt(5e+18)
    30  	RegionBlockReward = new(big.Int).Mul(ZoneBlockReward, big3)
    31  	PrimeBlockReward  = new(big.Int).Mul(RegionBlockReward, big3)
    32  )
    33  
    34  // Some useful constants to avoid constant memory allocs for them.
    35  var (
    36  	expDiffPeriod = big.NewInt(100000)
    37  	big0          = big.NewInt(0)
    38  	big1          = big.NewInt(1)
    39  	big2          = big.NewInt(2)
    40  	big3          = big.NewInt(3)
    41  	big8          = big.NewInt(8)
    42  	big9          = big.NewInt(9)
    43  	big10         = big.NewInt(10)
    44  	big32         = big.NewInt(32)
    45  	bigMinus99    = big.NewInt(-99)
    46  	big2e256      = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)) // 2^256
    47  )
    48  
    49  // Various error messages to mark blocks invalid. These should be private to
    50  // prevent engine specific errors from being referenced in the remainder of the
    51  // codebase, inherently breaking if the engine is swapped out. Please put common
    52  // error types into the consensus package.
    53  var (
    54  	errOlderBlockTime      = errors.New("timestamp older than parent")
    55  	errTooManyUncles       = errors.New("too many uncles")
    56  	errDuplicateUncle      = errors.New("duplicate uncle")
    57  	errUncleIsAncestor     = errors.New("uncle is ancestor")
    58  	errDanglingUncle       = errors.New("uncle's parent is not ancestor")
    59  	errInvalidDifficulty   = errors.New("non-positive difficulty")
    60  	errDifficultyCrossover = errors.New("sub's difficulty exceeds dom's")
    61  	errInvalidPoW          = errors.New("invalid proof-of-work")
    62  	errInvalidOrder        = errors.New("invalid order")
    63  )
    64  
    65  // Author implements consensus.Engine, returning the header's coinbase as the
    66  // proof-of-work verified author of the block.
    67  func (blake3pow *Blake3pow) Author(header *types.Header) (common.Address, error) {
    68  	return header.Coinbase(), nil
    69  }
    70  
    71  // VerifyHeader checks whether a header conforms to the consensus rules of the
    72  // stock Quai blake3pow engine.
    73  func (blake3pow *Blake3pow) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
    74  	// If we're running a full engine faking, accept any input as valid
    75  	if blake3pow.config.PowMode == ModeFullFake {
    76  		return nil
    77  	}
    78  	// Short circuit if the header is known, or its parent not
    79  	number := header.NumberU64()
    80  	if chain.GetHeader(header.Hash(), number) != nil {
    81  		return nil
    82  	}
    83  	parent := chain.GetHeader(header.ParentHash(), number-1)
    84  	if parent == nil {
    85  		return consensus.ErrUnknownAncestor
    86  	}
    87  	// Sanity checks passed, do a proper verification
    88  	return blake3pow.verifyHeader(chain, header, parent, false, time.Now().Unix())
    89  }
    90  
    91  // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
    92  // concurrently. The method returns a quit channel to abort the operations and
    93  // a results channel to retrieve the async verifications.
    94  func (blake3pow *Blake3pow) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) {
    95  	// If we're running a full engine faking, accept any input as valid
    96  	if blake3pow.config.PowMode == ModeFullFake || len(headers) == 0 {
    97  		abort, results := make(chan struct{}), make(chan error, len(headers))
    98  		for i := 0; i < len(headers); i++ {
    99  			results <- nil
   100  		}
   101  		return abort, results
   102  	}
   103  
   104  	// Spawn as many workers as allowed threads
   105  	workers := runtime.GOMAXPROCS(0)
   106  	if len(headers) < workers {
   107  		workers = len(headers)
   108  	}
   109  
   110  	// Create a task channel and spawn the verifiers
   111  	var (
   112  		inputs  = make(chan int)
   113  		done    = make(chan int, workers)
   114  		errors  = make([]error, len(headers))
   115  		abort   = make(chan struct{})
   116  		unixNow = time.Now().Unix()
   117  	)
   118  	for i := 0; i < workers; i++ {
   119  		go func() {
   120  			for index := range inputs {
   121  				errors[index] = blake3pow.verifyHeaderWorker(chain, headers, index, unixNow)
   122  				done <- index
   123  			}
   124  		}()
   125  	}
   126  
   127  	errorsOut := make(chan error, len(headers))
   128  	go func() {
   129  		defer close(inputs)
   130  		var (
   131  			in, out = 0, 0
   132  			checked = make([]bool, len(headers))
   133  			inputs  = inputs
   134  		)
   135  		for {
   136  			select {
   137  			case inputs <- in:
   138  				if in++; in == len(headers) {
   139  					// Reached end of headers. Stop sending to workers.
   140  					inputs = nil
   141  				}
   142  			case index := <-done:
   143  				for checked[index] = true; checked[out]; out++ {
   144  					errorsOut <- errors[out]
   145  					if out == len(headers)-1 {
   146  						return
   147  					}
   148  				}
   149  			case <-abort:
   150  				return
   151  			}
   152  		}
   153  	}()
   154  	return abort, errorsOut
   155  }
   156  
   157  func (blake3pow *Blake3pow) verifyHeaderWorker(chain consensus.ChainHeaderReader, headers []*types.Header, index int, unixNow int64) error {
   158  	var parent *types.Header
   159  	if index == 0 {
   160  		parent = chain.GetHeader(headers[0].ParentHash(), headers[0].NumberU64()-1)
   161  	} else if headers[index-1].Hash() == headers[index].ParentHash() {
   162  		parent = headers[index-1]
   163  	}
   164  	if parent == nil {
   165  		return consensus.ErrUnknownAncestor
   166  	}
   167  	return blake3pow.verifyHeader(chain, headers[index], parent, false, unixNow)
   168  }
   169  
   170  // VerifyUncles verifies that the given block's uncles conform to the consensus
   171  // rules of the stock Quai blake3pow engine.
   172  func (blake3pow *Blake3pow) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
   173  	// If we're running a full engine faking, accept any input as valid
   174  	if blake3pow.config.PowMode == ModeFullFake {
   175  		return nil
   176  	}
   177  	// Verify that there are at most 2 uncles included in this block
   178  	if len(block.Uncles()) > maxUncles {
   179  		return errTooManyUncles
   180  	}
   181  	if len(block.Uncles()) == 0 {
   182  		return nil
   183  	}
   184  	// Gather the set of past uncles and ancestors
   185  	uncles, ancestors := mapset.NewSet(), make(map[common.Hash]*types.Header)
   186  
   187  	number, parent := block.NumberU64()-1, block.ParentHash()
   188  	for i := 0; i < 7; i++ {
   189  		ancestorHeader := chain.GetHeader(parent, number)
   190  		if ancestorHeader == nil {
   191  			break
   192  		}
   193  		ancestors[parent] = ancestorHeader
   194  		// If the ancestor doesn't have any uncles, we don't have to iterate them
   195  		if ancestorHeader.UncleHash() != types.EmptyUncleHash {
   196  			// Need to add those uncles to the banned list too
   197  			ancestor := chain.GetBlock(parent, number)
   198  			if ancestor == nil {
   199  				break
   200  			}
   201  			for _, uncle := range ancestor.Uncles() {
   202  				uncles.Add(uncle.Hash())
   203  			}
   204  		}
   205  		parent, number = ancestorHeader.ParentHash(), number-1
   206  	}
   207  	ancestors[block.Hash()] = block.Header()
   208  	uncles.Add(block.Hash())
   209  
   210  	// Verify each of the uncles that it's recent, but not an ancestor
   211  	for _, uncle := range block.Uncles() {
   212  		// Make sure every uncle is rewarded only once
   213  		hash := uncle.Hash()
   214  		if uncles.Contains(hash) {
   215  			return errDuplicateUncle
   216  		}
   217  		uncles.Add(hash)
   218  
   219  		// Make sure the uncle has a valid ancestry
   220  		if ancestors[hash] != nil {
   221  			return errUncleIsAncestor
   222  		}
   223  		if ancestors[uncle.ParentHash()] == nil || uncle.ParentHash() == block.ParentHash() {
   224  			return errDanglingUncle
   225  		}
   226  		if err := blake3pow.verifyHeader(chain, uncle, ancestors[uncle.ParentHash()], true, time.Now().Unix()); err != nil {
   227  			return err
   228  		}
   229  	}
   230  	return nil
   231  }
   232  
   233  // verifyHeader checks whether a header conforms to the consensus rules
   234  func (blake3pow *Blake3pow) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, uncle bool, unixNow int64) error {
   235  	nodeCtx := common.NodeLocation.Context()
   236  	// Ensure that the header's extra-data section is of a reasonable size
   237  	if uint64(len(header.Extra())) > params.MaximumExtraDataSize {
   238  		return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra()), params.MaximumExtraDataSize)
   239  	}
   240  	// Verify the header's timestamp
   241  	if !uncle {
   242  		if header.Time() > uint64(unixNow+allowedFutureBlockTimeSeconds) {
   243  			return consensus.ErrFutureBlock
   244  		}
   245  	}
   246  	if header.Time() < parent.Time() {
   247  		return errOlderBlockTime
   248  	}
   249  	// Verify the block's difficulty based on its timestamp and parent's difficulty
   250  	// difficulty adjustment can only be checked in zone
   251  	if nodeCtx == common.ZONE_CTX {
   252  		expected := blake3pow.CalcDifficulty(chain, parent)
   253  		if expected.Cmp(header.Difficulty()) != 0 {
   254  			return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty(), expected)
   255  		}
   256  	}
   257  	// Verify the engine specific seal securing the block
   258  	_, order, err := blake3pow.CalcOrder(header)
   259  	if err != nil {
   260  		return err
   261  	}
   262  	if order > nodeCtx {
   263  		return fmt.Errorf("order of the block is greater than the context")
   264  	}
   265  
   266  	if !common.NodeLocation.InSameSliceAs(header.Location()) {
   267  		return fmt.Errorf("block location is not in the same slice as the node location")
   268  	}
   269  
   270  	// Verify that the parent entropy is calculated correctly on the header
   271  	parentEntropy := blake3pow.TotalLogS(parent)
   272  	if parentEntropy.Cmp(header.ParentEntropy()) != 0 {
   273  		return fmt.Errorf("invalid parent entropy: have %v, want %v", header.ParentEntropy(), parentEntropy)
   274  	}
   275  
   276  	// If not prime, verify the parentDeltaS field as well
   277  	if nodeCtx > common.PRIME_CTX {
   278  		_, parentOrder, _ := blake3pow.CalcOrder(parent)
   279  		// If parent was dom, deltaS is zero and otherwise should be the calc delta s on the parent
   280  		if parentOrder < nodeCtx {
   281  			if common.Big0.Cmp(header.ParentDeltaS()) != 0 {
   282  				return fmt.Errorf("invalid parent delta s: have %v, want %v", header.ParentDeltaS(), common.Big0)
   283  			}
   284  		} else {
   285  			parentDeltaS := blake3pow.DeltaLogS(parent)
   286  			if parentDeltaS.Cmp(header.ParentDeltaS()) != 0 {
   287  				return fmt.Errorf("invalid parent delta s: have %v, want %v", header.ParentDeltaS(), parentDeltaS)
   288  			}
   289  		}
   290  	}
   291  
   292  	if nodeCtx == common.ZONE_CTX {
   293  		// check if the header coinbase is in scope
   294  		_, err := header.Coinbase().InternalAddress()
   295  		if err != nil {
   296  			return fmt.Errorf("out-of-scope coinbase in the header")
   297  		}
   298  		// Verify that the gas limit is <= 2^63-1
   299  		cap := uint64(0x7fffffffffffffff)
   300  		if header.GasLimit() > cap {
   301  			return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit(), cap)
   302  		}
   303  		// Verify that the gasUsed is <= gasLimit
   304  		if header.GasUsed() > header.GasLimit() {
   305  			return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed(), header.GasLimit())
   306  		}
   307  		// Verify the block's gas usage and verify the base fee.
   308  		// Verify that the gas limit remains within allowed bounds
   309  		expectedGasLimit := core.CalcGasLimit(parent, blake3pow.config.GasCeil)
   310  		if expectedGasLimit != header.GasLimit() {
   311  			return fmt.Errorf("invalid gasLimit: have %d, want %d",
   312  				header.GasLimit(), expectedGasLimit)
   313  		}
   314  		// Verify the header is not malformed
   315  		if header.BaseFee() == nil {
   316  			return fmt.Errorf("header is missing baseFee")
   317  		}
   318  		// Verify the baseFee is correct based on the parent header.
   319  		expectedBaseFee := misc.CalcBaseFee(chain.Config(), parent)
   320  		if header.BaseFee().Cmp(expectedBaseFee) != 0 {
   321  			return fmt.Errorf("invalid baseFee: have %s, want %s, parentBaseFee %s, parentGasUsed %d",
   322  				expectedBaseFee, header.BaseFee(), parent.BaseFee(), parent.GasUsed())
   323  		}
   324  	}
   325  	// Verify that the block number is parent's +1
   326  	if diff := new(big.Int).Sub(header.Number(), parent.Number()); diff.Cmp(big.NewInt(1)) != 0 {
   327  		return consensus.ErrInvalidNumber
   328  	}
   329  	return nil
   330  }
   331  
   332  // CalcDifficulty is the difficulty adjustment algorithm. It returns
   333  // the difficulty that a new block should have when created at time
   334  // given the parent block's time and difficulty.
   335  func (blake3pow *Blake3pow) CalcDifficulty(chain consensus.ChainHeaderReader, parent *types.Header) *big.Int {
   336  	nodeCtx := common.NodeLocation.Context()
   337  
   338  	if nodeCtx != common.ZONE_CTX {
   339  		log.Error("Cannot CalcDifficulty for", "context", nodeCtx)
   340  		return nil
   341  	}
   342  
   343  	///// Algorithm:
   344  	///// e = (DurationLimit - (parent.Time() - parentOfParent.Time())) * parent.Difficulty()
   345  	///// k = Floor(BinaryLog(parent.Difficulty()))/(DurationLimit*DifficultyAdjustmentFactor*AdjustmentPeriod)
   346  	///// Difficulty = Max(parent.Difficulty() + e * k, MinimumDifficulty)
   347  
   348  	if parent.Hash() == chain.Config().GenesisHash {
   349  		return parent.Difficulty()
   350  	}
   351  
   352  	parentOfParent := chain.GetHeaderByHash(parent.ParentHash())
   353  	if parentOfParent == nil || parentOfParent.Hash() == chain.Config().GenesisHash {
   354  		return parent.Difficulty()
   355  	}
   356  
   357  	time := parent.Time()
   358  	bigTime := new(big.Int).SetUint64(time)
   359  	bigParentTime := new(big.Int).SetUint64(parentOfParent.Time())
   360  
   361  	// holds intermediate values to make the algo easier to read & audit
   362  	x := new(big.Int)
   363  	x.Sub(bigTime, bigParentTime)
   364  	x.Sub(blake3pow.config.DurationLimit, x)
   365  	x.Mul(x, parent.Difficulty())
   366  	k, _ := mathutil.BinaryLog(new(big.Int).Set(parent.Difficulty()), 64)
   367  	x.Mul(x, big.NewInt(int64(k)))
   368  	x.Div(x, blake3pow.config.DurationLimit)
   369  	x.Div(x, big.NewInt(params.DifficultyAdjustmentFactor))
   370  	x.Div(x, params.DifficultyAdjustmentPeriod)
   371  	x.Add(x, parent.Difficulty())
   372  
   373  	// minimum difficulty can ever be (before exponential factor)
   374  	if x.Cmp(blake3pow.config.MinDifficulty) < 0 {
   375  		x.Set(blake3pow.config.MinDifficulty)
   376  	}
   377  	return x
   378  }
   379  
   380  func (blake3pow *Blake3pow) IsDomCoincident(chain consensus.ChainHeaderReader, header *types.Header) bool {
   381  	_, order, err := blake3pow.CalcOrder(header)
   382  	if err != nil {
   383  		return false
   384  	}
   385  	return order < common.NodeLocation.Context()
   386  }
   387  
   388  // VerifySeal returns the PowHash and the verifySeal output
   389  func (blake3pow *Blake3pow) VerifySeal(header *types.Header) (common.Hash, error) {
   390  	return header.Hash(), blake3pow.verifySeal(header)
   391  }
   392  
   393  // verifySeal checks whether a block satisfies the PoW difficulty requirements,
   394  // either using the usual blake3pow cache for it, or alternatively using a full DAG
   395  // to make remote mining fast.
   396  func (blake3pow *Blake3pow) verifySeal(header *types.Header) error {
   397  	// If we're running a fake PoW, accept any seal as valid
   398  	if blake3pow.config.PowMode == ModeFake || blake3pow.config.PowMode == ModeFullFake {
   399  		time.Sleep(blake3pow.fakeDelay)
   400  		if blake3pow.fakeFail == header.Number().Uint64() {
   401  			return errInvalidPoW
   402  		}
   403  		return nil
   404  	}
   405  	// Ensure that we have a valid difficulty for the block
   406  	if header.Difficulty().Sign() <= 0 {
   407  		return errInvalidDifficulty
   408  	}
   409  
   410  	target := new(big.Int).Div(big2e256, header.Difficulty())
   411  	if new(big.Int).SetBytes(header.Hash().Bytes()).Cmp(target) > 0 {
   412  		return errInvalidPoW
   413  	}
   414  	return nil
   415  }
   416  
   417  // Prepare implements consensus.Engine, initializing the difficulty field of a
   418  // header to conform to the blake3pow protocol. The changes are done inline.
   419  func (blake3pow *Blake3pow) Prepare(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header) error {
   420  	header.SetDifficulty(blake3pow.CalcDifficulty(chain, parent))
   421  	return nil
   422  }
   423  
   424  // Finalize implements consensus.Engine, accumulating the block and uncle rewards,
   425  // setting the final state on the header
   426  func (blake3pow *Blake3pow) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
   427  	// Accumulate any block and uncle rewards and commit the final state root
   428  	accumulateRewards(chain.Config(), state, header, uncles)
   429  
   430  	if common.NodeLocation.Context() == common.ZONE_CTX && header.ParentHash() == chain.Config().GenesisHash {
   431  		alloc := core.ReadGenesisAlloc("genallocs/gen_alloc_" + common.NodeLocation.Name() + ".json")
   432  		log.Info("Allocating genesis accounts", "num", len(alloc))
   433  
   434  		for addressString, account := range alloc {
   435  			addr := common.HexToAddress(addressString)
   436  			internal, err := addr.InternalAddress()
   437  			if err != nil {
   438  				log.Error("Provided address in genesis block is out of scope")
   439  			}
   440  			state.AddBalance(internal, account.Balance)
   441  			state.SetCode(internal, account.Code)
   442  			state.SetNonce(internal, account.Nonce)
   443  			for key, value := range account.Storage {
   444  				state.SetState(internal, key, value)
   445  			}
   446  		}
   447  	}
   448  
   449  	header.SetRoot(state.IntermediateRoot(true))
   450  }
   451  
   452  // FinalizeAndAssemble implements consensus.Engine, accumulating the block and
   453  // uncle rewards, setting the final state and assembling the block.
   454  func (blake3pow *Blake3pow) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, etxs []*types.Transaction, subManifest types.BlockManifest, receipts []*types.Receipt) (*types.Block, error) {
   455  	nodeCtx := common.NodeLocation.Context()
   456  	if nodeCtx == common.ZONE_CTX && chain.ProcessingState() {
   457  		// Finalize block
   458  		blake3pow.Finalize(chain, header, state, txs, uncles)
   459  	}
   460  
   461  	// Header seems complete, assemble into a block and return
   462  	return types.NewBlock(header, txs, uncles, etxs, subManifest, receipts, trie.NewStackTrie(nil)), nil
   463  }
   464  
   465  func (blake3pow *Blake3pow) ComputePowLight(header *types.Header) (common.Hash, common.Hash) {
   466  	panic("compute pow light doesnt exist for blake3")
   467  }
   468  
   469  // AccumulateRewards credits the coinbase of the given block with the mining
   470  // reward. The total reward consists of the static block reward and rewards for
   471  // included uncles. The coinbase of each uncle block is also rewarded.
   472  func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) {
   473  	// Select the correct block reward based on chain progression
   474  	blockReward := misc.CalculateReward(header)
   475  
   476  	coinbase, err := header.Coinbase().InternalAddress()
   477  	if err != nil {
   478  		log.Error("Block has out of scope coinbase, skipping block reward", "Address", header.Coinbase().String(), "Hash", header.Hash().String())
   479  		return
   480  	}
   481  
   482  	// Accumulate the rewards for the miner and any included uncles
   483  	reward := new(big.Int).Set(blockReward)
   484  	r := new(big.Int)
   485  	for _, uncle := range uncles {
   486  		coinbase, err := uncle.Coinbase().InternalAddress()
   487  		if err != nil {
   488  			log.Error("Found uncle with out of scope coinbase, skipping reward", "Address", uncle.Coinbase().String(), "Hash", uncle.Hash().String())
   489  			continue
   490  		}
   491  		r.Add(uncle.Number(), big8)
   492  		r.Sub(r, header.Number())
   493  		r.Mul(r, blockReward)
   494  		r.Div(r, big8)
   495  		state.AddBalance(coinbase, r)
   496  
   497  		r.Div(blockReward, big32)
   498  		reward.Add(reward, r)
   499  	}
   500  	state.AddBalance(coinbase, reward)
   501  }