github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/eth/catalyst/api.go (about)

     1  // Copyright 2020 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 catalyst implements the temporary eth1/eth2 RPC integration.
    18  package catalyst
    19  
    20  import (
    21  	"errors"
    22  	"fmt"
    23  	"math/big"
    24  	"time"
    25  
    26  	"github.com/scroll-tech/go-ethereum/common"
    27  	"github.com/scroll-tech/go-ethereum/consensus/misc"
    28  	"github.com/scroll-tech/go-ethereum/core"
    29  	"github.com/scroll-tech/go-ethereum/core/state"
    30  	"github.com/scroll-tech/go-ethereum/core/types"
    31  	"github.com/scroll-tech/go-ethereum/eth"
    32  	"github.com/scroll-tech/go-ethereum/log"
    33  	"github.com/scroll-tech/go-ethereum/node"
    34  	chainParams "github.com/scroll-tech/go-ethereum/params"
    35  	"github.com/scroll-tech/go-ethereum/rpc"
    36  	"github.com/scroll-tech/go-ethereum/trie"
    37  )
    38  
    39  // Register adds catalyst APIs to the node.
    40  func Register(stack *node.Node, backend *eth.Ethereum) error {
    41  	chainconfig := backend.BlockChain().Config()
    42  	if chainconfig.TerminalTotalDifficulty == nil {
    43  		return errors.New("catalyst started without valid total difficulty")
    44  	}
    45  
    46  	log.Warn("Catalyst mode enabled")
    47  	stack.RegisterAPIs([]rpc.API{
    48  		{
    49  			Namespace: "consensus",
    50  			Version:   "1.0",
    51  			Service:   newConsensusAPI(backend),
    52  			Public:    true,
    53  		},
    54  	})
    55  	return nil
    56  }
    57  
    58  type consensusAPI struct {
    59  	eth *eth.Ethereum
    60  }
    61  
    62  func newConsensusAPI(eth *eth.Ethereum) *consensusAPI {
    63  	return &consensusAPI{eth: eth}
    64  }
    65  
    66  // blockExecutionEnv gathers all the data required to execute
    67  // a block, either when assembling it or when inserting it.
    68  type blockExecutionEnv struct {
    69  	chain   *core.BlockChain
    70  	state   *state.StateDB
    71  	tcount  int
    72  	gasPool *core.GasPool
    73  
    74  	header   *types.Header
    75  	txs      []*types.Transaction
    76  	receipts []*types.Receipt
    77  }
    78  
    79  func (env *blockExecutionEnv) commitTransaction(tx *types.Transaction, coinbase common.Address) error {
    80  	vmconfig := *env.chain.GetVMConfig()
    81  	snap := env.state.Snapshot()
    82  	receipt, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vmconfig)
    83  	if err != nil {
    84  		env.state.RevertToSnapshot(snap)
    85  		return err
    86  	}
    87  	env.txs = append(env.txs, tx)
    88  	env.receipts = append(env.receipts, receipt)
    89  	return nil
    90  }
    91  
    92  func (api *consensusAPI) makeEnv(parent *types.Block, header *types.Header) (*blockExecutionEnv, error) {
    93  	state, err := api.eth.BlockChain().StateAt(parent.Root())
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	env := &blockExecutionEnv{
    98  		chain:   api.eth.BlockChain(),
    99  		state:   state,
   100  		header:  header,
   101  		gasPool: new(core.GasPool).AddGas(header.GasLimit),
   102  	}
   103  	return env, nil
   104  }
   105  
   106  // AssembleBlock creates a new block, inserts it into the chain, and returns the "execution
   107  // data" required for eth2 clients to process the new block.
   108  func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableData, error) {
   109  	log.Info("Producing block", "parentHash", params.ParentHash)
   110  
   111  	bc := api.eth.BlockChain()
   112  	parent := bc.GetBlockByHash(params.ParentHash)
   113  	if parent == nil {
   114  		log.Warn("Cannot assemble block with parent hash to unknown block", "parentHash", params.ParentHash)
   115  		return nil, fmt.Errorf("cannot assemble block with unknown parent %s", params.ParentHash)
   116  	}
   117  
   118  	pool := api.eth.TxPool()
   119  
   120  	if parent.Time() >= params.Timestamp {
   121  		return nil, fmt.Errorf("child timestamp lower than parent's: %d >= %d", parent.Time(), params.Timestamp)
   122  	}
   123  	if now := uint64(time.Now().Unix()); params.Timestamp > now+1 {
   124  		wait := time.Duration(params.Timestamp-now) * time.Second
   125  		log.Info("Producing block too far in the future", "wait", common.PrettyDuration(wait))
   126  		time.Sleep(wait)
   127  	}
   128  
   129  	pending := pool.Pending(true)
   130  
   131  	coinbase, err := api.eth.Etherbase()
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	num := parent.Number()
   136  	header := &types.Header{
   137  		ParentHash: parent.Hash(),
   138  		Number:     num.Add(num, common.Big1),
   139  		Coinbase:   coinbase,
   140  		GasLimit:   parent.GasLimit(), // Keep the gas limit constant in this prototype
   141  		Extra:      []byte{},
   142  		Time:       params.Timestamp,
   143  	}
   144  	if config := api.eth.BlockChain().Config(); config.IsLondon(header.Number) {
   145  		header.BaseFee = misc.CalcBaseFee(config, parent.Header())
   146  	}
   147  	err = api.eth.Engine().Prepare(bc, header)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  
   152  	env, err := api.makeEnv(parent, header)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	var (
   158  		signer       = types.MakeSigner(bc.Config(), header.Number)
   159  		txHeap       = types.NewTransactionsByPriceAndNonce(signer, pending, nil)
   160  		transactions []*types.Transaction
   161  	)
   162  	for {
   163  		if env.gasPool.Gas() < chainParams.TxGas {
   164  			log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", chainParams.TxGas)
   165  			break
   166  		}
   167  		tx := txHeap.Peek()
   168  		if tx == nil {
   169  			break
   170  		}
   171  
   172  		// The sender is only for logging purposes, and it doesn't really matter if it's correct.
   173  		from, _ := types.Sender(signer, tx)
   174  
   175  		// Execute the transaction
   176  		env.state.Prepare(tx.Hash(), env.tcount)
   177  		err = env.commitTransaction(tx, coinbase)
   178  		switch err {
   179  		case core.ErrGasLimitReached:
   180  			// Pop the current out-of-gas transaction without shifting in the next from the account
   181  			log.Trace("Gas limit exceeded for current block", "sender", from)
   182  			txHeap.Pop()
   183  
   184  		case core.ErrNonceTooLow:
   185  			// New head notification data race between the transaction pool and miner, shift
   186  			log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
   187  			txHeap.Shift()
   188  
   189  		case core.ErrNonceTooHigh:
   190  			// Reorg notification data race between the transaction pool and miner, skip account =
   191  			log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce())
   192  			txHeap.Pop()
   193  
   194  		case nil:
   195  			// Everything ok, collect the logs and shift in the next transaction from the same account
   196  			env.tcount++
   197  			txHeap.Shift()
   198  			transactions = append(transactions, tx)
   199  
   200  		default:
   201  			// Strange error, discard the transaction and get the next in line (note, the
   202  			// nonce-too-high clause will prevent us from executing in vain).
   203  			log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
   204  			txHeap.Shift()
   205  		}
   206  	}
   207  
   208  	// Create the block.
   209  	block, err := api.eth.Engine().FinalizeAndAssemble(bc, header, env.state, transactions, nil /* uncles */, env.receipts)
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  	return &executableData{
   214  		BlockHash:    block.Hash(),
   215  		ParentHash:   block.ParentHash(),
   216  		Miner:        block.Coinbase(),
   217  		StateRoot:    block.Root(),
   218  		Number:       block.NumberU64(),
   219  		GasLimit:     block.GasLimit(),
   220  		GasUsed:      block.GasUsed(),
   221  		Timestamp:    block.Time(),
   222  		ReceiptRoot:  block.ReceiptHash(),
   223  		LogsBloom:    block.Bloom().Bytes(),
   224  		Transactions: encodeTransactions(block.Transactions()),
   225  	}, nil
   226  }
   227  
   228  func encodeTransactions(txs []*types.Transaction) [][]byte {
   229  	var enc = make([][]byte, len(txs))
   230  	for i, tx := range txs {
   231  		enc[i], _ = tx.MarshalBinary()
   232  	}
   233  	return enc
   234  }
   235  
   236  func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
   237  	var txs = make([]*types.Transaction, len(enc))
   238  	for i, encTx := range enc {
   239  		var tx types.Transaction
   240  		if err := tx.UnmarshalBinary(encTx); err != nil {
   241  			return nil, fmt.Errorf("invalid transaction %d: %v", i, err)
   242  		}
   243  		txs[i] = &tx
   244  	}
   245  	return txs, nil
   246  }
   247  
   248  func insertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Header, params executableData) (*types.Block, error) {
   249  	txs, err := decodeTransactions(params.Transactions)
   250  	if err != nil {
   251  		return nil, err
   252  	}
   253  
   254  	number := big.NewInt(0)
   255  	number.SetUint64(params.Number)
   256  	header := &types.Header{
   257  		ParentHash:  params.ParentHash,
   258  		UncleHash:   types.EmptyUncleHash,
   259  		Coinbase:    params.Miner,
   260  		Root:        params.StateRoot,
   261  		TxHash:      types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
   262  		ReceiptHash: params.ReceiptRoot,
   263  		Bloom:       types.BytesToBloom(params.LogsBloom),
   264  		Difficulty:  big.NewInt(1),
   265  		Number:      number,
   266  		GasLimit:    params.GasLimit,
   267  		GasUsed:     params.GasUsed,
   268  		Time:        params.Timestamp,
   269  	}
   270  	if config.IsLondon(number) {
   271  		header.BaseFee = misc.CalcBaseFee(config, parent)
   272  	}
   273  	block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */)
   274  	return block, nil
   275  }
   276  
   277  // NewBlock creates an Eth1 block, inserts it in the chain, and either returns true,
   278  // or false + an error. This is a bit redundant for go, but simplifies things on the
   279  // eth2 side.
   280  func (api *consensusAPI) NewBlock(params executableData) (*newBlockResponse, error) {
   281  	parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash)
   282  	if parent == nil {
   283  		return &newBlockResponse{false}, fmt.Errorf("could not find parent %x", params.ParentHash)
   284  	}
   285  	block, err := insertBlockParamsToBlock(api.eth.BlockChain().Config(), parent.Header(), params)
   286  	if err != nil {
   287  		return nil, err
   288  	}
   289  	_, err = api.eth.BlockChain().InsertChainWithoutSealVerification(block)
   290  	return &newBlockResponse{err == nil}, err
   291  }
   292  
   293  // Used in tests to add a the list of transactions from a block to the tx pool.
   294  func (api *consensusAPI) addBlockTxs(block *types.Block) error {
   295  	for _, tx := range block.Transactions() {
   296  		api.eth.TxPool().AddLocal(tx)
   297  	}
   298  	return nil
   299  }
   300  
   301  // FinalizeBlock is called to mark a block as synchronized, so
   302  // that data that is no longer needed can be removed.
   303  func (api *consensusAPI) FinalizeBlock(blockHash common.Hash) (*genericResponse, error) {
   304  	return &genericResponse{true}, nil
   305  }
   306  
   307  // SetHead is called to perform a force choice.
   308  func (api *consensusAPI) SetHead(newHead common.Hash) (*genericResponse, error) {
   309  	return &genericResponse{true}, nil
   310  }