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