github.com/calmw/ethereum@v0.1.1/eth/catalyst/api_test.go (about)

     1  // Copyright 2021 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package catalyst
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	crand "crypto/rand"
    23  	"fmt"
    24  	"math/big"
    25  	"math/rand"
    26  	"reflect"
    27  	"sync"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/calmw/ethereum/beacon/engine"
    32  	"github.com/calmw/ethereum/common"
    33  	"github.com/calmw/ethereum/common/hexutil"
    34  	"github.com/calmw/ethereum/consensus"
    35  	beaconConsensus "github.com/calmw/ethereum/consensus/beacon"
    36  	"github.com/calmw/ethereum/consensus/ethash"
    37  	"github.com/calmw/ethereum/core"
    38  	"github.com/calmw/ethereum/core/types"
    39  	"github.com/calmw/ethereum/crypto"
    40  	"github.com/calmw/ethereum/eth"
    41  	"github.com/calmw/ethereum/eth/downloader"
    42  	"github.com/calmw/ethereum/eth/ethconfig"
    43  	"github.com/calmw/ethereum/miner"
    44  	"github.com/calmw/ethereum/node"
    45  	"github.com/calmw/ethereum/p2p"
    46  	"github.com/calmw/ethereum/params"
    47  	"github.com/calmw/ethereum/rpc"
    48  	"github.com/calmw/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, 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  	api := NewConsensusAPI(ethservice)
   883  
   884  	// Test parent already post TTD in FCU
   885  	parent := preMergeBlocks[len(preMergeBlocks)-2]
   886  	fcState := engine.ForkchoiceStateV1{
   887  		HeadBlockHash:      parent.Hash(),
   888  		SafeBlockHash:      common.Hash{},
   889  		FinalizedBlockHash: common.Hash{},
   890  	}
   891  	resp, err := api.ForkchoiceUpdatedV1(fcState, nil)
   892  	if err != nil {
   893  		t.Fatalf("error sending forkchoice, err=%v", err)
   894  	}
   895  	if resp.PayloadStatus != engine.INVALID_TERMINAL_BLOCK {
   896  		t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
   897  	}
   898  
   899  	// Test parent already post TTD in NewPayload
   900  	args := &miner.BuildPayloadArgs{
   901  		Parent:       parent.Hash(),
   902  		Timestamp:    parent.Time() + 1,
   903  		Random:       crypto.Keccak256Hash([]byte{byte(1)}),
   904  		FeeRecipient: parent.Coinbase(),
   905  	}
   906  	payload, err := api.eth.Miner().BuildPayload(args)
   907  	if err != nil {
   908  		t.Fatalf("error preparing payload, err=%v", err)
   909  	}
   910  	data := *payload.Resolve().ExecutionPayload
   911  	// We need to recompute the blockhash, since the miner computes a wrong (correct) blockhash
   912  	txs, _ := decodeTransactions(data.Transactions)
   913  	header := &types.Header{
   914  		ParentHash:  data.ParentHash,
   915  		UncleHash:   types.EmptyUncleHash,
   916  		Coinbase:    data.FeeRecipient,
   917  		Root:        data.StateRoot,
   918  		TxHash:      types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
   919  		ReceiptHash: data.ReceiptsRoot,
   920  		Bloom:       types.BytesToBloom(data.LogsBloom),
   921  		Difficulty:  common.Big0,
   922  		Number:      new(big.Int).SetUint64(data.Number),
   923  		GasLimit:    data.GasLimit,
   924  		GasUsed:     data.GasUsed,
   925  		Time:        data.Timestamp,
   926  		BaseFee:     data.BaseFeePerGas,
   927  		Extra:       data.ExtraData,
   928  		MixDigest:   data.Random,
   929  	}
   930  	block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */)
   931  	data.BlockHash = block.Hash()
   932  	// Send the new payload
   933  	resp2, err := api.NewPayloadV1(data)
   934  	if err != nil {
   935  		t.Fatalf("error sending NewPayload, err=%v", err)
   936  	}
   937  	if resp2 != engine.INVALID_TERMINAL_BLOCK {
   938  		t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
   939  	}
   940  }
   941  
   942  // TestSimultaneousNewBlock does several parallel inserts, both as
   943  // newPayLoad and forkchoiceUpdate. This is to test that the api behaves
   944  // well even of the caller is not being 'serial'.
   945  func TestSimultaneousNewBlock(t *testing.T) {
   946  	genesis, preMergeBlocks := generateMergeChain(10, false)
   947  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   948  	defer n.Close()
   949  
   950  	var (
   951  		api    = NewConsensusAPI(ethservice)
   952  		parent = preMergeBlocks[len(preMergeBlocks)-1]
   953  	)
   954  	for i := 0; i < 10; i++ {
   955  		execData, err := assembleBlock(api, parent.Hash(), &engine.PayloadAttributes{
   956  			Timestamp: parent.Time() + 5,
   957  		})
   958  		if err != nil {
   959  			t.Fatalf("Failed to create the executable data %v", err)
   960  		}
   961  		// Insert it 10 times in parallel. Should be ignored.
   962  		{
   963  			var (
   964  				wg      sync.WaitGroup
   965  				testErr error
   966  				errMu   sync.Mutex
   967  			)
   968  			wg.Add(10)
   969  			for ii := 0; ii < 10; ii++ {
   970  				go func() {
   971  					defer wg.Done()
   972  					if newResp, err := api.NewPayloadV1(*execData); err != nil {
   973  						errMu.Lock()
   974  						testErr = fmt.Errorf("Failed to insert block: %w", err)
   975  						errMu.Unlock()
   976  					} else if newResp.Status != "VALID" {
   977  						errMu.Lock()
   978  						testErr = fmt.Errorf("Failed to insert block: %v", newResp.Status)
   979  						errMu.Unlock()
   980  					}
   981  				}()
   982  			}
   983  			wg.Wait()
   984  			if testErr != nil {
   985  				t.Fatal(testErr)
   986  			}
   987  		}
   988  		block, err := engine.ExecutableDataToBlock(*execData)
   989  		if err != nil {
   990  			t.Fatalf("Failed to convert executable data to block %v", err)
   991  		}
   992  		if ethservice.BlockChain().CurrentBlock().Number.Uint64() != block.NumberU64()-1 {
   993  			t.Fatalf("Chain head shouldn't be updated")
   994  		}
   995  		fcState := engine.ForkchoiceStateV1{
   996  			HeadBlockHash:      block.Hash(),
   997  			SafeBlockHash:      block.Hash(),
   998  			FinalizedBlockHash: block.Hash(),
   999  		}
  1000  		{
  1001  			var (
  1002  				wg      sync.WaitGroup
  1003  				testErr error
  1004  				errMu   sync.Mutex
  1005  			)
  1006  			wg.Add(10)
  1007  			// Do each FCU 10 times
  1008  			for ii := 0; ii < 10; ii++ {
  1009  				go func() {
  1010  					defer wg.Done()
  1011  					if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
  1012  						errMu.Lock()
  1013  						testErr = fmt.Errorf("Failed to insert block: %w", err)
  1014  						errMu.Unlock()
  1015  					}
  1016  				}()
  1017  			}
  1018  			wg.Wait()
  1019  			if testErr != nil {
  1020  				t.Fatal(testErr)
  1021  			}
  1022  		}
  1023  		if have, want := ethservice.BlockChain().CurrentBlock().Number.Uint64(), block.NumberU64(); have != want {
  1024  			t.Fatalf("Chain head should be updated, have %d want %d", have, want)
  1025  		}
  1026  		parent = block
  1027  	}
  1028  }
  1029  
  1030  // TestWithdrawals creates and verifies two post-Shanghai blocks. The first
  1031  // includes zero withdrawals and the second includes two.
  1032  func TestWithdrawals(t *testing.T) {
  1033  	genesis, blocks := generateMergeChain(10, true)
  1034  	// Set shanghai time to last block + 5 seconds (first post-merge block)
  1035  	time := blocks[len(blocks)-1].Time() + 5
  1036  	genesis.Config.ShanghaiTime = &time
  1037  
  1038  	n, ethservice := startEthService(t, genesis, blocks)
  1039  	ethservice.Merger().ReachTTD()
  1040  	defer n.Close()
  1041  
  1042  	api := NewConsensusAPI(ethservice)
  1043  
  1044  	// 10: Build Shanghai block with no withdrawals.
  1045  	parent := ethservice.BlockChain().CurrentHeader()
  1046  	blockParams := engine.PayloadAttributes{
  1047  		Timestamp:   parent.Time + 5,
  1048  		Withdrawals: make([]*types.Withdrawal, 0),
  1049  	}
  1050  	fcState := engine.ForkchoiceStateV1{
  1051  		HeadBlockHash: parent.Hash(),
  1052  	}
  1053  	resp, err := api.ForkchoiceUpdatedV2(fcState, &blockParams)
  1054  	if err != nil {
  1055  		t.Fatalf("error preparing payload, err=%v", err)
  1056  	}
  1057  	if resp.PayloadStatus.Status != engine.VALID {
  1058  		t.Fatalf("unexpected status (got: %s, want: %s)", resp.PayloadStatus.Status, engine.VALID)
  1059  	}
  1060  
  1061  	// 10: verify state root is the same as parent
  1062  	payloadID := (&miner.BuildPayloadArgs{
  1063  		Parent:       fcState.HeadBlockHash,
  1064  		Timestamp:    blockParams.Timestamp,
  1065  		FeeRecipient: blockParams.SuggestedFeeRecipient,
  1066  		Random:       blockParams.Random,
  1067  		Withdrawals:  blockParams.Withdrawals,
  1068  	}).Id()
  1069  	execData, err := api.GetPayloadV2(payloadID)
  1070  	if err != nil {
  1071  		t.Fatalf("error getting payload, err=%v", err)
  1072  	}
  1073  	if execData.ExecutionPayload.StateRoot != parent.Root {
  1074  		t.Fatalf("mismatch state roots (got: %s, want: %s)", execData.ExecutionPayload.StateRoot, blocks[8].Root())
  1075  	}
  1076  
  1077  	// 10: verify locally built block
  1078  	if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
  1079  		t.Fatalf("error validating payload: %v", err)
  1080  	} else if status.Status != engine.VALID {
  1081  		t.Fatalf("invalid payload")
  1082  	}
  1083  
  1084  	// 11: build shanghai block with withdrawal
  1085  	aa := common.Address{0xaa}
  1086  	bb := common.Address{0xbb}
  1087  	blockParams = engine.PayloadAttributes{
  1088  		Timestamp: execData.ExecutionPayload.Timestamp + 5,
  1089  		Withdrawals: []*types.Withdrawal{
  1090  			{
  1091  				Index:   0,
  1092  				Address: aa,
  1093  				Amount:  32,
  1094  			},
  1095  			{
  1096  				Index:   1,
  1097  				Address: bb,
  1098  				Amount:  33,
  1099  			},
  1100  		},
  1101  	}
  1102  	fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
  1103  	_, err = api.ForkchoiceUpdatedV2(fcState, &blockParams)
  1104  	if err != nil {
  1105  		t.Fatalf("error preparing payload, err=%v", err)
  1106  	}
  1107  
  1108  	// 11: verify locally build block.
  1109  	payloadID = (&miner.BuildPayloadArgs{
  1110  		Parent:       fcState.HeadBlockHash,
  1111  		Timestamp:    blockParams.Timestamp,
  1112  		FeeRecipient: blockParams.SuggestedFeeRecipient,
  1113  		Random:       blockParams.Random,
  1114  		Withdrawals:  blockParams.Withdrawals,
  1115  	}).Id()
  1116  	execData, err = api.GetPayloadV2(payloadID)
  1117  	if err != nil {
  1118  		t.Fatalf("error getting payload, err=%v", err)
  1119  	}
  1120  	if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
  1121  		t.Fatalf("error validating payload: %v", err)
  1122  	} else if status.Status != engine.VALID {
  1123  		t.Fatalf("invalid payload")
  1124  	}
  1125  
  1126  	// 11: set block as head.
  1127  	fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
  1128  	_, err = api.ForkchoiceUpdatedV2(fcState, nil)
  1129  	if err != nil {
  1130  		t.Fatalf("error preparing payload, err=%v", err)
  1131  	}
  1132  
  1133  	// 11: verify withdrawals were processed.
  1134  	db, _, err := ethservice.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(execData.ExecutionPayload.Number))
  1135  	if err != nil {
  1136  		t.Fatalf("unable to load db: %v", err)
  1137  	}
  1138  	for i, w := range blockParams.Withdrawals {
  1139  		// w.Amount is in gwei, balance in wei
  1140  		if db.GetBalance(w.Address).Uint64() != w.Amount*params.GWei {
  1141  			t.Fatalf("failed to process withdrawal %d", i)
  1142  		}
  1143  	}
  1144  }
  1145  
  1146  func TestNilWithdrawals(t *testing.T) {
  1147  	genesis, blocks := generateMergeChain(10, true)
  1148  	// Set shanghai time to last block + 4 seconds (first post-merge block)
  1149  	time := blocks[len(blocks)-1].Time() + 4
  1150  	genesis.Config.ShanghaiTime = &time
  1151  
  1152  	n, ethservice := startEthService(t, genesis, blocks)
  1153  	ethservice.Merger().ReachTTD()
  1154  	defer n.Close()
  1155  
  1156  	api := NewConsensusAPI(ethservice)
  1157  	parent := ethservice.BlockChain().CurrentHeader()
  1158  	aa := common.Address{0xaa}
  1159  
  1160  	type test struct {
  1161  		blockParams engine.PayloadAttributes
  1162  		wantErr     bool
  1163  	}
  1164  	tests := []test{
  1165  		// Before Shanghai
  1166  		{
  1167  			blockParams: engine.PayloadAttributes{
  1168  				Timestamp:   parent.Time + 2,
  1169  				Withdrawals: nil,
  1170  			},
  1171  			wantErr: false,
  1172  		},
  1173  		{
  1174  			blockParams: engine.PayloadAttributes{
  1175  				Timestamp:   parent.Time + 2,
  1176  				Withdrawals: make([]*types.Withdrawal, 0),
  1177  			},
  1178  			wantErr: true,
  1179  		},
  1180  		{
  1181  			blockParams: engine.PayloadAttributes{
  1182  				Timestamp: parent.Time + 2,
  1183  				Withdrawals: []*types.Withdrawal{
  1184  					{
  1185  						Index:   0,
  1186  						Address: aa,
  1187  						Amount:  32,
  1188  					},
  1189  				},
  1190  			},
  1191  			wantErr: true,
  1192  		},
  1193  		// After Shanghai
  1194  		{
  1195  			blockParams: engine.PayloadAttributes{
  1196  				Timestamp:   parent.Time + 5,
  1197  				Withdrawals: nil,
  1198  			},
  1199  			wantErr: true,
  1200  		},
  1201  		{
  1202  			blockParams: engine.PayloadAttributes{
  1203  				Timestamp:   parent.Time + 5,
  1204  				Withdrawals: make([]*types.Withdrawal, 0),
  1205  			},
  1206  			wantErr: false,
  1207  		},
  1208  		{
  1209  			blockParams: engine.PayloadAttributes{
  1210  				Timestamp: parent.Time + 5,
  1211  				Withdrawals: []*types.Withdrawal{
  1212  					{
  1213  						Index:   0,
  1214  						Address: aa,
  1215  						Amount:  32,
  1216  					},
  1217  				},
  1218  			},
  1219  			wantErr: false,
  1220  		},
  1221  	}
  1222  
  1223  	fcState := engine.ForkchoiceStateV1{
  1224  		HeadBlockHash: parent.Hash(),
  1225  	}
  1226  
  1227  	for _, test := range tests {
  1228  		_, err := api.ForkchoiceUpdatedV2(fcState, &test.blockParams)
  1229  		if test.wantErr {
  1230  			if err == nil {
  1231  				t.Fatal("wanted error on fcuv2 with invalid withdrawals")
  1232  			}
  1233  			continue
  1234  		}
  1235  		if err != nil {
  1236  			t.Fatalf("error preparing payload, err=%v", err)
  1237  		}
  1238  
  1239  		// 11: verify locally build block.
  1240  		payloadID := (&miner.BuildPayloadArgs{
  1241  			Parent:       fcState.HeadBlockHash,
  1242  			Timestamp:    test.blockParams.Timestamp,
  1243  			FeeRecipient: test.blockParams.SuggestedFeeRecipient,
  1244  			Random:       test.blockParams.Random,
  1245  		}).Id()
  1246  		execData, err := api.GetPayloadV2(payloadID)
  1247  		if err != nil {
  1248  			t.Fatalf("error getting payload, err=%v", err)
  1249  		}
  1250  		if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
  1251  			t.Fatalf("error validating payload: %v", err)
  1252  		} else if status.Status != engine.VALID {
  1253  			t.Fatalf("invalid payload")
  1254  		}
  1255  	}
  1256  }
  1257  
  1258  func setupBodies(t *testing.T) (*node.Node, *eth.Ethereum, []*types.Block) {
  1259  	genesis, blocks := generateMergeChain(10, true)
  1260  	// enable shanghai on the last block
  1261  	time := blocks[len(blocks)-1].Header().Time + 1
  1262  	genesis.Config.ShanghaiTime = &time
  1263  	n, ethservice := startEthService(t, genesis, blocks)
  1264  
  1265  	var (
  1266  		parent = ethservice.BlockChain().CurrentBlock()
  1267  		// This EVM code generates a log when the contract is created.
  1268  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
  1269  	)
  1270  
  1271  	callback := func(parent *types.Header) {
  1272  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root)
  1273  		nonce := statedb.GetNonce(testAddr)
  1274  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
  1275  		ethservice.TxPool().AddLocal(tx)
  1276  	}
  1277  
  1278  	withdrawals := make([][]*types.Withdrawal, 10)
  1279  	withdrawals[0] = nil // should be filtered out by miner
  1280  	withdrawals[1] = make([]*types.Withdrawal, 0)
  1281  	for i := 2; i < len(withdrawals); i++ {
  1282  		addr := make([]byte, 20)
  1283  		crand.Read(addr)
  1284  		withdrawals[i] = []*types.Withdrawal{
  1285  			{Index: rand.Uint64(), Validator: rand.Uint64(), Amount: rand.Uint64(), Address: common.BytesToAddress(addr)},
  1286  		}
  1287  	}
  1288  
  1289  	postShanghaiHeaders := setupBlocks(t, ethservice, 10, parent, callback, withdrawals)
  1290  	postShanghaiBlocks := make([]*types.Block, len(postShanghaiHeaders))
  1291  	for i, header := range postShanghaiHeaders {
  1292  		postShanghaiBlocks[i] = ethservice.BlockChain().GetBlock(header.Hash(), header.Number.Uint64())
  1293  	}
  1294  	return n, ethservice, append(blocks, postShanghaiBlocks...)
  1295  }
  1296  
  1297  func allHashes(blocks []*types.Block) []common.Hash {
  1298  	var hashes []common.Hash
  1299  	for _, b := range blocks {
  1300  		hashes = append(hashes, b.Hash())
  1301  	}
  1302  	return hashes
  1303  }
  1304  func allBodies(blocks []*types.Block) []*types.Body {
  1305  	var bodies []*types.Body
  1306  	for _, b := range blocks {
  1307  		bodies = append(bodies, b.Body())
  1308  	}
  1309  	return bodies
  1310  }
  1311  
  1312  func TestGetBlockBodiesByHash(t *testing.T) {
  1313  	node, eth, blocks := setupBodies(t)
  1314  	api := NewConsensusAPI(eth)
  1315  	defer node.Close()
  1316  
  1317  	tests := []struct {
  1318  		results []*types.Body
  1319  		hashes  []common.Hash
  1320  	}{
  1321  		// First pow block
  1322  		{
  1323  			results: []*types.Body{eth.BlockChain().GetBlockByNumber(0).Body()},
  1324  			hashes:  []common.Hash{eth.BlockChain().GetBlockByNumber(0).Hash()},
  1325  		},
  1326  		// Last pow block
  1327  		{
  1328  			results: []*types.Body{blocks[9].Body()},
  1329  			hashes:  []common.Hash{blocks[9].Hash()},
  1330  		},
  1331  		// First post-merge block
  1332  		{
  1333  			results: []*types.Body{blocks[10].Body()},
  1334  			hashes:  []common.Hash{blocks[10].Hash()},
  1335  		},
  1336  		// Pre & post merge blocks
  1337  		{
  1338  			results: []*types.Body{blocks[0].Body(), blocks[9].Body(), blocks[14].Body()},
  1339  			hashes:  []common.Hash{blocks[0].Hash(), blocks[9].Hash(), blocks[14].Hash()},
  1340  		},
  1341  		// unavailable block
  1342  		{
  1343  			results: []*types.Body{blocks[0].Body(), nil, blocks[14].Body()},
  1344  			hashes:  []common.Hash{blocks[0].Hash(), {1, 2}, blocks[14].Hash()},
  1345  		},
  1346  		// same block multiple times
  1347  		{
  1348  			results: []*types.Body{blocks[0].Body(), nil, blocks[0].Body(), blocks[0].Body()},
  1349  			hashes:  []common.Hash{blocks[0].Hash(), {1, 2}, blocks[0].Hash(), blocks[0].Hash()},
  1350  		},
  1351  		// all blocks
  1352  		{
  1353  			results: allBodies(blocks),
  1354  			hashes:  allHashes(blocks),
  1355  		},
  1356  	}
  1357  
  1358  	for k, test := range tests {
  1359  		result := api.GetPayloadBodiesByHashV1(test.hashes)
  1360  		for i, r := range result {
  1361  			if !equalBody(test.results[i], r) {
  1362  				t.Fatalf("test %v: invalid response: expected %+v got %+v", k, test.results[i], r)
  1363  			}
  1364  		}
  1365  	}
  1366  }
  1367  
  1368  func TestGetBlockBodiesByRange(t *testing.T) {
  1369  	node, eth, blocks := setupBodies(t)
  1370  	api := NewConsensusAPI(eth)
  1371  	defer node.Close()
  1372  
  1373  	tests := []struct {
  1374  		results []*types.Body
  1375  		start   hexutil.Uint64
  1376  		count   hexutil.Uint64
  1377  	}{
  1378  		{
  1379  			results: []*types.Body{blocks[9].Body()},
  1380  			start:   10,
  1381  			count:   1,
  1382  		},
  1383  		// Genesis
  1384  		{
  1385  			results: []*types.Body{blocks[0].Body()},
  1386  			start:   1,
  1387  			count:   1,
  1388  		},
  1389  		// First post-merge block
  1390  		{
  1391  			results: []*types.Body{blocks[9].Body()},
  1392  			start:   10,
  1393  			count:   1,
  1394  		},
  1395  		// Pre & post merge blocks
  1396  		{
  1397  			results: []*types.Body{blocks[7].Body(), blocks[8].Body(), blocks[9].Body(), blocks[10].Body()},
  1398  			start:   8,
  1399  			count:   4,
  1400  		},
  1401  		// unavailable block
  1402  		{
  1403  			results: []*types.Body{blocks[18].Body(), blocks[19].Body()},
  1404  			start:   19,
  1405  			count:   3,
  1406  		},
  1407  		// unavailable block
  1408  		{
  1409  			results: []*types.Body{blocks[19].Body()},
  1410  			start:   20,
  1411  			count:   2,
  1412  		},
  1413  		{
  1414  			results: []*types.Body{blocks[19].Body()},
  1415  			start:   20,
  1416  			count:   1,
  1417  		},
  1418  		// whole range unavailable
  1419  		{
  1420  			results: make([]*types.Body, 0),
  1421  			start:   22,
  1422  			count:   2,
  1423  		},
  1424  		// allBlocks
  1425  		{
  1426  			results: allBodies(blocks),
  1427  			start:   1,
  1428  			count:   hexutil.Uint64(len(blocks)),
  1429  		},
  1430  	}
  1431  
  1432  	for k, test := range tests {
  1433  		result, err := api.GetPayloadBodiesByRangeV1(test.start, test.count)
  1434  		if err != nil {
  1435  			t.Fatal(err)
  1436  		}
  1437  		if len(result) == len(test.results) {
  1438  			for i, r := range result {
  1439  				if !equalBody(test.results[i], r) {
  1440  					t.Fatalf("test %d: invalid response: expected \n%+v\ngot\n%+v", k, test.results[i], r)
  1441  				}
  1442  			}
  1443  		} else {
  1444  			t.Fatalf("test %d: invalid length want %v got %v", k, len(test.results), len(result))
  1445  		}
  1446  	}
  1447  }
  1448  
  1449  func TestGetBlockBodiesByRangeInvalidParams(t *testing.T) {
  1450  	node, eth, _ := setupBodies(t)
  1451  	api := NewConsensusAPI(eth)
  1452  	defer node.Close()
  1453  	tests := []struct {
  1454  		start hexutil.Uint64
  1455  		count hexutil.Uint64
  1456  		want  *engine.EngineAPIError
  1457  	}{
  1458  		// Genesis
  1459  		{
  1460  			start: 0,
  1461  			count: 1,
  1462  			want:  engine.InvalidParams,
  1463  		},
  1464  		// No block requested
  1465  		{
  1466  			start: 1,
  1467  			count: 0,
  1468  			want:  engine.InvalidParams,
  1469  		},
  1470  		// Genesis & no block
  1471  		{
  1472  			start: 0,
  1473  			count: 0,
  1474  			want:  engine.InvalidParams,
  1475  		},
  1476  		// More than 1024 blocks
  1477  		{
  1478  			start: 1,
  1479  			count: 1025,
  1480  			want:  engine.TooLargeRequest,
  1481  		},
  1482  	}
  1483  	for i, tc := range tests {
  1484  		result, err := api.GetPayloadBodiesByRangeV1(tc.start, tc.count)
  1485  		if err == nil {
  1486  			t.Fatalf("test %d: expected error, got %v", i, result)
  1487  		}
  1488  		if have, want := err.Error(), tc.want.Error(); have != want {
  1489  			t.Fatalf("test %d: have %s, want %s", i, have, want)
  1490  		}
  1491  	}
  1492  }
  1493  
  1494  func equalBody(a *types.Body, b *engine.ExecutionPayloadBodyV1) bool {
  1495  	if a == nil && b == nil {
  1496  		return true
  1497  	} else if a == nil || b == nil {
  1498  		return false
  1499  	}
  1500  	if len(a.Transactions) != len(b.TransactionData) {
  1501  		return false
  1502  	}
  1503  	for i, tx := range a.Transactions {
  1504  		data, _ := tx.MarshalBinary()
  1505  		if !bytes.Equal(data, b.TransactionData[i]) {
  1506  			return false
  1507  		}
  1508  	}
  1509  	return reflect.DeepEqual(a.Withdrawals, b.Withdrawals)
  1510  }