github.com/ethereum/go-ethereum@v1.16.1/eth/catalyst/api_test.go (about)

     1  // Copyright 2021 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
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	crand "crypto/rand"
    23  	"errors"
    24  	"fmt"
    25  	"math/big"
    26  	"math/rand"
    27  	"reflect"
    28  	"sync"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/ethereum/go-ethereum/beacon/engine"
    33  	"github.com/ethereum/go-ethereum/common"
    34  	"github.com/ethereum/go-ethereum/common/hexutil"
    35  	"github.com/ethereum/go-ethereum/consensus/beacon"
    36  	"github.com/ethereum/go-ethereum/consensus/ethash"
    37  	"github.com/ethereum/go-ethereum/core"
    38  	"github.com/ethereum/go-ethereum/core/types"
    39  	"github.com/ethereum/go-ethereum/crypto"
    40  	"github.com/ethereum/go-ethereum/crypto/kzg4844"
    41  	"github.com/ethereum/go-ethereum/eth"
    42  	"github.com/ethereum/go-ethereum/eth/ethconfig"
    43  	"github.com/ethereum/go-ethereum/internal/version"
    44  	"github.com/ethereum/go-ethereum/miner"
    45  	"github.com/ethereum/go-ethereum/node"
    46  	"github.com/ethereum/go-ethereum/p2p"
    47  	"github.com/ethereum/go-ethereum/params"
    48  	"github.com/ethereum/go-ethereum/rpc"
    49  	"github.com/ethereum/go-ethereum/trie"
    50  )
    51  
    52  var (
    53  	// testKey is a private key to use for funding a tester account.
    54  	testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    55  
    56  	// testAddr is the Ethereum address of the tester account.
    57  	testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
    58  
    59  	testBalance = big.NewInt(2e18)
    60  )
    61  
    62  func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
    63  	config := *params.AllEthashProtocolChanges
    64  	engine := beacon.New(ethash.NewFaker())
    65  	if merged {
    66  		config.TerminalTotalDifficulty = common.Big0
    67  		config.MergeNetsplitBlock = common.Big0
    68  	} else {
    69  		// When !merged, the tests expect the next block after the generated chain to be in PoS.
    70  		config.MergeNetsplitBlock = big.NewInt(int64(n + 1))
    71  	}
    72  
    73  	genesis := &core.Genesis{
    74  		Config: &config,
    75  		Alloc: types.GenesisAlloc{
    76  			testAddr:                  {Balance: testBalance},
    77  			params.BeaconRootsAddress: {Balance: common.Big0, Code: common.Hex2Bytes("3373fffffffffffffffffffffffffffffffffffffffe14604457602036146024575f5ffd5b620180005f350680545f35146037575f5ffd5b6201800001545f5260205ff35b6201800042064281555f359062018000015500")},
    78  			config.DepositContractAddress: {
    79  				// Simple deposit generator, source: https://gist.github.com/lightclient/54abb2af2465d6969fa6d1920b9ad9d7
    80  				Code:    common.Hex2Bytes("6080604052366103aa575f603067ffffffffffffffff811115610025576100246103ae565b5b6040519080825280601f01601f1916602001820160405280156100575781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061007d5761007c6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f602067ffffffffffffffff8111156100c7576100c66103ae565b5b6040519080825280601f01601f1916602001820160405280156100f95781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061011f5761011e6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff811115610169576101686103ae565b5b6040519080825280601f01601f19166020018201604052801561019b5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f815181106101c1576101c06103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f606067ffffffffffffffff81111561020b5761020a6103ae565b5b6040519080825280601f01601f19166020018201604052801561023d5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610263576102626103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff8111156102ad576102ac6103ae565b5b6040519080825280601f01601f1916602001820160405280156102df5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610305576103046103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f8081819054906101000a900460ff168092919061035090610441565b91906101000a81548160ff021916908360ff160217905550507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c585858585856040516103a09594939291906104d9565b60405180910390a1005b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60ff82169050919050565b5f61044b82610435565b915060ff820361045e5761045d610408565b5b600182019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6104ab82610469565b6104b58185610473565b93506104c5818560208601610483565b6104ce81610491565b840191505092915050565b5f60a0820190508181035f8301526104f181886104a1565b9050818103602083015261050581876104a1565b9050818103604083015261051981866104a1565b9050818103606083015261052d81856104a1565b9050818103608083015261054181846104a1565b9050969550505050505056fea26469706673582212208569967e58690162d7d6fe3513d07b393b4c15e70f41505cbbfd08f53eba739364736f6c63430008190033"),
    81  				Nonce:   0,
    82  				Balance: big.NewInt(0),
    83  			},
    84  		},
    85  		ExtraData:  []byte("test genesis"),
    86  		Timestamp:  9000,
    87  		BaseFee:    big.NewInt(params.InitialBaseFee),
    88  		Difficulty: big.NewInt(0),
    89  	}
    90  	testNonce := uint64(0)
    91  	generate := func(i int, g *core.BlockGen) {
    92  		g.OffsetTime(5)
    93  		g.SetExtra([]byte("test"))
    94  		tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(&config), testKey)
    95  		g.AddTx(tx)
    96  		testNonce++
    97  	}
    98  	_, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, n, generate)
    99  
   100  	if !merged {
   101  		totalDifficulty := big.NewInt(0)
   102  		for _, b := range blocks {
   103  			totalDifficulty.Add(totalDifficulty, b.Difficulty())
   104  		}
   105  		config.TerminalTotalDifficulty = totalDifficulty
   106  	}
   107  	return genesis, blocks
   108  }
   109  
   110  func TestEth2AssembleBlock(t *testing.T) {
   111  	genesis, blocks := generateMergeChain(10, false)
   112  	n, ethservice := startEthService(t, genesis, blocks)
   113  	defer n.Close()
   114  
   115  	api := NewConsensusAPI(ethservice)
   116  	signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID)
   117  	tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey)
   118  	if err != nil {
   119  		t.Fatalf("error signing transaction, err=%v", err)
   120  	}
   121  	ethservice.TxPool().Add([]*types.Transaction{tx}, true)
   122  	blockParams := engine.PayloadAttributes{
   123  		Timestamp: blocks[9].Time() + 5,
   124  	}
   125  	// The miner needs to pick up on the txs in the pool, so a few retries might be
   126  	// needed.
   127  	if _, testErr := assembleWithTransactions(api, blocks[9].Hash(), &blockParams, 1); testErr != nil {
   128  		t.Fatal(testErr)
   129  	}
   130  }
   131  
   132  // assembleWithTransactions tries to assemble a block, retrying until it has 'want',
   133  // number of transactions in it, or it has retried three times.
   134  func assembleWithTransactions(api *ConsensusAPI, parentHash common.Hash, params *engine.PayloadAttributes, want int) (execData *engine.ExecutableData, err error) {
   135  	for retries := 3; retries > 0; retries-- {
   136  		execData, err = assembleBlock(api, parentHash, params)
   137  		if err != nil {
   138  			return nil, err
   139  		}
   140  		if have, want := len(execData.Transactions), want; have != want {
   141  			err = fmt.Errorf("invalid number of transactions, have %d want %d", have, want)
   142  			continue
   143  		}
   144  		return execData, nil
   145  	}
   146  	return nil, err
   147  }
   148  
   149  func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
   150  	genesis, blocks := generateMergeChain(10, false)
   151  	n, ethservice := startEthService(t, genesis, blocks[:9])
   152  	defer n.Close()
   153  
   154  	api := NewConsensusAPI(ethservice)
   155  
   156  	// Put the 10th block's tx in the pool and produce a new block
   157  	txs := blocks[9].Transactions()
   158  	api.eth.TxPool().Add(txs, true)
   159  	blockParams := engine.PayloadAttributes{
   160  		Timestamp: blocks[8].Time() + 5,
   161  	}
   162  	// The miner needs to pick up on the txs in the pool, so a few retries might be
   163  	// needed.
   164  	if _, err := assembleWithTransactions(api, blocks[8].Hash(), &blockParams, blocks[9].Transactions().Len()); err != nil {
   165  		t.Fatal(err)
   166  	}
   167  }
   168  
   169  func TestEth2PrepareAndGetPayload(t *testing.T) {
   170  	genesis, blocks := generateMergeChain(10, false)
   171  	// We need to properly set the terminal total difficulty
   172  	genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty())
   173  	n, ethservice := startEthService(t, genesis, blocks[:9])
   174  	defer n.Close()
   175  
   176  	api := NewConsensusAPI(ethservice)
   177  
   178  	// Put the 10th block's tx in the pool and produce a new block
   179  	txs := blocks[9].Transactions()
   180  	ethservice.TxPool().Add(txs, true)
   181  	blockParams := engine.PayloadAttributes{
   182  		Timestamp: blocks[8].Time() + 5,
   183  	}
   184  	fcState := engine.ForkchoiceStateV1{
   185  		HeadBlockHash:      blocks[8].Hash(),
   186  		SafeBlockHash:      common.Hash{},
   187  		FinalizedBlockHash: common.Hash{},
   188  	}
   189  	_, err := api.ForkchoiceUpdatedV1(fcState, &blockParams)
   190  	if err != nil {
   191  		t.Fatalf("error preparing payload, err=%v", err)
   192  	}
   193  	// give the payload some time to be built
   194  	payloadID := (&miner.BuildPayloadArgs{
   195  		Parent:       fcState.HeadBlockHash,
   196  		Timestamp:    blockParams.Timestamp,
   197  		FeeRecipient: blockParams.SuggestedFeeRecipient,
   198  		Random:       blockParams.Random,
   199  		BeaconRoot:   blockParams.BeaconRoot,
   200  		Version:      engine.PayloadV1,
   201  	}).Id()
   202  	execData, err := api.getPayload(payloadID, true)
   203  	if err != nil {
   204  		t.Fatalf("error getting payload, err=%v", err)
   205  	}
   206  	if len(execData.ExecutionPayload.Transactions) != blocks[9].Transactions().Len() {
   207  		t.Fatalf("invalid number of transactions %d != 1", len(execData.ExecutionPayload.Transactions))
   208  	}
   209  	// Test invalid payloadID
   210  	var invPayload engine.PayloadID
   211  	copy(invPayload[:], payloadID[:])
   212  	invPayload[0] = ^invPayload[0]
   213  	_, err = api.GetPayloadV1(invPayload)
   214  	if err == nil {
   215  		t.Fatal("expected error retrieving invalid payload")
   216  	}
   217  }
   218  
   219  func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) {
   220  	t.Helper()
   221  
   222  	if len(logsCh) != wantNew {
   223  		t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew)
   224  	}
   225  	if len(rmLogsCh) != wantRemoved {
   226  		t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved)
   227  	}
   228  	// Drain events.
   229  	for i := 0; i < len(logsCh); i++ {
   230  		<-logsCh
   231  	}
   232  	for i := 0; i < len(rmLogsCh); i++ {
   233  		<-rmLogsCh
   234  	}
   235  }
   236  
   237  func TestInvalidPayloadTimestamp(t *testing.T) {
   238  	genesis, preMergeBlocks := generateMergeChain(10, false)
   239  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   240  	defer n.Close()
   241  	var (
   242  		api    = NewConsensusAPI(ethservice)
   243  		parent = ethservice.BlockChain().CurrentBlock()
   244  	)
   245  	tests := []struct {
   246  		time      uint64
   247  		shouldErr bool
   248  	}{
   249  		{0, true},
   250  		{parent.Time, true},
   251  		{parent.Time - 1, true},
   252  		{parent.Time + 1, false},
   253  		{uint64(time.Now().Unix()) + uint64(time.Minute), false},
   254  	}
   255  
   256  	for i, test := range tests {
   257  		t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) {
   258  			params := engine.PayloadAttributes{
   259  				Timestamp:             test.time,
   260  				Random:                crypto.Keccak256Hash([]byte{byte(123)}),
   261  				SuggestedFeeRecipient: parent.Coinbase,
   262  			}
   263  			fcState := engine.ForkchoiceStateV1{
   264  				HeadBlockHash:      parent.Hash(),
   265  				SafeBlockHash:      common.Hash{},
   266  				FinalizedBlockHash: common.Hash{},
   267  			}
   268  			_, err := api.ForkchoiceUpdatedV1(fcState, &params)
   269  			if test.shouldErr && err == nil {
   270  				t.Fatalf("expected error preparing payload with invalid timestamp, err=%v", err)
   271  			} else if !test.shouldErr && err != nil {
   272  				t.Fatalf("error preparing payload with valid timestamp, err=%v", err)
   273  			}
   274  		})
   275  	}
   276  }
   277  
   278  func TestEth2NewBlock(t *testing.T) {
   279  	genesis, preMergeBlocks := generateMergeChain(10, false)
   280  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   281  	defer n.Close()
   282  
   283  	var (
   284  		api    = NewConsensusAPI(ethservice)
   285  		parent = preMergeBlocks[len(preMergeBlocks)-1]
   286  
   287  		// This EVM code generates a log when the contract is created.
   288  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   289  	)
   290  	// The event channels.
   291  	newLogCh := make(chan []*types.Log, 10)
   292  	rmLogsCh := make(chan core.RemovedLogsEvent, 10)
   293  	ethservice.BlockChain().SubscribeLogsEvent(newLogCh)
   294  	ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh)
   295  
   296  	for i := 0; i < 10; i++ {
   297  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   298  		nonce := statedb.GetNonce(testAddr)
   299  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   300  		ethservice.TxPool().Add([]*types.Transaction{tx}, true)
   301  
   302  		execData, err := assembleWithTransactions(api, parent.Hash(), &engine.PayloadAttributes{
   303  			Timestamp: parent.Time() + 5,
   304  		}, 1)
   305  		if err != nil {
   306  			t.Fatalf("Failed to create the executable data, block %d: %v", i, err)
   307  		}
   308  		block, err := engine.ExecutableDataToBlock(*execData, nil, nil, nil)
   309  		if err != nil {
   310  			t.Fatalf("Failed to convert executable data to block %v", err)
   311  		}
   312  		newResp, err := api.NewPayloadV1(*execData)
   313  		switch {
   314  		case err != nil:
   315  			t.Fatalf("Failed to insert block: %v", err)
   316  		case newResp.Status != "VALID":
   317  			t.Fatalf("Failed to insert block: %v", newResp.Status)
   318  		case ethservice.BlockChain().CurrentBlock().Number.Uint64() != block.NumberU64()-1:
   319  			t.Fatalf("Chain head shouldn't be updated")
   320  		}
   321  		checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
   322  		fcState := engine.ForkchoiceStateV1{
   323  			HeadBlockHash:      block.Hash(),
   324  			SafeBlockHash:      block.Hash(),
   325  			FinalizedBlockHash: block.Hash(),
   326  		}
   327  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   328  			t.Fatalf("Failed to insert block: %v", err)
   329  		}
   330  		if have, want := ethservice.BlockChain().CurrentBlock().Number.Uint64(), block.NumberU64(); have != want {
   331  			t.Fatalf("Chain head should be updated, have %d want %d", have, want)
   332  		}
   333  		checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
   334  
   335  		parent = block
   336  	}
   337  
   338  	// Introduce fork chain
   339  	var (
   340  		head = ethservice.BlockChain().CurrentBlock().Number.Uint64()
   341  	)
   342  	parent = preMergeBlocks[len(preMergeBlocks)-1]
   343  	for i := 0; i < 10; i++ {
   344  		execData, err := assembleBlock(api, parent.Hash(), &engine.PayloadAttributes{
   345  			Timestamp: parent.Time() + 6,
   346  		})
   347  		if err != nil {
   348  			t.Fatalf("Failed to create the executable data %v", err)
   349  		}
   350  		block, err := engine.ExecutableDataToBlock(*execData, nil, nil, nil)
   351  		if err != nil {
   352  			t.Fatalf("Failed to convert executable data to block %v", err)
   353  		}
   354  		newResp, err := api.NewPayloadV1(*execData)
   355  		if err != nil || newResp.Status != "VALID" {
   356  			t.Fatalf("Failed to insert block: %v", err)
   357  		}
   358  		if ethservice.BlockChain().CurrentBlock().Number.Uint64() != head {
   359  			t.Fatalf("Chain head shouldn't be updated")
   360  		}
   361  
   362  		fcState := engine.ForkchoiceStateV1{
   363  			HeadBlockHash:      block.Hash(),
   364  			SafeBlockHash:      block.Hash(),
   365  			FinalizedBlockHash: block.Hash(),
   366  		}
   367  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   368  			t.Fatalf("Failed to insert block: %v", err)
   369  		}
   370  		if ethservice.BlockChain().CurrentBlock().Number.Uint64() != block.NumberU64() {
   371  			t.Fatalf("Chain head should be updated")
   372  		}
   373  		parent, head = block, block.NumberU64()
   374  	}
   375  }
   376  
   377  func TestEth2DeepReorg(t *testing.T) {
   378  	// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
   379  	// before the totalTerminalDifficulty threshold
   380  	/*
   381  		genesis, preMergeBlocks := generateMergeChain(core.TriesInMemory * 2, false)
   382  		n, ethservice := startEthService(t, genesis, preMergeBlocks)
   383  		defer n.Close()
   384  
   385  		var (
   386  			api    = NewConsensusAPI(ethservice, nil)
   387  			parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1]
   388  			head   = ethservice.BlockChain().CurrentBlock().Number.Uint64()()
   389  		)
   390  		if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) {
   391  			t.Errorf("Block %d not pruned", parent.NumberU64())
   392  		}
   393  		for i := 0; i < 10; i++ {
   394  			execData, err := api.assembleBlock(AssembleBlockParams{
   395  				ParentHash: parent.Hash(),
   396  				Timestamp:  parent.Time() + 5,
   397  			})
   398  			if err != nil {
   399  				t.Fatalf("Failed to create the executable data %v", err)
   400  			}
   401  			block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData)
   402  			if err != nil {
   403  				t.Fatalf("Failed to convert executable data to block %v", err)
   404  			}
   405  			newResp, err := api.ExecutePayload(*execData)
   406  			if err != nil || newResp.Status != "VALID" {
   407  				t.Fatalf("Failed to insert block: %v", err)
   408  			}
   409  			if ethservice.BlockChain().CurrentBlock().Number.Uint64()() != head {
   410  				t.Fatalf("Chain head shouldn't be updated")
   411  			}
   412  			if err := api.setHead(block.Hash()); err != nil {
   413  				t.Fatalf("Failed to set head: %v", err)
   414  			}
   415  			if ethservice.BlockChain().CurrentBlock().Number.Uint64()() != block.NumberU64() {
   416  				t.Fatalf("Chain head should be updated")
   417  			}
   418  			parent, head = block, block.NumberU64()
   419  		}
   420  	*/
   421  }
   422  
   423  // startEthService creates a full node instance for testing.
   424  func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) {
   425  	t.Helper()
   426  
   427  	n, err := node.New(&node.Config{
   428  		P2P: p2p.Config{
   429  			ListenAddr:  "0.0.0.0:0",
   430  			NoDiscovery: true,
   431  			MaxPeers:    25,
   432  		}})
   433  	if err != nil {
   434  		t.Fatal("can't create node:", err)
   435  	}
   436  
   437  	mcfg := miner.DefaultConfig
   438  	ethcfg := &ethconfig.Config{Genesis: genesis, SyncMode: ethconfig.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: mcfg}
   439  	ethservice, err := eth.New(n, ethcfg)
   440  	if err != nil {
   441  		t.Fatal("can't create eth service:", err)
   442  	}
   443  	if err := n.Start(); err != nil {
   444  		t.Fatal("can't start node:", err)
   445  	}
   446  	if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil {
   447  		n.Close()
   448  		t.Fatal("can't import test blocks:", err)
   449  	}
   450  	if err := ethservice.TxPool().Sync(); err != nil {
   451  		t.Fatal("failed to sync txpool after initial blockchain import:", err)
   452  	}
   453  
   454  	ethservice.SetSynced()
   455  	return n, ethservice
   456  }
   457  
   458  func TestFullAPI(t *testing.T) {
   459  	genesis, preMergeBlocks := generateMergeChain(10, false)
   460  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   461  	defer n.Close()
   462  	var (
   463  		parent = ethservice.BlockChain().CurrentBlock()
   464  		// This EVM code generates a log when the contract is created.
   465  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   466  	)
   467  
   468  	callback := func(parent *types.Header) {
   469  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root)
   470  		nonce := statedb.GetNonce(testAddr)
   471  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   472  		ethservice.TxPool().Add([]*types.Transaction{tx}, false)
   473  	}
   474  
   475  	setupBlocks(t, ethservice, 10, parent, callback, nil, nil)
   476  }
   477  
   478  func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Header, callback func(parent *types.Header), withdrawals [][]*types.Withdrawal, beaconRoots []common.Hash) []*types.Header {
   479  	api := NewConsensusAPI(ethservice)
   480  	var blocks []*types.Header
   481  	for i := 0; i < n; i++ {
   482  		callback(parent)
   483  		var w []*types.Withdrawal
   484  		if withdrawals != nil {
   485  			w = withdrawals[i]
   486  		}
   487  		var h *common.Hash
   488  		if beaconRoots != nil {
   489  			h = &beaconRoots[i]
   490  		}
   491  
   492  		envelope := getNewEnvelope(t, api, parent, w, h)
   493  		execResp, err := api.newPayload(*envelope.ExecutionPayload, []common.Hash{}, h, envelope.Requests, false)
   494  		if err != nil {
   495  			t.Fatalf("can't execute payload: %v", err)
   496  		}
   497  		if execResp.Status != engine.VALID {
   498  			t.Fatalf("invalid status: %v %s", execResp.Status, *execResp.ValidationError)
   499  		}
   500  		payload := envelope.ExecutionPayload
   501  		fcState := engine.ForkchoiceStateV1{
   502  			HeadBlockHash:      payload.BlockHash,
   503  			SafeBlockHash:      payload.ParentHash,
   504  			FinalizedBlockHash: payload.ParentHash,
   505  		}
   506  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   507  			t.Fatalf("Failed to insert block: %v", err)
   508  		}
   509  		if ethservice.BlockChain().CurrentBlock().Number.Uint64() != payload.Number {
   510  			t.Fatal("Chain head should be updated")
   511  		}
   512  		if ethservice.BlockChain().CurrentFinalBlock().Number.Uint64() != payload.Number-1 {
   513  			t.Fatal("Finalized block should be updated")
   514  		}
   515  		parent = ethservice.BlockChain().CurrentBlock()
   516  		blocks = append(blocks, parent)
   517  	}
   518  	return blocks
   519  }
   520  
   521  func TestExchangeTransitionConfig(t *testing.T) {
   522  	genesis, preMergeBlocks := generateMergeChain(10, false)
   523  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   524  	defer n.Close()
   525  
   526  	// invalid ttd
   527  	api := NewConsensusAPI(ethservice)
   528  	config := engine.TransitionConfigurationV1{
   529  		TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)),
   530  		TerminalBlockHash:       common.Hash{},
   531  		TerminalBlockNumber:     0,
   532  	}
   533  	if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
   534  		t.Fatal("expected error on invalid config, invalid ttd")
   535  	}
   536  	// invalid terminal block hash
   537  	config = engine.TransitionConfigurationV1{
   538  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   539  		TerminalBlockHash:       common.Hash{1},
   540  		TerminalBlockNumber:     0,
   541  	}
   542  	if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
   543  		t.Fatal("expected error on invalid config, invalid hash")
   544  	}
   545  	// valid config
   546  	config = engine.TransitionConfigurationV1{
   547  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   548  		TerminalBlockHash:       common.Hash{},
   549  		TerminalBlockNumber:     0,
   550  	}
   551  	if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
   552  		t.Fatalf("expected no error on valid config, got %v", err)
   553  	}
   554  	// valid config
   555  	config = engine.TransitionConfigurationV1{
   556  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   557  		TerminalBlockHash:       preMergeBlocks[5].Hash(),
   558  		TerminalBlockNumber:     6,
   559  	}
   560  	if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
   561  		t.Fatalf("expected no error on valid config, got %v", err)
   562  	}
   563  }
   564  
   565  /*
   566  TestNewPayloadOnInvalidChain sets up a valid chain and tries to feed blocks
   567  from an invalid chain to test if latestValidHash (LVH) works correctly.
   568  
   569  We set up the following chain where P1 ... Pn and P1” are valid while
   570  P1' is invalid.
   571  We expect
   572  (1) The LVH to point to the current inserted payload if it was valid.
   573  (2) The LVH to point to the valid parent on an invalid payload (if the parent is available).
   574  (3) If the parent is unavailable, the LVH should not be set.
   575  
   576  	CommonAncestor◄─▲── P1 ◄── P2  ◄─ P3  ◄─ ... ◄─ Pn
   577  	                │
   578  	                └── P1' ◄─ P2' ◄─ P3' ◄─ ... ◄─ Pn'
   579  	                │
   580  	                └── P1''
   581  */
   582  func TestNewPayloadOnInvalidChain(t *testing.T) {
   583  	genesis, preMergeBlocks := generateMergeChain(10, false)
   584  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   585  	defer n.Close()
   586  
   587  	var (
   588  		api    = NewConsensusAPI(ethservice)
   589  		parent = ethservice.BlockChain().CurrentBlock()
   590  		signer = types.LatestSigner(ethservice.BlockChain().Config())
   591  		// This EVM code generates a log when the contract is created.
   592  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   593  	)
   594  	for i := 0; i < 10; i++ {
   595  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root)
   596  		tx := types.MustSignNewTx(testKey, signer, &types.LegacyTx{
   597  			Nonce:    statedb.GetNonce(testAddr),
   598  			Value:    new(big.Int),
   599  			Gas:      1000000,
   600  			GasPrice: big.NewInt(2 * params.InitialBaseFee),
   601  			Data:     logCode,
   602  		})
   603  		ethservice.TxPool().Add([]*types.Transaction{tx}, true)
   604  		var (
   605  			params = engine.PayloadAttributes{
   606  				Timestamp:             parent.Time + 1,
   607  				Random:                crypto.Keccak256Hash([]byte{byte(i)}),
   608  				SuggestedFeeRecipient: parent.Coinbase,
   609  			}
   610  			fcState = engine.ForkchoiceStateV1{
   611  				HeadBlockHash:      parent.Hash(),
   612  				SafeBlockHash:      common.Hash{},
   613  				FinalizedBlockHash: common.Hash{},
   614  			}
   615  			payload *engine.ExecutionPayloadEnvelope
   616  			resp    engine.ForkChoiceResponse
   617  			err     error
   618  		)
   619  		for i := 0; ; i++ {
   620  			if resp, err = api.ForkchoiceUpdatedV1(fcState, &params); err != nil {
   621  				t.Fatalf("error preparing payload, err=%v", err)
   622  			}
   623  			if resp.PayloadStatus.Status != engine.VALID {
   624  				t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status)
   625  			}
   626  			// give the payload some time to be built
   627  			if payload, err = api.getPayload(*resp.PayloadID, true); err != nil {
   628  				t.Fatalf("can't get payload: %v", err)
   629  			}
   630  			if len(payload.ExecutionPayload.Transactions) > 0 {
   631  				break
   632  			}
   633  			// No luck this time we need to update the params and try again.
   634  			params.Timestamp = params.Timestamp + 1
   635  			if i > 10 {
   636  				t.Fatalf("payload should not be empty")
   637  			}
   638  		}
   639  		execResp, err := api.NewPayloadV1(*payload.ExecutionPayload)
   640  		if err != nil {
   641  			t.Fatalf("can't execute payload: %v", err)
   642  		}
   643  		if execResp.Status != engine.VALID {
   644  			t.Fatalf("invalid status: %v", execResp.Status)
   645  		}
   646  		fcState = engine.ForkchoiceStateV1{
   647  			HeadBlockHash:      payload.ExecutionPayload.BlockHash,
   648  			SafeBlockHash:      payload.ExecutionPayload.ParentHash,
   649  			FinalizedBlockHash: payload.ExecutionPayload.ParentHash,
   650  		}
   651  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   652  			t.Fatalf("Failed to insert block: %v", err)
   653  		}
   654  		if ethservice.BlockChain().CurrentBlock().Number.Uint64() != payload.ExecutionPayload.Number {
   655  			t.Fatalf("Chain head should be updated")
   656  		}
   657  		parent = ethservice.BlockChain().CurrentBlock()
   658  	}
   659  }
   660  
   661  func assembleEnvelope(api *ConsensusAPI, parentHash common.Hash, params *engine.PayloadAttributes) (*engine.ExecutionPayloadEnvelope, error) {
   662  	args := &miner.BuildPayloadArgs{
   663  		Parent:       parentHash,
   664  		Timestamp:    params.Timestamp,
   665  		FeeRecipient: params.SuggestedFeeRecipient,
   666  		Random:       params.Random,
   667  		Withdrawals:  params.Withdrawals,
   668  		BeaconRoot:   params.BeaconRoot,
   669  	}
   670  	payload, err := api.eth.Miner().BuildPayload(args, false)
   671  	if err != nil {
   672  		return nil, err
   673  	}
   674  	return payload.ResolveFull(), nil
   675  }
   676  
   677  func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *engine.PayloadAttributes) (*engine.ExecutableData, error) {
   678  	envelope, err := assembleEnvelope(api, parentHash, params)
   679  	if err != nil {
   680  		return nil, err
   681  	}
   682  	return envelope.ExecutionPayload, nil
   683  }
   684  
   685  func TestEmptyBlocks(t *testing.T) {
   686  	genesis, preMergeBlocks := generateMergeChain(10, false)
   687  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   688  	defer n.Close()
   689  
   690  	commonAncestor := ethservice.BlockChain().CurrentBlock()
   691  	api := NewConsensusAPI(ethservice)
   692  
   693  	// Setup 10 blocks on the canonical chain
   694  	setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Header) {}, nil, nil)
   695  
   696  	// (1) check LatestValidHash by sending a normal payload (P1'')
   697  	payload := getNewPayload(t, api, commonAncestor, nil, nil)
   698  
   699  	status, err := api.NewPayloadV1(*payload)
   700  	if err != nil {
   701  		t.Fatal(err)
   702  	}
   703  	if status.Status != engine.VALID {
   704  		t.Errorf("invalid status: expected VALID got: %v", status.Status)
   705  	}
   706  	if !bytes.Equal(status.LatestValidHash[:], payload.BlockHash[:]) {
   707  		t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, payload.BlockHash)
   708  	}
   709  
   710  	// (2) Now send P1' which is invalid
   711  	payload = getNewPayload(t, api, commonAncestor, nil, nil)
   712  	payload.GasUsed += 1
   713  	payload = setBlockhash(payload)
   714  	// Now latestValidHash should be the common ancestor
   715  	status, err = api.NewPayloadV1(*payload)
   716  	if err != nil {
   717  		t.Fatal(err)
   718  	}
   719  	if status.Status != engine.INVALID {
   720  		t.Errorf("invalid status: expected INVALID got: %v", status.Status)
   721  	}
   722  	// Expect 0x0 on INVALID block on top of PoW block
   723  	expected := common.Hash{}
   724  	if !bytes.Equal(status.LatestValidHash[:], expected[:]) {
   725  		t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, expected)
   726  	}
   727  
   728  	// (3) Now send a payload with unknown parent
   729  	payload = getNewPayload(t, api, commonAncestor, nil, nil)
   730  	payload.ParentHash = common.Hash{1}
   731  	payload = setBlockhash(payload)
   732  	// Now latestValidHash should be the common ancestor
   733  	status, err = api.NewPayloadV1(*payload)
   734  	if err != nil {
   735  		t.Fatal(err)
   736  	}
   737  	if status.Status != engine.SYNCING {
   738  		t.Errorf("invalid status: expected SYNCING got: %v", status.Status)
   739  	}
   740  	if status.LatestValidHash != nil {
   741  		t.Fatalf("invalid LVH: got %v wanted nil", status.LatestValidHash)
   742  	}
   743  }
   744  
   745  func getNewEnvelope(t *testing.T, api *ConsensusAPI, parent *types.Header, withdrawals []*types.Withdrawal, beaconRoot *common.Hash) *engine.ExecutionPayloadEnvelope {
   746  	params := engine.PayloadAttributes{
   747  		Timestamp:             parent.Time + 1,
   748  		Random:                crypto.Keccak256Hash([]byte{byte(1)}),
   749  		SuggestedFeeRecipient: parent.Coinbase,
   750  		Withdrawals:           withdrawals,
   751  		BeaconRoot:            beaconRoot,
   752  	}
   753  
   754  	envelope, err := assembleEnvelope(api, parent.Hash(), &params)
   755  	if err != nil {
   756  		t.Fatal(err)
   757  	}
   758  	return envelope
   759  }
   760  
   761  func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Header, withdrawals []*types.Withdrawal, beaconRoot *common.Hash) *engine.ExecutableData {
   762  	return getNewEnvelope(t, api, parent, withdrawals, beaconRoot).ExecutionPayload
   763  }
   764  
   765  // setBlockhash sets the blockhash of a modified ExecutableData.
   766  // Can be used to make modified payloads look valid.
   767  func setBlockhash(data *engine.ExecutableData) *engine.ExecutableData {
   768  	txs, _ := decodeTransactions(data.Transactions)
   769  	number := big.NewInt(0)
   770  	number.SetUint64(data.Number)
   771  	header := &types.Header{
   772  		ParentHash:  data.ParentHash,
   773  		UncleHash:   types.EmptyUncleHash,
   774  		Coinbase:    data.FeeRecipient,
   775  		Root:        data.StateRoot,
   776  		TxHash:      types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
   777  		ReceiptHash: data.ReceiptsRoot,
   778  		Bloom:       types.BytesToBloom(data.LogsBloom),
   779  		Difficulty:  common.Big0,
   780  		Number:      number,
   781  		GasLimit:    data.GasLimit,
   782  		GasUsed:     data.GasUsed,
   783  		Time:        data.Timestamp,
   784  		BaseFee:     data.BaseFeePerGas,
   785  		Extra:       data.ExtraData,
   786  		MixDigest:   data.Random,
   787  	}
   788  	block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs})
   789  	data.BlockHash = block.Hash()
   790  	return data
   791  }
   792  
   793  func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
   794  	var txs = make([]*types.Transaction, len(enc))
   795  	for i, encTx := range enc {
   796  		var tx types.Transaction
   797  		if err := tx.UnmarshalBinary(encTx); err != nil {
   798  			return nil, fmt.Errorf("invalid transaction %d: %v", i, err)
   799  		}
   800  		txs[i] = &tx
   801  	}
   802  	return txs, nil
   803  }
   804  
   805  func TestTrickRemoteBlockCache(t *testing.T) {
   806  	// Setup two nodes
   807  	genesis, preMergeBlocks := generateMergeChain(10, false)
   808  	nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks)
   809  	nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks)
   810  	defer nodeA.Close()
   811  	defer nodeB.Close()
   812  	for nodeB.Server().NodeInfo().Ports.Listener == 0 {
   813  		time.Sleep(250 * time.Millisecond)
   814  	}
   815  	nodeA.Server().AddPeer(nodeB.Server().Self())
   816  	nodeB.Server().AddPeer(nodeA.Server().Self())
   817  	apiA := NewConsensusAPI(ethserviceA)
   818  	apiB := NewConsensusAPI(ethserviceB)
   819  
   820  	commonAncestor := ethserviceA.BlockChain().CurrentBlock()
   821  
   822  	// Setup 10 blocks on the canonical chain
   823  	setupBlocks(t, ethserviceA, 10, commonAncestor, func(parent *types.Header) {}, nil, nil)
   824  	commonAncestor = ethserviceA.BlockChain().CurrentBlock()
   825  
   826  	var invalidChain []*engine.ExecutableData
   827  	// create a valid payload (P1)
   828  	//payload1 := getNewPayload(t, apiA, commonAncestor)
   829  	//invalidChain = append(invalidChain, payload1)
   830  
   831  	// create an invalid payload2 (P2)
   832  	payload2 := getNewPayload(t, apiA, commonAncestor, nil, nil)
   833  	//payload2.ParentHash = payload1.BlockHash
   834  	payload2.GasUsed += 1
   835  	payload2 = setBlockhash(payload2)
   836  	invalidChain = append(invalidChain, payload2)
   837  
   838  	head := payload2
   839  	// create some valid payloads on top
   840  	for i := 0; i < 10; i++ {
   841  		payload := getNewPayload(t, apiA, commonAncestor, nil, nil)
   842  		payload.ParentHash = head.BlockHash
   843  		payload = setBlockhash(payload)
   844  		invalidChain = append(invalidChain, payload)
   845  		head = payload
   846  	}
   847  
   848  	// feed the payloads to node B
   849  	for _, payload := range invalidChain {
   850  		status, err := apiB.NewPayloadV1(*payload)
   851  		if err != nil {
   852  			panic(err)
   853  		}
   854  		if status.Status == engine.VALID {
   855  			t.Error("invalid status: VALID on an invalid chain")
   856  		}
   857  		// Now reorg to the head of the invalid chain
   858  		resp, err := apiB.ForkchoiceUpdatedV1(engine.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil)
   859  		if err != nil {
   860  			t.Fatal(err)
   861  		}
   862  		if resp.PayloadStatus.Status == engine.VALID {
   863  			t.Error("invalid status: VALID on an invalid chain")
   864  		}
   865  		time.Sleep(100 * time.Millisecond)
   866  	}
   867  }
   868  
   869  func TestInvalidBloom(t *testing.T) {
   870  	genesis, preMergeBlocks := generateMergeChain(10, false)
   871  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   872  	defer n.Close()
   873  
   874  	commonAncestor := ethservice.BlockChain().CurrentBlock()
   875  	api := NewConsensusAPI(ethservice)
   876  
   877  	// Setup 10 blocks on the canonical chain
   878  	setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Header) {}, nil, nil)
   879  
   880  	// (1) check LatestValidHash by sending a normal payload (P1'')
   881  	payload := getNewPayload(t, api, commonAncestor, nil, nil)
   882  	payload.LogsBloom = append(payload.LogsBloom, byte(1))
   883  	status, err := api.NewPayloadV1(*payload)
   884  	if err != nil {
   885  		t.Fatal(err)
   886  	}
   887  	if status.Status != engine.INVALID {
   888  		t.Errorf("invalid status: expected INVALID got: %v", status.Status)
   889  	}
   890  }
   891  
   892  // TestSimultaneousNewBlock does several parallel inserts, both as
   893  // newPayLoad and forkchoiceUpdate. This is to test that the api behaves
   894  // well even of the caller is not being 'serial'.
   895  func TestSimultaneousNewBlock(t *testing.T) {
   896  	genesis, preMergeBlocks := generateMergeChain(10, false)
   897  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   898  	defer n.Close()
   899  
   900  	var (
   901  		api    = NewConsensusAPI(ethservice)
   902  		parent = preMergeBlocks[len(preMergeBlocks)-1]
   903  	)
   904  	for i := 0; i < 10; i++ {
   905  		execData, err := assembleBlock(api, parent.Hash(), &engine.PayloadAttributes{
   906  			Timestamp: parent.Time() + 5,
   907  		})
   908  		if err != nil {
   909  			t.Fatalf("Failed to create the executable data %v", err)
   910  		}
   911  		// Insert it 10 times in parallel. Should be ignored.
   912  		{
   913  			var (
   914  				wg      sync.WaitGroup
   915  				testErr error
   916  				errMu   sync.Mutex
   917  			)
   918  			wg.Add(10)
   919  			for ii := 0; ii < 10; ii++ {
   920  				go func() {
   921  					defer wg.Done()
   922  					if newResp, err := api.NewPayloadV1(*execData); err != nil {
   923  						errMu.Lock()
   924  						testErr = fmt.Errorf("failed to insert block: %w", err)
   925  						errMu.Unlock()
   926  					} else if newResp.Status != "VALID" {
   927  						errMu.Lock()
   928  						testErr = fmt.Errorf("failed to insert block: %v", newResp.Status)
   929  						errMu.Unlock()
   930  					}
   931  				}()
   932  			}
   933  			wg.Wait()
   934  			if testErr != nil {
   935  				t.Fatal(testErr)
   936  			}
   937  		}
   938  		block, err := engine.ExecutableDataToBlock(*execData, nil, nil, nil)
   939  		if err != nil {
   940  			t.Fatalf("Failed to convert executable data to block %v", err)
   941  		}
   942  		if ethservice.BlockChain().CurrentBlock().Number.Uint64() != block.NumberU64()-1 {
   943  			t.Fatalf("Chain head shouldn't be updated")
   944  		}
   945  		fcState := engine.ForkchoiceStateV1{
   946  			HeadBlockHash:      block.Hash(),
   947  			SafeBlockHash:      block.Hash(),
   948  			FinalizedBlockHash: block.Hash(),
   949  		}
   950  		{
   951  			var (
   952  				wg      sync.WaitGroup
   953  				testErr error
   954  				errMu   sync.Mutex
   955  			)
   956  			wg.Add(10)
   957  			// Do each FCU 10 times
   958  			for ii := 0; ii < 10; ii++ {
   959  				go func() {
   960  					defer wg.Done()
   961  					if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   962  						errMu.Lock()
   963  						testErr = fmt.Errorf("failed to insert block: %w", err)
   964  						errMu.Unlock()
   965  					}
   966  				}()
   967  			}
   968  			wg.Wait()
   969  			if testErr != nil {
   970  				t.Fatal(testErr)
   971  			}
   972  		}
   973  		if have, want := ethservice.BlockChain().CurrentBlock().Number.Uint64(), block.NumberU64(); have != want {
   974  			t.Fatalf("Chain head should be updated, have %d want %d", have, want)
   975  		}
   976  		parent = block
   977  	}
   978  }
   979  
   980  // TestWithdrawals creates and verifies two post-Shanghai blocks. The first
   981  // includes zero withdrawals and the second includes two.
   982  func TestWithdrawals(t *testing.T) {
   983  	genesis, blocks := generateMergeChain(10, true)
   984  	// Set shanghai time to last block + 5 seconds (first post-merge block)
   985  	time := blocks[len(blocks)-1].Time() + 5
   986  	genesis.Config.ShanghaiTime = &time
   987  
   988  	n, ethservice := startEthService(t, genesis, blocks)
   989  	defer n.Close()
   990  
   991  	api := NewConsensusAPI(ethservice)
   992  
   993  	// 10: Build Shanghai block with no withdrawals.
   994  	parent := ethservice.BlockChain().CurrentHeader()
   995  	blockParams := engine.PayloadAttributes{
   996  		Timestamp:   parent.Time + 5,
   997  		Withdrawals: make([]*types.Withdrawal, 0),
   998  	}
   999  	fcState := engine.ForkchoiceStateV1{
  1000  		HeadBlockHash: parent.Hash(),
  1001  	}
  1002  	resp, err := api.ForkchoiceUpdatedV2(fcState, &blockParams)
  1003  	if err != nil {
  1004  		t.Fatalf("error preparing payload, err=%v", err)
  1005  	}
  1006  	if resp.PayloadStatus.Status != engine.VALID {
  1007  		t.Fatalf("unexpected status (got: %s, want: %s)", resp.PayloadStatus.Status, engine.VALID)
  1008  	}
  1009  
  1010  	// 10: verify state root is the same as parent
  1011  	payloadID := (&miner.BuildPayloadArgs{
  1012  		Parent:       fcState.HeadBlockHash,
  1013  		Timestamp:    blockParams.Timestamp,
  1014  		FeeRecipient: blockParams.SuggestedFeeRecipient,
  1015  		Random:       blockParams.Random,
  1016  		Withdrawals:  blockParams.Withdrawals,
  1017  		BeaconRoot:   blockParams.BeaconRoot,
  1018  		Version:      engine.PayloadV2,
  1019  	}).Id()
  1020  	execData, err := api.GetPayloadV2(payloadID)
  1021  	if err != nil {
  1022  		t.Fatalf("error getting payload, err=%v", err)
  1023  	}
  1024  	if execData.ExecutionPayload.StateRoot != parent.Root {
  1025  		t.Fatalf("mismatch state roots (got: %s, want: %s)", execData.ExecutionPayload.StateRoot, blocks[8].Root())
  1026  	}
  1027  
  1028  	// 10: verify locally built block
  1029  	if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
  1030  		t.Fatalf("error validating payload: %v", err)
  1031  	} else if status.Status != engine.VALID {
  1032  		t.Fatalf("invalid payload")
  1033  	}
  1034  
  1035  	// 11: build shanghai block with withdrawal
  1036  	aa := common.Address{0xaa}
  1037  	bb := common.Address{0xbb}
  1038  	blockParams = engine.PayloadAttributes{
  1039  		Timestamp: execData.ExecutionPayload.Timestamp + 5,
  1040  		Withdrawals: []*types.Withdrawal{
  1041  			{
  1042  				Index:   0,
  1043  				Address: aa,
  1044  				Amount:  32,
  1045  			},
  1046  			{
  1047  				Index:   1,
  1048  				Address: bb,
  1049  				Amount:  33,
  1050  			},
  1051  		},
  1052  	}
  1053  	fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
  1054  	_, err = api.ForkchoiceUpdatedV2(fcState, &blockParams)
  1055  	if err != nil {
  1056  		t.Fatalf("error preparing payload, err=%v", err)
  1057  	}
  1058  
  1059  	// 11: verify locally build block.
  1060  	payloadID = (&miner.BuildPayloadArgs{
  1061  		Parent:       fcState.HeadBlockHash,
  1062  		Timestamp:    blockParams.Timestamp,
  1063  		FeeRecipient: blockParams.SuggestedFeeRecipient,
  1064  		Random:       blockParams.Random,
  1065  		Withdrawals:  blockParams.Withdrawals,
  1066  		BeaconRoot:   blockParams.BeaconRoot,
  1067  		Version:      engine.PayloadV2,
  1068  	}).Id()
  1069  	execData, err = api.GetPayloadV2(payloadID)
  1070  	if err != nil {
  1071  		t.Fatalf("error getting payload, err=%v", err)
  1072  	}
  1073  	if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
  1074  		t.Fatalf("error validating payload: %v", err)
  1075  	} else if status.Status != engine.VALID {
  1076  		t.Fatalf("invalid payload")
  1077  	}
  1078  
  1079  	// 11: set block as head.
  1080  	fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
  1081  	_, err = api.ForkchoiceUpdatedV2(fcState, nil)
  1082  	if err != nil {
  1083  		t.Fatalf("error preparing payload, err=%v", err)
  1084  	}
  1085  
  1086  	// 11: verify withdrawals were processed.
  1087  	db, _, err := ethservice.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(execData.ExecutionPayload.Number))
  1088  	if err != nil {
  1089  		t.Fatalf("unable to load db: %v", err)
  1090  	}
  1091  	for i, w := range blockParams.Withdrawals {
  1092  		// w.Amount is in gwei, balance in wei
  1093  		if db.GetBalance(w.Address).Uint64() != w.Amount*params.GWei {
  1094  			t.Fatalf("failed to process withdrawal %d", i)
  1095  		}
  1096  	}
  1097  }
  1098  
  1099  func TestNilWithdrawals(t *testing.T) {
  1100  	genesis, blocks := generateMergeChain(10, true)
  1101  	// Set shanghai time to last block + 4 seconds (first post-merge block)
  1102  	time := blocks[len(blocks)-1].Time() + 4
  1103  	genesis.Config.ShanghaiTime = &time
  1104  
  1105  	n, ethservice := startEthService(t, genesis, blocks)
  1106  	defer n.Close()
  1107  
  1108  	api := NewConsensusAPI(ethservice)
  1109  	parent := ethservice.BlockChain().CurrentHeader()
  1110  	aa := common.Address{0xaa}
  1111  
  1112  	type test struct {
  1113  		blockParams engine.PayloadAttributes
  1114  		wantErr     bool
  1115  	}
  1116  	tests := []test{
  1117  		// Before Shanghai
  1118  		{
  1119  			blockParams: engine.PayloadAttributes{
  1120  				Timestamp:   parent.Time + 2,
  1121  				Withdrawals: nil,
  1122  			},
  1123  			wantErr: false,
  1124  		},
  1125  		{
  1126  			blockParams: engine.PayloadAttributes{
  1127  				Timestamp:   parent.Time + 2,
  1128  				Withdrawals: make([]*types.Withdrawal, 0),
  1129  			},
  1130  			wantErr: true,
  1131  		},
  1132  		{
  1133  			blockParams: engine.PayloadAttributes{
  1134  				Timestamp: parent.Time + 2,
  1135  				Withdrawals: []*types.Withdrawal{
  1136  					{
  1137  						Index:   0,
  1138  						Address: aa,
  1139  						Amount:  32,
  1140  					},
  1141  				},
  1142  			},
  1143  			wantErr: true,
  1144  		},
  1145  		// After Shanghai
  1146  		{
  1147  			blockParams: engine.PayloadAttributes{
  1148  				Timestamp:   parent.Time + 5,
  1149  				Withdrawals: nil,
  1150  			},
  1151  			wantErr: true,
  1152  		},
  1153  		{
  1154  			blockParams: engine.PayloadAttributes{
  1155  				Timestamp:   parent.Time + 5,
  1156  				Withdrawals: make([]*types.Withdrawal, 0),
  1157  			},
  1158  			wantErr: false,
  1159  		},
  1160  		{
  1161  			blockParams: engine.PayloadAttributes{
  1162  				Timestamp: parent.Time + 5,
  1163  				Withdrawals: []*types.Withdrawal{
  1164  					{
  1165  						Index:   0,
  1166  						Address: aa,
  1167  						Amount:  32,
  1168  					},
  1169  				},
  1170  			},
  1171  			wantErr: false,
  1172  		},
  1173  	}
  1174  
  1175  	fcState := engine.ForkchoiceStateV1{
  1176  		HeadBlockHash: parent.Hash(),
  1177  	}
  1178  
  1179  	for _, test := range tests {
  1180  		var (
  1181  			err            error
  1182  			payloadVersion engine.PayloadVersion
  1183  			shanghai       = genesis.Config.IsShanghai(genesis.Config.LondonBlock, test.blockParams.Timestamp)
  1184  		)
  1185  		if !shanghai {
  1186  			payloadVersion = engine.PayloadV1
  1187  			_, err = api.ForkchoiceUpdatedV1(fcState, &test.blockParams)
  1188  		} else {
  1189  			payloadVersion = engine.PayloadV2
  1190  			_, err = api.ForkchoiceUpdatedV2(fcState, &test.blockParams)
  1191  		}
  1192  		if test.wantErr {
  1193  			if err == nil {
  1194  				t.Fatal("wanted error on fcuv2 with invalid withdrawals")
  1195  			}
  1196  			continue
  1197  		}
  1198  		if err != nil {
  1199  			t.Fatalf("error preparing payload, err=%v", err)
  1200  		}
  1201  
  1202  		// 11: verify locally build block.
  1203  		payloadID := (&miner.BuildPayloadArgs{
  1204  			Parent:       fcState.HeadBlockHash,
  1205  			Timestamp:    test.blockParams.Timestamp,
  1206  			FeeRecipient: test.blockParams.SuggestedFeeRecipient,
  1207  			Random:       test.blockParams.Random,
  1208  			Version:      payloadVersion,
  1209  		}).Id()
  1210  		execData, err := api.GetPayloadV2(payloadID)
  1211  		if err != nil {
  1212  			t.Fatalf("error getting payload, err=%v", err)
  1213  		}
  1214  		var status engine.PayloadStatusV1
  1215  		if !shanghai {
  1216  			status, err = api.NewPayloadV1(*execData.ExecutionPayload)
  1217  		} else {
  1218  			status, err = api.NewPayloadV2(*execData.ExecutionPayload)
  1219  		}
  1220  		if err != nil {
  1221  			t.Fatalf("error validating payload: %v", err.(*engine.EngineAPIError).ErrorData())
  1222  		} else if status.Status != engine.VALID {
  1223  			t.Fatalf("invalid payload")
  1224  		}
  1225  	}
  1226  }
  1227  
  1228  func setupBodies(t *testing.T) (*node.Node, *eth.Ethereum, []*types.Block) {
  1229  	genesis, blocks := generateMergeChain(10, true)
  1230  
  1231  	// Enable next forks on the last block.
  1232  	time := blocks[len(blocks)-1].Header().Time + 1
  1233  	genesis.Config.ShanghaiTime = &time
  1234  	genesis.Config.CancunTime = &time
  1235  	genesis.Config.PragueTime = &time
  1236  	genesis.Config.BlobScheduleConfig = params.DefaultBlobSchedule
  1237  
  1238  	n, ethservice := startEthService(t, genesis, blocks)
  1239  
  1240  	var (
  1241  		// This EVM code generates a log when the contract is created.
  1242  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
  1243  		parent  = ethservice.BlockChain().CurrentBlock()
  1244  	)
  1245  
  1246  	// Each block, this callback will include two txs that generate body values like logs and requests.
  1247  	callback := func(parent *types.Header) {
  1248  		var (
  1249  			statedb, _ = ethservice.BlockChain().StateAt(parent.Root)
  1250  			// Create tx to trigger log generator.
  1251  			tx1, _ = types.SignTx(types.NewContractCreation(statedb.GetNonce(testAddr), new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
  1252  			// Create tx to trigger deposit generator.
  1253  			tx2, _ = types.SignTx(types.NewTransaction(statedb.GetNonce(testAddr)+1, ethservice.APIBackend.ChainConfig().DepositContractAddress, new(big.Int), 500000, big.NewInt(2*params.InitialBaseFee), nil), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
  1254  		)
  1255  		ethservice.TxPool().Add([]*types.Transaction{tx1}, false)
  1256  		ethservice.TxPool().Add([]*types.Transaction{tx2}, false)
  1257  	}
  1258  
  1259  	// Make some withdrawals to include.
  1260  	withdrawals := make([][]*types.Withdrawal, 10)
  1261  	withdrawals[0] = nil // should be filtered out by miner
  1262  	withdrawals[1] = make([]*types.Withdrawal, 0)
  1263  	for i := 2; i < len(withdrawals); i++ {
  1264  		addr := make([]byte, 20)
  1265  		crand.Read(addr)
  1266  		withdrawals[i] = []*types.Withdrawal{
  1267  			{Index: rand.Uint64(), Validator: rand.Uint64(), Amount: rand.Uint64(), Address: common.BytesToAddress(addr)},
  1268  		}
  1269  	}
  1270  
  1271  	// Make beacon root update for each block.
  1272  	beaconRoots := make([]common.Hash, 10)
  1273  	for i := 0; i < 10; i++ {
  1274  		beaconRoots[i] = common.Hash{byte(i)}
  1275  	}
  1276  
  1277  	// Create the blocks.
  1278  	newHeaders := setupBlocks(t, ethservice, 10, parent, callback, withdrawals, beaconRoots)
  1279  	newBlocks := make([]*types.Block, len(newHeaders))
  1280  	for i, header := range newHeaders {
  1281  		newBlocks[i] = ethservice.BlockChain().GetBlock(header.Hash(), header.Number.Uint64())
  1282  	}
  1283  
  1284  	return n, ethservice, append(blocks, newBlocks...)
  1285  }
  1286  
  1287  func allHashes(blocks []*types.Block) []common.Hash {
  1288  	var hashes []common.Hash
  1289  	for _, b := range blocks {
  1290  		hashes = append(hashes, b.Hash())
  1291  	}
  1292  	return hashes
  1293  }
  1294  func allBodies(blocks []*types.Block) []*types.Body {
  1295  	var bodies []*types.Body
  1296  	for _, b := range blocks {
  1297  		bodies = append(bodies, b.Body())
  1298  	}
  1299  	return bodies
  1300  }
  1301  
  1302  func TestGetBlockBodiesByHash(t *testing.T) {
  1303  	node, eth, blocks := setupBodies(t)
  1304  	api := NewConsensusAPI(eth)
  1305  	defer node.Close()
  1306  
  1307  	tests := []struct {
  1308  		results []*types.Body
  1309  		hashes  []common.Hash
  1310  	}{
  1311  		// First pow block
  1312  		{
  1313  			results: []*types.Body{eth.BlockChain().GetBlockByNumber(0).Body()},
  1314  			hashes:  []common.Hash{eth.BlockChain().GetBlockByNumber(0).Hash()},
  1315  		},
  1316  		// Last pow block
  1317  		{
  1318  			results: []*types.Body{blocks[9].Body()},
  1319  			hashes:  []common.Hash{blocks[9].Hash()},
  1320  		},
  1321  		// First post-merge block
  1322  		{
  1323  			results: []*types.Body{blocks[10].Body()},
  1324  			hashes:  []common.Hash{blocks[10].Hash()},
  1325  		},
  1326  		// Pre & post merge blocks
  1327  		{
  1328  			results: []*types.Body{blocks[0].Body(), blocks[9].Body(), blocks[14].Body()},
  1329  			hashes:  []common.Hash{blocks[0].Hash(), blocks[9].Hash(), blocks[14].Hash()},
  1330  		},
  1331  		// unavailable block
  1332  		{
  1333  			results: []*types.Body{blocks[0].Body(), nil, blocks[14].Body()},
  1334  			hashes:  []common.Hash{blocks[0].Hash(), {1, 2}, blocks[14].Hash()},
  1335  		},
  1336  		// same block multiple times
  1337  		{
  1338  			results: []*types.Body{blocks[0].Body(), nil, blocks[0].Body(), blocks[0].Body()},
  1339  			hashes:  []common.Hash{blocks[0].Hash(), {1, 2}, blocks[0].Hash(), blocks[0].Hash()},
  1340  		},
  1341  		// all blocks
  1342  		{
  1343  			results: allBodies(blocks),
  1344  			hashes:  allHashes(blocks),
  1345  		},
  1346  	}
  1347  
  1348  	for k, test := range tests {
  1349  		result := api.GetPayloadBodiesByHashV2(test.hashes)
  1350  		for i, r := range result {
  1351  			if err := checkEqualBody(test.results[i], r); err != nil {
  1352  				t.Fatalf("test %v: invalid response: %v\nexpected %+v\ngot %+v", k, err, test.results[i], r)
  1353  			}
  1354  		}
  1355  	}
  1356  }
  1357  
  1358  func TestGetBlockBodiesByRange(t *testing.T) {
  1359  	node, eth, blocks := setupBodies(t)
  1360  	api := NewConsensusAPI(eth)
  1361  	defer node.Close()
  1362  
  1363  	tests := []struct {
  1364  		results []*types.Body
  1365  		start   hexutil.Uint64
  1366  		count   hexutil.Uint64
  1367  	}{
  1368  		{
  1369  			results: []*types.Body{blocks[9].Body()},
  1370  			start:   10,
  1371  			count:   1,
  1372  		},
  1373  		// Genesis
  1374  		{
  1375  			results: []*types.Body{blocks[0].Body()},
  1376  			start:   1,
  1377  			count:   1,
  1378  		},
  1379  		// First post-merge block
  1380  		{
  1381  			results: []*types.Body{blocks[9].Body()},
  1382  			start:   10,
  1383  			count:   1,
  1384  		},
  1385  		// Pre & post merge blocks
  1386  		{
  1387  			results: []*types.Body{blocks[7].Body(), blocks[8].Body(), blocks[9].Body(), blocks[10].Body()},
  1388  			start:   8,
  1389  			count:   4,
  1390  		},
  1391  		// unavailable block
  1392  		{
  1393  			results: []*types.Body{blocks[18].Body(), blocks[19].Body()},
  1394  			start:   19,
  1395  			count:   3,
  1396  		},
  1397  		// unavailable block
  1398  		{
  1399  			results: []*types.Body{blocks[19].Body()},
  1400  			start:   20,
  1401  			count:   2,
  1402  		},
  1403  		{
  1404  			results: []*types.Body{blocks[19].Body()},
  1405  			start:   20,
  1406  			count:   1,
  1407  		},
  1408  		// whole range unavailable
  1409  		{
  1410  			results: make([]*types.Body, 0),
  1411  			start:   22,
  1412  			count:   2,
  1413  		},
  1414  		// allBlocks
  1415  		{
  1416  			results: allBodies(blocks),
  1417  			start:   1,
  1418  			count:   hexutil.Uint64(len(blocks)),
  1419  		},
  1420  	}
  1421  
  1422  	for k, test := range tests {
  1423  		result, err := api.GetPayloadBodiesByRangeV2(test.start, test.count)
  1424  		if err != nil {
  1425  			t.Fatal(err)
  1426  		}
  1427  		if len(result) == len(test.results) {
  1428  			for i, r := range result {
  1429  				if err := checkEqualBody(test.results[i], r); err != nil {
  1430  					t.Fatalf("test %d: invalid response: %v\nexpected %+v\ngot %+v", k, err, test.results[i], r)
  1431  				}
  1432  			}
  1433  		} else {
  1434  			t.Fatalf("test %d: invalid length want %v got %v", k, len(test.results), len(result))
  1435  		}
  1436  	}
  1437  }
  1438  
  1439  func TestGetBlockBodiesByRangeInvalidParams(t *testing.T) {
  1440  	node, eth, _ := setupBodies(t)
  1441  	api := NewConsensusAPI(eth)
  1442  	defer node.Close()
  1443  	tests := []struct {
  1444  		start hexutil.Uint64
  1445  		count hexutil.Uint64
  1446  		want  *engine.EngineAPIError
  1447  	}{
  1448  		// Genesis
  1449  		{
  1450  			start: 0,
  1451  			count: 1,
  1452  			want:  engine.InvalidParams,
  1453  		},
  1454  		// No block requested
  1455  		{
  1456  			start: 1,
  1457  			count: 0,
  1458  			want:  engine.InvalidParams,
  1459  		},
  1460  		// Genesis & no block
  1461  		{
  1462  			start: 0,
  1463  			count: 0,
  1464  			want:  engine.InvalidParams,
  1465  		},
  1466  		// More than 1024 blocks
  1467  		{
  1468  			start: 1,
  1469  			count: 1025,
  1470  			want:  engine.TooLargeRequest,
  1471  		},
  1472  	}
  1473  	for i, tc := range tests {
  1474  		result, err := api.GetPayloadBodiesByRangeV2(tc.start, tc.count)
  1475  		if err == nil {
  1476  			t.Fatalf("test %d: expected error, got %v", i, result)
  1477  		}
  1478  		if have, want := err.Error(), tc.want.Error(); have != want {
  1479  			t.Fatalf("test %d: have %s, want %s", i, have, want)
  1480  		}
  1481  	}
  1482  }
  1483  
  1484  func checkEqualBody(a *types.Body, b *engine.ExecutionPayloadBody) error {
  1485  	if a == nil && b == nil {
  1486  		return nil
  1487  	} else if a == nil || b == nil {
  1488  		return errors.New("nil vs. non-nil")
  1489  	}
  1490  	if len(a.Transactions) != len(b.TransactionData) {
  1491  		return errors.New("transactions length mismatch")
  1492  	}
  1493  	for i, tx := range a.Transactions {
  1494  		data, _ := tx.MarshalBinary()
  1495  		if !bytes.Equal(data, b.TransactionData[i]) {
  1496  			return fmt.Errorf("transaction %d mismatch", i)
  1497  		}
  1498  	}
  1499  	if !reflect.DeepEqual(a.Withdrawals, b.Withdrawals) {
  1500  		return fmt.Errorf("withdrawals mismatch")
  1501  	}
  1502  	return nil
  1503  }
  1504  
  1505  func TestBlockToPayloadWithBlobs(t *testing.T) {
  1506  	header := types.Header{}
  1507  	var txs []*types.Transaction
  1508  
  1509  	inner := types.BlobTx{
  1510  		BlobHashes: make([]common.Hash, 1),
  1511  	}
  1512  
  1513  	txs = append(txs, types.NewTx(&inner))
  1514  	sidecars := []*types.BlobTxSidecar{
  1515  		{
  1516  			Blobs:       make([]kzg4844.Blob, 1),
  1517  			Commitments: make([]kzg4844.Commitment, 1),
  1518  			Proofs:      make([]kzg4844.Proof, 1),
  1519  		},
  1520  	}
  1521  
  1522  	block := types.NewBlock(&header, &types.Body{Transactions: txs}, nil, trie.NewStackTrie(nil))
  1523  	envelope := engine.BlockToExecutableData(block, nil, sidecars, nil)
  1524  	var want int
  1525  	for _, tx := range txs {
  1526  		want += len(tx.BlobHashes())
  1527  	}
  1528  	if got := len(envelope.BlobsBundle.Commitments); got != want {
  1529  		t.Fatalf("invalid number of commitments: got %v, want %v", got, want)
  1530  	}
  1531  	if got := len(envelope.BlobsBundle.Proofs); got != want {
  1532  		t.Fatalf("invalid number of proofs: got %v, want %v", got, want)
  1533  	}
  1534  	if got := len(envelope.BlobsBundle.Blobs); got != want {
  1535  		t.Fatalf("invalid number of blobs: got %v, want %v", got, want)
  1536  	}
  1537  	_, err := engine.ExecutableDataToBlock(*envelope.ExecutionPayload, make([]common.Hash, 1), nil, nil)
  1538  	if err != nil {
  1539  		t.Error(err)
  1540  	}
  1541  }
  1542  
  1543  // This checks that beaconRoot is applied to the state from the engine API.
  1544  func TestParentBeaconBlockRoot(t *testing.T) {
  1545  	//log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(colorable.NewColorableStderr(), log.LevelTrace, true)))
  1546  
  1547  	genesis, blocks := generateMergeChain(10, true)
  1548  
  1549  	// Set cancun time to last block + 5 seconds
  1550  	time := blocks[len(blocks)-1].Time() + 5
  1551  	genesis.Config.ShanghaiTime = &time
  1552  	genesis.Config.CancunTime = &time
  1553  	genesis.Config.BlobScheduleConfig = params.DefaultBlobSchedule
  1554  
  1555  	n, ethservice := startEthService(t, genesis, blocks)
  1556  	defer n.Close()
  1557  
  1558  	api := NewConsensusAPI(ethservice)
  1559  
  1560  	// 11: Build Shanghai block with no withdrawals.
  1561  	parent := ethservice.BlockChain().CurrentHeader()
  1562  	blockParams := engine.PayloadAttributes{
  1563  		Timestamp:   parent.Time + 5,
  1564  		Withdrawals: make([]*types.Withdrawal, 0),
  1565  		BeaconRoot:  &common.Hash{42},
  1566  	}
  1567  	fcState := engine.ForkchoiceStateV1{
  1568  		HeadBlockHash: parent.Hash(),
  1569  	}
  1570  	resp, err := api.ForkchoiceUpdatedV3(fcState, &blockParams)
  1571  	if err != nil {
  1572  		t.Fatalf("error preparing payload, err=%v", err.(*engine.EngineAPIError).ErrorData())
  1573  	}
  1574  	if resp.PayloadStatus.Status != engine.VALID {
  1575  		t.Fatalf("unexpected status (got: %s, want: %s)", resp.PayloadStatus.Status, engine.VALID)
  1576  	}
  1577  
  1578  	// 11: verify state root is the same as parent
  1579  	payloadID := (&miner.BuildPayloadArgs{
  1580  		Parent:       fcState.HeadBlockHash,
  1581  		Timestamp:    blockParams.Timestamp,
  1582  		FeeRecipient: blockParams.SuggestedFeeRecipient,
  1583  		Random:       blockParams.Random,
  1584  		Withdrawals:  blockParams.Withdrawals,
  1585  		BeaconRoot:   blockParams.BeaconRoot,
  1586  		Version:      engine.PayloadV3,
  1587  	}).Id()
  1588  	execData, err := api.GetPayloadV3(payloadID)
  1589  	if err != nil {
  1590  		t.Fatalf("error getting payload, err=%v", err)
  1591  	}
  1592  
  1593  	// 11: verify locally built block
  1594  	if status, err := api.NewPayloadV3(*execData.ExecutionPayload, []common.Hash{}, &common.Hash{42}); err != nil {
  1595  		t.Fatalf("error validating payload: %v", err)
  1596  	} else if status.Status != engine.VALID {
  1597  		t.Fatalf("invalid payload")
  1598  	}
  1599  
  1600  	fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
  1601  	resp, err = api.ForkchoiceUpdatedV3(fcState, nil)
  1602  	if err != nil {
  1603  		t.Fatalf("error preparing payload, err=%v", err.(*engine.EngineAPIError).ErrorData())
  1604  	}
  1605  	if resp.PayloadStatus.Status != engine.VALID {
  1606  		t.Fatalf("unexpected status (got: %s, want: %s)", resp.PayloadStatus.Status, engine.VALID)
  1607  	}
  1608  
  1609  	// 11: verify beacon root was processed.
  1610  	db, _, err := ethservice.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(execData.ExecutionPayload.Number))
  1611  	if err != nil {
  1612  		t.Fatalf("unable to load db: %v", err)
  1613  	}
  1614  	var (
  1615  		timeIdx = common.BigToHash(big.NewInt(int64(execData.ExecutionPayload.Timestamp % 98304)))
  1616  		rootIdx = common.BigToHash(big.NewInt(int64((execData.ExecutionPayload.Timestamp % 98304) + 98304)))
  1617  	)
  1618  
  1619  	if num := db.GetState(params.BeaconRootsAddress, timeIdx); num != timeIdx {
  1620  		t.Fatalf("incorrect number stored: want %s, got %s", timeIdx, num)
  1621  	}
  1622  	if root := db.GetState(params.BeaconRootsAddress, rootIdx); root != *blockParams.BeaconRoot {
  1623  		t.Fatalf("incorrect root stored: want %s, got %s", *blockParams.BeaconRoot, root)
  1624  	}
  1625  }
  1626  
  1627  func TestWitnessCreationAndConsumption(t *testing.T) {
  1628  	//log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(colorable.NewColorableStderr(), log.LevelTrace, true)))
  1629  
  1630  	genesis, blocks := generateMergeChain(10, true)
  1631  
  1632  	// Set cancun time to semi-last block + 5 seconds
  1633  	timestamp := blocks[len(blocks)-2].Time() + 5
  1634  	genesis.Config.ShanghaiTime = &timestamp
  1635  	genesis.Config.CancunTime = &timestamp
  1636  	genesis.Config.BlobScheduleConfig = params.DefaultBlobSchedule
  1637  
  1638  	n, ethservice := startEthService(t, genesis, blocks[:9])
  1639  	defer n.Close()
  1640  
  1641  	api := NewConsensusAPI(ethservice)
  1642  
  1643  	// Put the 10th block's tx in the pool and produce a new block
  1644  	txs := blocks[9].Transactions()
  1645  
  1646  	ethservice.TxPool().Add(txs, true)
  1647  	blockParams := engine.PayloadAttributes{
  1648  		Timestamp:   blocks[8].Time() + 5,
  1649  		Withdrawals: make([]*types.Withdrawal, 0),
  1650  		BeaconRoot:  &common.Hash{42},
  1651  	}
  1652  	fcState := engine.ForkchoiceStateV1{
  1653  		HeadBlockHash:      blocks[8].Hash(),
  1654  		SafeBlockHash:      common.Hash{},
  1655  		FinalizedBlockHash: common.Hash{},
  1656  	}
  1657  	_, err := api.ForkchoiceUpdatedWithWitnessV3(fcState, &blockParams)
  1658  	if err != nil {
  1659  		t.Fatalf("error preparing payload, err=%v", err)
  1660  	}
  1661  	payloadID := (&miner.BuildPayloadArgs{
  1662  		Parent:       fcState.HeadBlockHash,
  1663  		Timestamp:    blockParams.Timestamp,
  1664  		FeeRecipient: blockParams.SuggestedFeeRecipient,
  1665  		Random:       blockParams.Random,
  1666  		Withdrawals:  blockParams.Withdrawals,
  1667  		BeaconRoot:   blockParams.BeaconRoot,
  1668  		Version:      engine.PayloadV3,
  1669  	}).Id()
  1670  	envelope, err := api.getPayload(payloadID, true)
  1671  	if err != nil {
  1672  		t.Fatalf("error getting payload, err=%v", err)
  1673  	}
  1674  	if len(envelope.ExecutionPayload.Transactions) != blocks[9].Transactions().Len() {
  1675  		t.Fatalf("invalid number of transactions %d != %d", len(envelope.ExecutionPayload.Transactions), blocks[9].Transactions().Len())
  1676  	}
  1677  	if envelope.Witness == nil {
  1678  		t.Fatalf("witness missing from payload")
  1679  	}
  1680  	// Test stateless execution of the created witness
  1681  	wantStateRoot := envelope.ExecutionPayload.StateRoot
  1682  	wantReceiptRoot := envelope.ExecutionPayload.ReceiptsRoot
  1683  
  1684  	envelope.ExecutionPayload.StateRoot = common.Hash{}
  1685  	envelope.ExecutionPayload.ReceiptsRoot = common.Hash{}
  1686  
  1687  	res, err := api.ExecuteStatelessPayloadV3(*envelope.ExecutionPayload, []common.Hash{}, &common.Hash{42}, *envelope.Witness)
  1688  	if err != nil {
  1689  		t.Fatalf("error executing stateless payload witness: %v", err)
  1690  	}
  1691  	if res.StateRoot != wantStateRoot {
  1692  		t.Fatalf("stateless state root mismatch: have %v, want %v", res.StateRoot, wantStateRoot)
  1693  	}
  1694  	if res.ReceiptsRoot != wantReceiptRoot {
  1695  		t.Fatalf("stateless receipt root mismatch: have %v, want %v", res.ReceiptsRoot, wantReceiptRoot)
  1696  	}
  1697  	// Test block insertion with witness creation
  1698  	envelope.ExecutionPayload.StateRoot = wantStateRoot
  1699  	envelope.ExecutionPayload.ReceiptsRoot = wantReceiptRoot
  1700  
  1701  	res2, err := api.NewPayloadWithWitnessV3(*envelope.ExecutionPayload, []common.Hash{}, &common.Hash{42})
  1702  	if err != nil {
  1703  		t.Fatalf("error executing stateless payload witness: %v", err)
  1704  	}
  1705  	if res2.Witness == nil {
  1706  		t.Fatalf("witness missing from payload")
  1707  	}
  1708  	// Test stateless execution of the created witness
  1709  	wantStateRoot = envelope.ExecutionPayload.StateRoot
  1710  	wantReceiptRoot = envelope.ExecutionPayload.ReceiptsRoot
  1711  
  1712  	envelope.ExecutionPayload.StateRoot = common.Hash{}
  1713  	envelope.ExecutionPayload.ReceiptsRoot = common.Hash{}
  1714  
  1715  	res, err = api.ExecuteStatelessPayloadV3(*envelope.ExecutionPayload, []common.Hash{}, &common.Hash{42}, *res2.Witness)
  1716  	if err != nil {
  1717  		t.Fatalf("error executing stateless payload witness: %v", err)
  1718  	}
  1719  	if res.StateRoot != wantStateRoot {
  1720  		t.Fatalf("stateless state root mismatch: have %v, want %v", res.StateRoot, wantStateRoot)
  1721  	}
  1722  	if res.ReceiptsRoot != wantReceiptRoot {
  1723  		t.Fatalf("stateless receipt root mismatch: have %v, want %v", res.ReceiptsRoot, wantReceiptRoot)
  1724  	}
  1725  }
  1726  
  1727  // TestGetClientVersion verifies the expected version info is returned.
  1728  func TestGetClientVersion(t *testing.T) {
  1729  	genesis, preMergeBlocks := generateMergeChain(10, false)
  1730  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
  1731  	defer n.Close()
  1732  
  1733  	api := NewConsensusAPI(ethservice)
  1734  	info := engine.ClientVersionV1{
  1735  		Code:    "TT",
  1736  		Name:    "test",
  1737  		Version: "1.1.1",
  1738  		Commit:  "0x12345678",
  1739  	}
  1740  	infos := api.GetClientVersionV1(info)
  1741  	if len(infos) != 1 {
  1742  		t.Fatalf("expected only one returned client version, got %d", len(infos))
  1743  	}
  1744  	info = infos[0]
  1745  	if info.Code != engine.ClientCode || info.Name != engine.ClientName || info.Version != version.WithMeta {
  1746  		t.Fatalf("client info does match expected, got %s", info.String())
  1747  	}
  1748  }
  1749  
  1750  func TestValidateRequests(t *testing.T) {
  1751  	tests := []struct {
  1752  		name     string
  1753  		requests [][]byte
  1754  		wantErr  bool
  1755  	}{
  1756  		{
  1757  			name: "valid ascending",
  1758  			requests: [][]byte{
  1759  				{0x00, 0xAA, 0xBB}, // type 0x00
  1760  				{0x01, 0xCC},       // type 0x01
  1761  				{0x02, 0xDD},       // type 0x02
  1762  			},
  1763  			wantErr: false,
  1764  		},
  1765  		{
  1766  			name: "empty request (too short)",
  1767  			requests: [][]byte{
  1768  				{0x00}, // only 1 byte: type with no data
  1769  			},
  1770  			wantErr: true,
  1771  		},
  1772  		{
  1773  			name: "duplicate type",
  1774  			requests: [][]byte{
  1775  				{0x00, 0x11},
  1776  				{0x01, 0x22},
  1777  				{0x01, 0x33}, // duplicate type 0x01
  1778  			},
  1779  			wantErr: true,
  1780  		},
  1781  		{
  1782  			name: "out of order",
  1783  			requests: [][]byte{
  1784  				{0x01, 0xAA}, // type 0x01
  1785  				{0x00, 0xBB}, // type 0x00 out of order (should be ascending)
  1786  			},
  1787  			wantErr: true,
  1788  		},
  1789  		{
  1790  			name: "single request valid",
  1791  			requests: [][]byte{
  1792  				{0x01, 0xAB},
  1793  			},
  1794  			wantErr: false,
  1795  		},
  1796  	}
  1797  	for _, tt := range tests {
  1798  		t.Run(tt.name, func(t *testing.T) {
  1799  			err := validateRequests(tt.requests)
  1800  			if (err != nil) != tt.wantErr {
  1801  				t.Errorf("validateRequests(%v) error = %v, wantErr = %v",
  1802  					tt.requests, err, tt.wantErr)
  1803  			}
  1804  		})
  1805  	}
  1806  }