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