2023-05-09 19:45:33 +02:00
|
|
|
package merge
|
2021-11-01 03:40:36 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"math/big"
|
|
|
|
|
2023-01-17 11:22:08 +01:00
|
|
|
"github.com/holiman/uint256"
|
2023-05-09 19:45:33 +02:00
|
|
|
|
2023-01-13 18:12:18 +00:00
|
|
|
"github.com/ledgerwatch/erigon-lib/chain"
|
|
|
|
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
2021-11-01 03:40:36 +01:00
|
|
|
"github.com/ledgerwatch/erigon/consensus"
|
2022-12-15 16:00:00 +01:00
|
|
|
"github.com/ledgerwatch/erigon/consensus/aura"
|
2021-11-01 03:40:36 +01:00
|
|
|
"github.com/ledgerwatch/erigon/consensus/misc"
|
|
|
|
"github.com/ledgerwatch/erigon/core/state"
|
|
|
|
"github.com/ledgerwatch/erigon/core/types"
|
|
|
|
"github.com/ledgerwatch/erigon/params"
|
|
|
|
"github.com/ledgerwatch/erigon/rpc"
|
2023-07-12 23:31:38 +01:00
|
|
|
"github.com/ledgerwatch/log/v3"
|
2021-11-01 03:40:36 +01:00
|
|
|
)
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
// Constants for The Merge as specified by EIP-3675: Upgrade consensus to Proof-of-Stake
|
2021-11-01 03:40:36 +01:00
|
|
|
var (
|
2023-05-09 19:45:33 +02:00
|
|
|
ProofOfStakeDifficulty = libcommon.Big0 // PoS block's difficulty is always 0
|
|
|
|
ProofOfStakeNonce = types.BlockNonce{} // PoS block's have all-zero nonces
|
2021-11-01 03:40:36 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// errInvalidDifficulty is returned if the difficulty is non-zero.
|
|
|
|
errInvalidDifficulty = errors.New("invalid difficulty")
|
|
|
|
|
|
|
|
// errInvalidNonce is returned if the nonce is non-zero.
|
|
|
|
errInvalidNonce = errors.New("invalid nonce")
|
|
|
|
|
|
|
|
// errInvalidUncleHash is returned if a block contains an non-empty uncle list.
|
|
|
|
errInvalidUncleHash = errors.New("non empty uncle hash")
|
2021-12-13 17:29:38 +00:00
|
|
|
|
|
|
|
errOlderBlockTime = errors.New("timestamp older than parent")
|
2021-11-01 03:40:36 +01:00
|
|
|
)
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
// Merge Consensus Engine for the Execution Layer.
|
|
|
|
// Merge is a consensus engine that combines the eth1 consensus and proof-of-stake
|
2021-12-13 17:29:38 +00:00
|
|
|
// algorithm. The transition rule is described in the eth1/2 merge spec:
|
|
|
|
// https://eips.ethereum.org/EIPS/eip-3675
|
|
|
|
//
|
|
|
|
// Note: After the Merge the work is mostly done on the Consensus Layer, so nothing much is to be added on this side.
|
2023-05-09 19:45:33 +02:00
|
|
|
type Merge struct {
|
2021-12-13 17:29:38 +00:00
|
|
|
eth1Engine consensus.Engine // Original consensus engine used in eth1, e.g. ethash or clique
|
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
// New creates a new instance of the Merge Engine with the given embedded eth1 engine.
|
|
|
|
func New(eth1Engine consensus.Engine) *Merge {
|
|
|
|
if _, ok := eth1Engine.(*Merge); ok {
|
2021-12-13 17:29:38 +00:00
|
|
|
panic("nested consensus engine")
|
|
|
|
}
|
2023-05-09 19:45:33 +02:00
|
|
|
return &Merge{eth1Engine: eth1Engine}
|
2021-11-01 03:40:36 +01:00
|
|
|
}
|
|
|
|
|
2022-06-03 18:14:49 +02:00
|
|
|
// InnerEngine returns the embedded eth1 consensus engine.
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) InnerEngine() consensus.Engine {
|
2022-06-03 18:14:49 +02:00
|
|
|
return s.eth1Engine
|
|
|
|
}
|
|
|
|
|
|
|
|
// Type returns the type of the underlying consensus engine.
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) Type() chain.ConsensusName {
|
2022-02-08 00:30:46 +03:00
|
|
|
return s.eth1Engine.Type()
|
|
|
|
}
|
|
|
|
|
2021-11-01 03:40:36 +01:00
|
|
|
// Author implements consensus.Engine, returning the header's coinbase as the
|
|
|
|
// proof-of-stake verified author of the block.
|
Fix trace error in Polygon | Pass Engin to the Base API (#6131)
So there is an issue with tracing certain blocks/transactions on
Polygon, for example:
```
> '{"method": "trace_transaction","params":["0xb198d93f640343a98f90d93aa2b74b4fc5c64f3a649f1608d2bfd1004f9dee0e"],"id":1,"jsonrpc":"2.0"}'
```
gives the error `first run for txIndex 1 error: insufficient funds for
gas * price + value: address 0x10AD27A96CDBffC90ab3b83bF695911426A69f5E
have 16927727762862809 want 17594166808296934`
The reason is that this transaction is from the author of the block,
which doesn't have enough ETH to pay for the gas fee + tx value if he's
not the block author receiving transactions fees.
The issue is that currently the APIs are using `ethash.NewFaker()`
Engine for running traces, etc. which doesn't know how to get the author
for a specific block (which is consensus dependant); as it was noting in
several TODO comments.
The fix is to pass the Engine to the BaseAPI, which can then be used to
create the right Block Context. I chose to split the current Engine
interface in 2, with Reader and Writer, so that the BaseAPI only
receives the Reader one, which might be safer (even though it's only
used for getting the block Author).
2022-12-04 06:17:39 +01:00
|
|
|
// This is thread-safe (only access the header.Coinbase or the underlying engine's thread-safe method)
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) Author(header *types.Header) (libcommon.Address, error) {
|
2023-06-19 22:06:51 +05:30
|
|
|
if !misc.IsPoSHeader(header) {
|
2021-12-13 17:29:38 +00:00
|
|
|
return s.eth1Engine.Author(header)
|
|
|
|
}
|
2021-11-01 03:40:36 +01:00
|
|
|
return header.Coinbase, nil
|
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
|
2021-12-13 17:29:38 +00:00
|
|
|
reached, err := IsTTDReached(chain, header.ParentHash, header.Number.Uint64()-1)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !reached {
|
2022-10-04 11:14:18 +01:00
|
|
|
// Not verifying seals if the TTD is passed
|
|
|
|
return s.eth1Engine.VerifyHeader(chain, header, !chain.Config().TerminalTotalDifficultyPassed)
|
2021-12-13 17:29:38 +00:00
|
|
|
}
|
|
|
|
// Short circuit if the parent is not known
|
2021-11-01 03:40:36 +01:00
|
|
|
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
|
|
|
|
if parent == nil {
|
|
|
|
return consensus.ErrUnknownAncestor
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.verifyHeader(chain, header, parent)
|
|
|
|
}
|
|
|
|
|
|
|
|
// VerifyUncles implements consensus.Engine, always returning an error for any
|
|
|
|
// uncles as this consensus mechanism doesn't permit uncles.
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) VerifyUncles(chain consensus.ChainReader, header *types.Header, uncles []*types.Header) error {
|
2023-06-19 22:06:51 +05:30
|
|
|
if !misc.IsPoSHeader(header) {
|
2021-12-13 17:29:38 +00:00
|
|
|
return s.eth1Engine.VerifyUncles(chain, header, uncles)
|
|
|
|
}
|
2021-11-01 03:40:36 +01:00
|
|
|
if len(uncles) > 0 {
|
|
|
|
return errors.New("uncles not allowed")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prepare makes sure difficulty and nonce are correct
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) Prepare(chain consensus.ChainHeaderReader, header *types.Header, state *state.IntraBlockState) error {
|
2021-12-13 17:29:38 +00:00
|
|
|
reached, err := IsTTDReached(chain, header.ParentHash, header.Number.Uint64()-1)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !reached {
|
2022-01-14 22:06:35 +03:00
|
|
|
return s.eth1Engine.Prepare(chain, header, state)
|
2021-12-13 17:29:38 +00:00
|
|
|
}
|
2023-05-09 19:45:33 +02:00
|
|
|
header.Difficulty = ProofOfStakeDifficulty
|
|
|
|
header.Nonce = ProofOfStakeNonce
|
2021-11-01 03:40:36 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) CalculateRewards(config *chain.Config, header *types.Header, uncles []*types.Header, syscall consensus.SystemCall,
|
2023-05-09 17:19:23 +02:00
|
|
|
) ([]consensus.Reward, error) {
|
|
|
|
_, isAura := s.eth1Engine.(*aura.AuRa)
|
2023-06-19 22:06:51 +05:30
|
|
|
if !misc.IsPoSHeader(header) || isAura {
|
2023-05-09 17:19:23 +02:00
|
|
|
return s.eth1Engine.CalculateRewards(config, header, uncles, syscall)
|
|
|
|
}
|
|
|
|
return []consensus.Reward{}, nil
|
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) Finalize(config *chain.Config, header *types.Header, state *state.IntraBlockState,
|
2022-12-01 09:15:01 +01:00
|
|
|
txs types.Transactions, uncles []*types.Header, r types.Receipts, withdrawals []*types.Withdrawal,
|
2023-07-12 23:31:38 +01:00
|
|
|
chain consensus.ChainHeaderReader, syscall consensus.SystemCall, logger log.Logger,
|
2022-01-14 22:06:35 +03:00
|
|
|
) (types.Transactions, types.Receipts, error) {
|
2023-06-19 22:06:51 +05:30
|
|
|
if !misc.IsPoSHeader(header) {
|
2023-07-12 23:31:38 +01:00
|
|
|
return s.eth1Engine.Finalize(config, header, state, txs, uncles, r, withdrawals, chain, syscall, logger)
|
2022-12-01 09:15:01 +01:00
|
|
|
}
|
2023-05-09 17:19:23 +02:00
|
|
|
|
|
|
|
rewards, err := s.CalculateRewards(config, header, uncles, syscall)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
for _, r := range rewards {
|
|
|
|
state.AddBalance(r.Beneficiary, &r.Amount)
|
|
|
|
}
|
|
|
|
|
|
|
|
if withdrawals != nil {
|
|
|
|
if auraEngine, ok := s.eth1Engine.(*aura.AuRa); ok {
|
2023-03-22 13:03:11 +01:00
|
|
|
if err := auraEngine.ExecuteSystemWithdrawals(withdrawals, syscall); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2023-05-09 17:19:23 +02:00
|
|
|
} else {
|
|
|
|
for _, w := range withdrawals {
|
|
|
|
amountInWei := new(uint256.Int).Mul(uint256.NewInt(w.Amount), uint256.NewInt(params.GWei))
|
|
|
|
state.AddBalance(w.Address, amountInWei)
|
|
|
|
}
|
2023-02-24 12:43:29 +01:00
|
|
|
}
|
2021-12-13 17:29:38 +00:00
|
|
|
}
|
2023-05-09 17:19:23 +02:00
|
|
|
|
2022-01-14 22:06:35 +03:00
|
|
|
return txs, r, nil
|
2021-12-13 17:29:38 +00:00
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) FinalizeAndAssemble(config *chain.Config, header *types.Header, state *state.IntraBlockState,
|
2022-12-01 09:15:01 +01:00
|
|
|
txs types.Transactions, uncles []*types.Header, receipts types.Receipts, withdrawals []*types.Withdrawal,
|
2023-07-12 23:31:38 +01:00
|
|
|
chain consensus.ChainHeaderReader, syscall consensus.SystemCall, call consensus.Call, logger log.Logger,
|
2022-01-14 22:06:35 +03:00
|
|
|
) (*types.Block, types.Transactions, types.Receipts, error) {
|
2023-06-19 22:06:51 +05:30
|
|
|
if !misc.IsPoSHeader(header) {
|
2023-07-12 23:31:38 +01:00
|
|
|
return s.eth1Engine.FinalizeAndAssemble(config, header, state, txs, uncles, receipts, withdrawals, chain, syscall, call, logger)
|
2022-12-01 09:15:01 +01:00
|
|
|
}
|
2023-07-12 23:31:38 +01:00
|
|
|
outTxs, outReceipts, err := s.Finalize(config, header, state, txs, uncles, receipts, withdrawals, chain, syscall, logger)
|
2022-12-01 09:15:01 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, err
|
2021-12-13 17:29:38 +00:00
|
|
|
}
|
2022-12-01 09:15:01 +01:00
|
|
|
return types.NewBlock(header, outTxs, uncles, outReceipts, withdrawals), outTxs, outReceipts, nil
|
2021-11-01 03:40:36 +01:00
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) SealHash(header *types.Header) (hash libcommon.Hash) {
|
2021-12-13 17:29:38 +00:00
|
|
|
return s.eth1Engine.SealHash(header)
|
2021-11-01 03:40:36 +01:00
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) CalcDifficulty(chain consensus.ChainHeaderReader, time, parentTime uint64, parentDifficulty *big.Int, parentNumber uint64, parentHash, parentUncleHash libcommon.Hash, parentAuRaStep uint64) *big.Int {
|
2021-12-13 17:29:38 +00:00
|
|
|
reached, err := IsTTDReached(chain, parentHash, parentNumber)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if !reached {
|
2022-10-21 12:43:44 +02:00
|
|
|
return s.eth1Engine.CalcDifficulty(chain, time, parentTime, parentDifficulty, parentNumber, parentHash, parentUncleHash, parentAuRaStep)
|
2021-12-13 17:29:38 +00:00
|
|
|
}
|
2023-05-09 19:45:33 +02:00
|
|
|
return ProofOfStakeDifficulty
|
2021-11-01 03:40:36 +01:00
|
|
|
}
|
|
|
|
|
2021-12-13 17:29:38 +00:00
|
|
|
// verifyHeader checks whether a Proof-of-Stake header conforms to the consensus rules of the
|
|
|
|
// stock Ethereum consensus engine with EIP-3675 modifications.
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header) error {
|
2021-11-01 03:40:36 +01:00
|
|
|
|
2021-12-13 17:29:38 +00:00
|
|
|
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
|
|
|
|
return fmt.Errorf("extra-data longer than %d bytes (%d)", params.MaximumExtraDataSize, len(header.Extra))
|
|
|
|
}
|
|
|
|
|
|
|
|
if header.Time <= parent.Time {
|
|
|
|
return errOlderBlockTime
|
2021-11-01 03:40:36 +01:00
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
if header.Difficulty.Cmp(ProofOfStakeDifficulty) != 0 {
|
2021-11-01 03:40:36 +01:00
|
|
|
return errInvalidDifficulty
|
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
if !bytes.Equal(header.Nonce[:], ProofOfStakeNonce[:]) {
|
2021-11-01 03:40:36 +01:00
|
|
|
return errInvalidNonce
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that the gas limit is within cap
|
2021-12-13 17:29:38 +00:00
|
|
|
if header.GasLimit > params.MaxGasLimit {
|
|
|
|
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit)
|
2021-11-01 03:40:36 +01:00
|
|
|
}
|
|
|
|
// Verify that the gasUsed is <= gasLimit
|
|
|
|
if header.GasUsed > header.GasLimit {
|
|
|
|
return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that the block number is parent's +1
|
2023-01-27 11:39:34 +07:00
|
|
|
if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(libcommon.Big1) != 0 {
|
2021-11-01 03:40:36 +01:00
|
|
|
return consensus.ErrInvalidNumber
|
|
|
|
}
|
|
|
|
|
|
|
|
if header.UncleHash != types.EmptyUncleHash {
|
|
|
|
return errInvalidUncleHash
|
|
|
|
}
|
|
|
|
|
2023-06-19 22:06:51 +05:30
|
|
|
if err := misc.VerifyEip1559Header(chain.Config(), parent, header, false); err != nil {
|
2022-12-01 09:15:01 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify existence / non-existence of withdrawalsHash
|
2022-12-07 18:45:44 +01:00
|
|
|
shanghai := chain.Config().IsShanghai(header.Time)
|
2022-12-01 09:15:01 +01:00
|
|
|
if shanghai && header.WithdrawalsHash == nil {
|
|
|
|
return fmt.Errorf("missing withdrawalsHash")
|
|
|
|
}
|
|
|
|
if !shanghai && header.WithdrawalsHash != nil {
|
|
|
|
return consensus.ErrUnexpectedWithdrawals
|
|
|
|
}
|
2023-04-16 14:12:40 +06:00
|
|
|
|
2023-08-01 16:01:26 +05:30
|
|
|
cancun := chain.Config().IsCancun(header.Time)
|
|
|
|
|
|
|
|
if !cancun {
|
2023-07-28 12:12:05 +02:00
|
|
|
if header.BlobGasUsed != nil {
|
|
|
|
return fmt.Errorf("invalid blobGasUsed before fork: have %v, expected 'nil'", header.BlobGasUsed)
|
2023-06-02 22:26:19 +02:00
|
|
|
}
|
2023-07-28 12:12:05 +02:00
|
|
|
if header.ExcessBlobGas != nil {
|
|
|
|
return fmt.Errorf("invalid excessBlobGas before fork: have %v, expected 'nil'", header.ExcessBlobGas)
|
2023-04-16 14:12:40 +06:00
|
|
|
}
|
2023-08-01 16:01:26 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
if cancun {
|
|
|
|
if err := misc.VerifyEip4844Header(chain.Config(), parent, header); err != nil {
|
|
|
|
// Verify the header's EIP-4844 attributes.
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if header.ParentBeaconBlockRoot == nil {
|
|
|
|
return fmt.Errorf("missing parentBeaconBlockRoot")
|
|
|
|
}
|
|
|
|
if header.ParentBeaconBlockRoot != chain.CurrentHeader().ParentBeaconBlockRoot {
|
|
|
|
return fmt.Errorf("parentBeaconBlockRoot mismatch")
|
|
|
|
}
|
2023-04-16 14:12:40 +06:00
|
|
|
}
|
2022-12-01 09:15:01 +01:00
|
|
|
return nil
|
2021-11-01 03:40:36 +01:00
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
|
2023-06-19 22:06:51 +05:30
|
|
|
if !misc.IsPoSHeader(block.Header()) {
|
2021-12-13 17:29:38 +00:00
|
|
|
return s.eth1Engine.Seal(chain, block, results, stop)
|
|
|
|
}
|
2021-11-01 03:40:36 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) GenerateSeal(chain consensus.ChainHeaderReader, currnt, parent *types.Header, call consensus.Call) []byte {
|
2021-11-01 03:40:36 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) IsServiceTransaction(sender libcommon.Address, syscall consensus.SystemCall) bool {
|
2022-10-26 13:03:47 +02:00
|
|
|
return s.eth1Engine.IsServiceTransaction(sender, syscall)
|
|
|
|
}
|
|
|
|
|
2023-06-19 22:06:51 +05:30
|
|
|
func (s *Merge) Initialize(config *chain.Config, chain consensus.ChainHeaderReader, header *types.Header, state *state.IntraBlockState, txs []types.Transaction, uncles []*types.Header, syscall consensus.SysCallCustom) {
|
2023-04-04 10:30:07 +07:00
|
|
|
s.eth1Engine.Initialize(config, chain, header, state, txs, uncles, syscall)
|
2023-08-01 16:01:26 +05:30
|
|
|
if chain.Config().IsCancun(header.Time) {
|
|
|
|
misc.ApplyBeaconRootEip4788(chain, header, state)
|
|
|
|
}
|
2021-11-01 03:40:36 +01:00
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) APIs(chain consensus.ChainHeaderReader) []rpc.API {
|
2021-12-13 17:29:38 +00:00
|
|
|
return s.eth1Engine.APIs(chain)
|
2021-11-01 03:40:36 +01:00
|
|
|
}
|
|
|
|
|
2023-05-09 19:45:33 +02:00
|
|
|
func (s *Merge) Close() error {
|
2021-12-13 17:29:38 +00:00
|
|
|
return s.eth1Engine.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsTTDReached checks if the TotalTerminalDifficulty has been surpassed on the `parentHash` block.
|
|
|
|
// It depends on the parentHash already being stored in the database.
|
|
|
|
// If the total difficulty is not stored in the database a ErrUnknownAncestorTD error is returned.
|
2023-01-13 18:12:18 +00:00
|
|
|
func IsTTDReached(chain consensus.ChainHeaderReader, parentHash libcommon.Hash, number uint64) (bool, error) {
|
2021-12-13 17:29:38 +00:00
|
|
|
if chain.Config().TerminalTotalDifficulty == nil {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
td := chain.GetTd(parentHash, number)
|
|
|
|
if td == nil {
|
|
|
|
return false, consensus.ErrUnknownAncestorTD
|
|
|
|
}
|
|
|
|
return td.Cmp(chain.Config().TerminalTotalDifficulty) >= 0, nil
|
2021-11-01 03:40:36 +01:00
|
|
|
}
|