github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/core/block_validator.go (about)

     1  // Copyright 2015 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  	"errors"
    21  	"fmt"
    22  	"sync"
    23  
    24  	"github.com/scroll-tech/go-ethereum/consensus"
    25  	"github.com/scroll-tech/go-ethereum/core/rawdb"
    26  	"github.com/scroll-tech/go-ethereum/core/state"
    27  	"github.com/scroll-tech/go-ethereum/core/types"
    28  	"github.com/scroll-tech/go-ethereum/ethdb"
    29  	"github.com/scroll-tech/go-ethereum/log"
    30  	"github.com/scroll-tech/go-ethereum/params"
    31  	"github.com/scroll-tech/go-ethereum/rollup/circuitcapacitychecker"
    32  	"github.com/scroll-tech/go-ethereum/trie"
    33  )
    34  
    35  // BlockValidator is responsible for validating block headers, uncles and
    36  // processed state.
    37  //
    38  // BlockValidator implements Validator.
    39  type BlockValidator struct {
    40  	config *params.ChainConfig // Chain configuration options
    41  	bc     *BlockChain         // Canonical block chain
    42  	engine consensus.Engine    // Consensus engine used for validating
    43  
    44  	// circuit capacity checker related fields
    45  	checkCircuitCapacity   bool                                           // whether enable circuit capacity check
    46  	db                     ethdb.Database                                 // db to store row consumption
    47  	cMu                    sync.Mutex                                     // mutex for circuit capacity checker
    48  	circuitCapacityChecker *circuitcapacitychecker.CircuitCapacityChecker // circuit capacity checker instance
    49  }
    50  
    51  // NewBlockValidator returns a new block validator which is safe for re-use
    52  func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine, db ethdb.Database, checkCircuitCapacity bool) *BlockValidator {
    53  	validator := &BlockValidator{
    54  		config:                 config,
    55  		engine:                 engine,
    56  		bc:                     blockchain,
    57  		checkCircuitCapacity:   checkCircuitCapacity,
    58  		db:                     db,
    59  		circuitCapacityChecker: circuitcapacitychecker.NewCircuitCapacityChecker(true),
    60  	}
    61  	log.Info("created new BlockValidator", "CircuitCapacityChecker ID", validator.circuitCapacityChecker.ID)
    62  	return validator
    63  }
    64  
    65  // ValidateBody validates the given block's uncles and verifies the block
    66  // header's transaction and uncle roots. The headers are assumed to be already
    67  // validated at this point.
    68  func (v *BlockValidator) ValidateBody(block *types.Block) error {
    69  	// Check whether the block's known, and if not, that it's linkable
    70  	if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
    71  		return ErrKnownBlock
    72  	}
    73  	if !v.config.Scroll.IsValidTxCount(len(block.Transactions())) {
    74  		return consensus.ErrInvalidTxCount
    75  	}
    76  	// Check if block payload size is smaller than the max size
    77  	if !v.config.Scroll.IsValidBlockSize(block.PayloadSize()) {
    78  		return ErrInvalidBlockPayloadSize
    79  	}
    80  	// Header validity is known at this point, check the uncles and transactions
    81  	header := block.Header()
    82  	if err := v.engine.VerifyUncles(v.bc, block); err != nil {
    83  		return err
    84  	}
    85  	if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
    86  		return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash)
    87  	}
    88  	if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
    89  		return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
    90  	}
    91  	if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
    92  		if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
    93  			return consensus.ErrUnknownAncestor
    94  		}
    95  		return consensus.ErrPrunedAncestor
    96  	}
    97  	if err := v.ValidateL1Messages(block); err != nil {
    98  		return err
    99  	}
   100  	if v.checkCircuitCapacity {
   101  		// if a block's RowConsumption has been stored, which means it has been processed before,
   102  		// (e.g., in miner/worker.go or in insertChain),
   103  		// we simply skip its calculation and validation
   104  		if rawdb.ReadBlockRowConsumption(v.db, block.Hash()) != nil {
   105  			return nil
   106  		}
   107  		rowConsumption, err := v.validateCircuitRowConsumption(block)
   108  		if err != nil {
   109  			return err
   110  		}
   111  		log.Trace(
   112  			"Validator write block row consumption",
   113  			"id", v.circuitCapacityChecker.ID,
   114  			"number", block.NumberU64(),
   115  			"hash", block.Hash().String(),
   116  			"rowConsumption", rowConsumption,
   117  		)
   118  		rawdb.WriteBlockRowConsumption(v.db, block.Hash(), rowConsumption)
   119  	}
   120  	return nil
   121  }
   122  
   123  // ValidateL1Messages validates L1 messages contained in a block.
   124  // We check the following conditions:
   125  // - L1 messages are in a contiguous section at the front of the block.
   126  // - The first L1 message's QueueIndex is right after the last L1 message included in the chain.
   127  // - L1 messages follow the QueueIndex order.
   128  // - The L1 messages included in the block match the node's view of the L1 ledger.
   129  func (v *BlockValidator) ValidateL1Messages(block *types.Block) error {
   130  	// skip DB read if the block contains no L1 messages
   131  	if !block.ContainsL1Messages() {
   132  		return nil
   133  	}
   134  
   135  	blockHash := block.Hash()
   136  
   137  	if v.config.Scroll.L1Config == nil {
   138  		// TODO: should we allow follower nodes to skip L1 message verification?
   139  		panic("Running on L1Message-enabled network but no l1Config was provided")
   140  	}
   141  
   142  	nextQueueIndex := rawdb.ReadFirstQueueIndexNotInL2Block(v.bc.db, block.ParentHash())
   143  	if nextQueueIndex == nil {
   144  		// we'll reprocess this block at a later time
   145  		return consensus.ErrMissingL1MessageData
   146  	}
   147  	queueIndex := *nextQueueIndex
   148  
   149  	L1SectionOver := false
   150  	it := rawdb.IterateL1MessagesFrom(v.bc.db, queueIndex)
   151  
   152  	for _, tx := range block.Transactions() {
   153  		if !tx.IsL1MessageTx() {
   154  			L1SectionOver = true
   155  			continue // we do not verify L2 transactions here
   156  		}
   157  
   158  		// check that L1 messages are before L2 transactions
   159  		if L1SectionOver {
   160  			return consensus.ErrInvalidL1MessageOrder
   161  		}
   162  
   163  		// queue index cannot decrease
   164  		txQueueIndex := tx.AsL1MessageTx().QueueIndex
   165  
   166  		if txQueueIndex < queueIndex {
   167  			return consensus.ErrInvalidL1MessageOrder
   168  		}
   169  
   170  		// skipped messages
   171  		// TODO: consider verifying that skipped messages overflow
   172  		for index := queueIndex; index < txQueueIndex; index++ {
   173  			if exists := it.Next(); !exists {
   174  				// the message in this block is not available in our local db.
   175  				// we'll reprocess this block at a later time.
   176  				return consensus.ErrMissingL1MessageData
   177  			}
   178  
   179  			l1msg := it.L1Message()
   180  			skippedTx := types.NewTx(&l1msg)
   181  			log.Debug("Skipped L1 message", "queueIndex", index, "tx", skippedTx.Hash().String(), "block", blockHash.String())
   182  			rawdb.WriteSkippedTransaction(v.db, skippedTx, nil, "unknown", block.NumberU64(), &blockHash)
   183  		}
   184  
   185  		queueIndex = txQueueIndex + 1
   186  
   187  		if exists := it.Next(); !exists {
   188  			// the message in this block is not available in our local db.
   189  			// we'll reprocess this block at a later time.
   190  			return consensus.ErrMissingL1MessageData
   191  		}
   192  
   193  		// check that the L1 message in the block is the same that we collected from L1
   194  		msg := it.L1Message()
   195  		expectedHash := types.NewTx(&msg).Hash()
   196  
   197  		if tx.Hash() != expectedHash {
   198  			return consensus.ErrUnknownL1Message
   199  		}
   200  	}
   201  
   202  	// TODO: consider adding a rule to enforce L1Config.NumL1MessagesPerBlock.
   203  	// If there are L1 messages available, sequencer nodes should include them.
   204  	// However, this is hard to enforce as different nodes might have different views of L1.
   205  
   206  	return nil
   207  }
   208  
   209  // ValidateState validates the various changes that happen after a state
   210  // transition, such as amount of used gas, the receipt roots and the state root
   211  // itself. ValidateState returns a database batch if the validation was a success
   212  // otherwise nil and an error is returned.
   213  func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error {
   214  	header := block.Header()
   215  	if block.GasUsed() != usedGas {
   216  		return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
   217  	}
   218  	// Validate the received block's bloom with the one derived from the generated receipts.
   219  	// For valid blocks this should always validate to true.
   220  	rbloom := types.CreateBloom(receipts)
   221  	if rbloom != header.Bloom {
   222  		return fmt.Errorf("invalid bloom (remote: %x  local: %x)", header.Bloom, rbloom)
   223  	}
   224  	// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
   225  	receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
   226  	if receiptSha != header.ReceiptHash {
   227  		return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
   228  	}
   229  	// Validate the state root against the received state root and throw
   230  	// an error if they don't match.
   231  	if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
   232  		return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root)
   233  	}
   234  	return nil
   235  }
   236  
   237  // CalcGasLimit computes the gas limit of the next block after parent. It aims
   238  // to keep the baseline gas close to the provided target, and increase it towards
   239  // the target if the baseline gas is lower.
   240  func CalcGasLimit(parentGasLimit, desiredLimit uint64) uint64 {
   241  	delta := parentGasLimit/params.GasLimitBoundDivisor - 1
   242  	limit := parentGasLimit
   243  	if desiredLimit < params.MinGasLimit {
   244  		desiredLimit = params.MinGasLimit
   245  	}
   246  	// If we're outside our allowed gas range, we try to hone towards them
   247  	if limit < desiredLimit {
   248  		limit = parentGasLimit + delta
   249  		if limit > desiredLimit {
   250  			limit = desiredLimit
   251  		}
   252  		return limit
   253  	}
   254  	if limit > desiredLimit {
   255  		limit = parentGasLimit - delta
   256  		if limit < desiredLimit {
   257  			limit = desiredLimit
   258  		}
   259  	}
   260  	return limit
   261  }
   262  
   263  func (v *BlockValidator) createTraceEnv(block *types.Block) (*TraceEnv, error) {
   264  	parent := v.bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
   265  	if parent == nil {
   266  		return nil, errors.New("validateCircuitRowConsumption: no parent block found")
   267  	}
   268  
   269  	statedb, err := v.bc.StateAt(parent.Root())
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  
   274  	return CreateTraceEnv(v.config, v.bc, v.engine, v.db, statedb, parent, block, true)
   275  }
   276  
   277  func (v *BlockValidator) validateCircuitRowConsumption(block *types.Block) (*types.RowConsumption, error) {
   278  	log.Trace(
   279  		"Validator apply ccc for block",
   280  		"id", v.circuitCapacityChecker.ID,
   281  		"number", block.NumberU64(),
   282  		"hash", block.Hash().String(),
   283  		"len(txs)", block.Transactions().Len(),
   284  	)
   285  
   286  	env, err := v.createTraceEnv(block)
   287  	if err != nil {
   288  		return nil, err
   289  	}
   290  
   291  	traces, err := env.GetBlockTrace(block)
   292  	if err != nil {
   293  		return nil, err
   294  	}
   295  
   296  	v.cMu.Lock()
   297  	defer v.cMu.Unlock()
   298  
   299  	v.circuitCapacityChecker.Reset()
   300  	log.Trace("Validator reset ccc", "id", v.circuitCapacityChecker.ID)
   301  	rc, err := v.circuitCapacityChecker.ApplyBlock(traces)
   302  
   303  	log.Trace(
   304  		"Validator apply ccc for block result",
   305  		"id", v.circuitCapacityChecker.ID,
   306  		"number", block.NumberU64(),
   307  		"hash", block.Hash().String(),
   308  		"len(txs)", block.Transactions().Len(),
   309  		"rc", rc,
   310  		"err", err,
   311  	)
   312  
   313  	return rc, err
   314  }