github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/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  	"fmt"
    22  	"math/big"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/electroneum/electroneum-sc/common"
    27  	"github.com/electroneum/electroneum-sc/common/hexutil"
    28  	"github.com/electroneum/electroneum-sc/consensus/ethash"
    29  	"github.com/electroneum/electroneum-sc/core"
    30  	"github.com/electroneum/electroneum-sc/core/beacon"
    31  	"github.com/electroneum/electroneum-sc/core/rawdb"
    32  	"github.com/electroneum/electroneum-sc/core/types"
    33  	"github.com/electroneum/electroneum-sc/crypto"
    34  	"github.com/electroneum/electroneum-sc/eth"
    35  	"github.com/electroneum/electroneum-sc/eth/downloader"
    36  	"github.com/electroneum/electroneum-sc/eth/ethconfig"
    37  	"github.com/electroneum/electroneum-sc/node"
    38  	"github.com/electroneum/electroneum-sc/p2p"
    39  	"github.com/electroneum/electroneum-sc/params"
    40  	"github.com/electroneum/electroneum-sc/trie"
    41  )
    42  
    43  var (
    44  	// testKey is a private key to use for funding a tester account.
    45  	testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    46  
    47  	// testAddr is the Ethereum address of the tester account.
    48  	testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
    49  
    50  	testBalance = big.NewInt(2e18)
    51  )
    52  
    53  func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) {
    54  	db := rawdb.NewMemoryDatabase()
    55  	config := params.AllEthashProtocolChanges
    56  	genesis := &core.Genesis{
    57  		Config:     config,
    58  		Alloc:      core.GenesisAlloc{testAddr: {Balance: testBalance}},
    59  		ExtraData:  []byte("test genesis"),
    60  		Timestamp:  9000,
    61  		BaseFee:    big.NewInt(params.InitialBaseFee),
    62  		Difficulty: big.NewInt(0),
    63  	}
    64  	testNonce := uint64(0)
    65  	generate := func(i int, g *core.BlockGen) {
    66  		g.OffsetTime(5)
    67  		g.SetExtra([]byte("test"))
    68  		tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(config), testKey)
    69  		g.AddTx(tx)
    70  		testNonce++
    71  	}
    72  	gblock := genesis.ToBlock(db)
    73  	engine := ethash.NewFaker()
    74  	blocks, _ := core.GenerateChain(config, gblock, engine, db, n, generate)
    75  	totalDifficulty := big.NewInt(0)
    76  	for _, b := range blocks {
    77  		totalDifficulty.Add(totalDifficulty, b.Difficulty())
    78  	}
    79  	config.TerminalTotalDifficulty = totalDifficulty
    80  	return genesis, blocks
    81  }
    82  
    83  func TestEth2AssembleBlock(t *testing.T) {
    84  	genesis, blocks := generatePreMergeChain(10)
    85  	n, ethservice := startEthService(t, genesis, blocks)
    86  	defer n.Close()
    87  
    88  	api := NewConsensusAPI(ethservice)
    89  	signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID)
    90  	tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey)
    91  	if err != nil {
    92  		t.Fatalf("error signing transaction, err=%v", err)
    93  	}
    94  	ethservice.TxPool().AddLocal(tx)
    95  	blockParams := beacon.PayloadAttributesV1{
    96  		Timestamp: blocks[9].Time() + 5,
    97  	}
    98  	execData, err := assembleBlock(api, blocks[9].Hash(), &blockParams)
    99  	if err != nil {
   100  		t.Fatalf("error producing block, err=%v", err)
   101  	}
   102  	if len(execData.Transactions) != 1 {
   103  		t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
   104  	}
   105  }
   106  
   107  func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
   108  	genesis, blocks := generatePreMergeChain(10)
   109  	n, ethservice := startEthService(t, genesis, blocks[:9])
   110  	defer n.Close()
   111  
   112  	api := NewConsensusAPI(ethservice)
   113  
   114  	// Put the 10th block's tx in the pool and produce a new block
   115  	api.eth.TxPool().AddRemotesSync(blocks[9].Transactions())
   116  	blockParams := beacon.PayloadAttributesV1{
   117  		Timestamp: blocks[8].Time() + 5,
   118  	}
   119  	execData, err := assembleBlock(api, blocks[8].Hash(), &blockParams)
   120  	if err != nil {
   121  		t.Fatalf("error producing block, err=%v", err)
   122  	}
   123  	if len(execData.Transactions) != blocks[9].Transactions().Len() {
   124  		t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
   125  	}
   126  }
   127  
   128  func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
   129  	genesis, blocks := generatePreMergeChain(10)
   130  	n, ethservice := startEthService(t, genesis, blocks)
   131  	defer n.Close()
   132  
   133  	api := NewConsensusAPI(ethservice)
   134  	fcState := beacon.ForkchoiceStateV1{
   135  		HeadBlockHash:      blocks[5].Hash(),
   136  		SafeBlockHash:      common.Hash{},
   137  		FinalizedBlockHash: common.Hash{},
   138  	}
   139  	if resp, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   140  		t.Errorf("fork choice updated should not error: %v", err)
   141  	} else if resp.PayloadStatus.Status != beacon.INVALID_TERMINAL_BLOCK.Status {
   142  		t.Errorf("fork choice updated before total terminal difficulty should be INVALID")
   143  	}
   144  }
   145  
   146  func TestEth2PrepareAndGetPayload(t *testing.T) {
   147  	genesis, blocks := generatePreMergeChain(10)
   148  	// We need to properly set the terminal total difficulty
   149  	genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty())
   150  	n, ethservice := startEthService(t, genesis, blocks[:9])
   151  	defer n.Close()
   152  
   153  	api := NewConsensusAPI(ethservice)
   154  
   155  	// Put the 10th block's tx in the pool and produce a new block
   156  	ethservice.TxPool().AddLocals(blocks[9].Transactions())
   157  	blockParams := beacon.PayloadAttributesV1{
   158  		Timestamp: blocks[8].Time() + 5,
   159  	}
   160  	fcState := beacon.ForkchoiceStateV1{
   161  		HeadBlockHash:      blocks[8].Hash(),
   162  		SafeBlockHash:      common.Hash{},
   163  		FinalizedBlockHash: common.Hash{},
   164  	}
   165  	_, err := api.ForkchoiceUpdatedV1(fcState, &blockParams)
   166  	if err != nil {
   167  		t.Fatalf("error preparing payload, err=%v", err)
   168  	}
   169  	payloadID := computePayloadId(fcState.HeadBlockHash, &blockParams)
   170  	execData, err := api.GetPayloadV1(payloadID)
   171  	if err != nil {
   172  		t.Fatalf("error getting payload, err=%v", err)
   173  	}
   174  	if len(execData.Transactions) != blocks[9].Transactions().Len() {
   175  		t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
   176  	}
   177  	// Test invalid payloadID
   178  	var invPayload beacon.PayloadID
   179  	copy(invPayload[:], payloadID[:])
   180  	invPayload[0] = ^invPayload[0]
   181  	_, err = api.GetPayloadV1(invPayload)
   182  	if err == nil {
   183  		t.Fatal("expected error retrieving invalid payload")
   184  	}
   185  }
   186  
   187  func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) {
   188  	t.Helper()
   189  
   190  	if len(logsCh) != wantNew {
   191  		t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew)
   192  	}
   193  	if len(rmLogsCh) != wantRemoved {
   194  		t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved)
   195  	}
   196  	// Drain events.
   197  	for i := 0; i < len(logsCh); i++ {
   198  		<-logsCh
   199  	}
   200  	for i := 0; i < len(rmLogsCh); i++ {
   201  		<-rmLogsCh
   202  	}
   203  }
   204  
   205  func TestInvalidPayloadTimestamp(t *testing.T) {
   206  	genesis, preMergeBlocks := generatePreMergeChain(10)
   207  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   208  	ethservice.Merger().ReachTTD()
   209  	defer n.Close()
   210  	var (
   211  		api    = NewConsensusAPI(ethservice)
   212  		parent = ethservice.BlockChain().CurrentBlock()
   213  	)
   214  	tests := []struct {
   215  		time      uint64
   216  		shouldErr bool
   217  	}{
   218  		{0, true},
   219  		{parent.Time(), true},
   220  		{parent.Time() - 1, true},
   221  
   222  		// TODO (MariusVanDerWijden) following tests are currently broken,
   223  		// fixed in upcoming merge-kiln-v2 pr
   224  		//{parent.Time() + 1, false},
   225  		//{uint64(time.Now().Unix()) + uint64(time.Minute), false},
   226  	}
   227  
   228  	for i, test := range tests {
   229  		t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) {
   230  			params := beacon.PayloadAttributesV1{
   231  				Timestamp:             test.time,
   232  				Random:                crypto.Keccak256Hash([]byte{byte(123)}),
   233  				SuggestedFeeRecipient: parent.Coinbase(),
   234  			}
   235  			fcState := beacon.ForkchoiceStateV1{
   236  				HeadBlockHash:      parent.Hash(),
   237  				SafeBlockHash:      common.Hash{},
   238  				FinalizedBlockHash: common.Hash{},
   239  			}
   240  			_, err := api.ForkchoiceUpdatedV1(fcState, &params)
   241  			if test.shouldErr && err == nil {
   242  				t.Fatalf("expected error preparing payload with invalid timestamp, err=%v", err)
   243  			} else if !test.shouldErr && err != nil {
   244  				t.Fatalf("error preparing payload with valid timestamp, err=%v", err)
   245  			}
   246  		})
   247  	}
   248  }
   249  
   250  func TestEth2NewBlock(t *testing.T) {
   251  	genesis, preMergeBlocks := generatePreMergeChain(10)
   252  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   253  	ethservice.Merger().ReachTTD()
   254  	defer n.Close()
   255  
   256  	var (
   257  		api    = NewConsensusAPI(ethservice)
   258  		parent = preMergeBlocks[len(preMergeBlocks)-1]
   259  
   260  		// This EVM code generates a log when the contract is created.
   261  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   262  	)
   263  	// The event channels.
   264  	newLogCh := make(chan []*types.Log, 10)
   265  	rmLogsCh := make(chan core.RemovedLogsEvent, 10)
   266  	ethservice.BlockChain().SubscribeLogsEvent(newLogCh)
   267  	ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh)
   268  
   269  	for i := 0; i < 10; i++ {
   270  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   271  		nonce := statedb.GetNonce(testAddr)
   272  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   273  		ethservice.TxPool().AddLocal(tx)
   274  
   275  		execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{
   276  			Timestamp: parent.Time() + 5,
   277  		})
   278  		if err != nil {
   279  			t.Fatalf("Failed to create the executable data %v", err)
   280  		}
   281  		block, err := beacon.ExecutableDataToBlock(*execData)
   282  		if err != nil {
   283  			t.Fatalf("Failed to convert executable data to block %v", err)
   284  		}
   285  		newResp, err := api.NewPayloadV1(*execData)
   286  		if err != nil || newResp.Status != "VALID" {
   287  			t.Fatalf("Failed to insert block: %v", err)
   288  		}
   289  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 {
   290  			t.Fatalf("Chain head shouldn't be updated")
   291  		}
   292  		checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
   293  		fcState := beacon.ForkchoiceStateV1{
   294  			HeadBlockHash:      block.Hash(),
   295  			SafeBlockHash:      block.Hash(),
   296  			FinalizedBlockHash: block.Hash(),
   297  		}
   298  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   299  			t.Fatalf("Failed to insert block: %v", err)
   300  		}
   301  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   302  			t.Fatalf("Chain head should be updated")
   303  		}
   304  		checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
   305  
   306  		parent = block
   307  	}
   308  
   309  	// Introduce fork chain
   310  	var (
   311  		head = ethservice.BlockChain().CurrentBlock().NumberU64()
   312  	)
   313  	parent = preMergeBlocks[len(preMergeBlocks)-1]
   314  	for i := 0; i < 10; i++ {
   315  		execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{
   316  			Timestamp: parent.Time() + 6,
   317  		})
   318  		if err != nil {
   319  			t.Fatalf("Failed to create the executable data %v", err)
   320  		}
   321  		block, err := beacon.ExecutableDataToBlock(*execData)
   322  		if err != nil {
   323  			t.Fatalf("Failed to convert executable data to block %v", err)
   324  		}
   325  		newResp, err := api.NewPayloadV1(*execData)
   326  		if err != nil || newResp.Status != "VALID" {
   327  			t.Fatalf("Failed to insert block: %v", err)
   328  		}
   329  		if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   330  			t.Fatalf("Chain head shouldn't be updated")
   331  		}
   332  
   333  		fcState := beacon.ForkchoiceStateV1{
   334  			HeadBlockHash:      block.Hash(),
   335  			SafeBlockHash:      block.Hash(),
   336  			FinalizedBlockHash: block.Hash(),
   337  		}
   338  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   339  			t.Fatalf("Failed to insert block: %v", err)
   340  		}
   341  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   342  			t.Fatalf("Chain head should be updated")
   343  		}
   344  		parent, head = block, block.NumberU64()
   345  	}
   346  }
   347  
   348  func TestEth2DeepReorg(t *testing.T) {
   349  	// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
   350  	// before the totalTerminalDifficulty threshold
   351  	/*
   352  		genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2)
   353  		n, ethservice := startEthService(t, genesis, preMergeBlocks)
   354  		defer n.Close()
   355  
   356  		var (
   357  			api    = NewConsensusAPI(ethservice, nil)
   358  			parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1]
   359  			head   = ethservice.BlockChain().CurrentBlock().NumberU64()
   360  		)
   361  		if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) {
   362  			t.Errorf("Block %d not pruned", parent.NumberU64())
   363  		}
   364  		for i := 0; i < 10; i++ {
   365  			execData, err := api.assembleBlock(AssembleBlockParams{
   366  				ParentHash: parent.Hash(),
   367  				Timestamp:  parent.Time() + 5,
   368  			})
   369  			if err != nil {
   370  				t.Fatalf("Failed to create the executable data %v", err)
   371  			}
   372  			block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData)
   373  			if err != nil {
   374  				t.Fatalf("Failed to convert executable data to block %v", err)
   375  			}
   376  			newResp, err := api.ExecutePayload(*execData)
   377  			if err != nil || newResp.Status != "VALID" {
   378  				t.Fatalf("Failed to insert block: %v", err)
   379  			}
   380  			if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   381  				t.Fatalf("Chain head shouldn't be updated")
   382  			}
   383  			if err := api.setHead(block.Hash()); err != nil {
   384  				t.Fatalf("Failed to set head: %v", err)
   385  			}
   386  			if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   387  				t.Fatalf("Chain head should be updated")
   388  			}
   389  			parent, head = block, block.NumberU64()
   390  		}
   391  	*/
   392  }
   393  
   394  // startEthService creates a full node instance for testing.
   395  func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) {
   396  	t.Helper()
   397  
   398  	n, err := node.New(&node.Config{
   399  		P2P: p2p.Config{
   400  			ListenAddr:  "0.0.0.0:0",
   401  			NoDiscovery: true,
   402  			MaxPeers:    25,
   403  		}})
   404  	if err != nil {
   405  		t.Fatal("can't create node:", err)
   406  	}
   407  
   408  	ethcfg := &ethconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, SyncMode: downloader.SnapSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256}
   409  	ethservice, err := eth.New(n, ethcfg)
   410  	if err != nil {
   411  		t.Fatal("can't create eth service:", err)
   412  	}
   413  	if err := n.Start(); err != nil {
   414  		t.Fatal("can't start node:", err)
   415  	}
   416  	if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil {
   417  		n.Close()
   418  		t.Fatal("can't import test blocks:", err)
   419  	}
   420  	time.Sleep(500 * time.Millisecond) // give txpool enough time to consume head event
   421  
   422  	ethservice.SetEtherbase(testAddr)
   423  	ethservice.SetSynced()
   424  	return n, ethservice
   425  }
   426  
   427  func TestFullAPI(t *testing.T) {
   428  	genesis, preMergeBlocks := generatePreMergeChain(10)
   429  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   430  	ethservice.Merger().ReachTTD()
   431  	defer n.Close()
   432  	var (
   433  		parent = ethservice.BlockChain().CurrentBlock()
   434  		// This EVM code generates a log when the contract is created.
   435  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   436  	)
   437  
   438  	callback := func(parent *types.Block) {
   439  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   440  		nonce := statedb.GetNonce(testAddr)
   441  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   442  		ethservice.TxPool().AddLocal(tx)
   443  	}
   444  
   445  	setupBlocks(t, ethservice, 10, parent, callback)
   446  }
   447  
   448  func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Block, callback func(parent *types.Block)) {
   449  	api := NewConsensusAPI(ethservice)
   450  	for i := 0; i < n; i++ {
   451  		callback(parent)
   452  
   453  		payload := getNewPayload(t, api, parent)
   454  
   455  		execResp, err := api.NewPayloadV1(*payload)
   456  		if err != nil {
   457  			t.Fatalf("can't execute payload: %v", err)
   458  		}
   459  		if execResp.Status != beacon.VALID {
   460  			t.Fatalf("invalid status: %v", execResp.Status)
   461  		}
   462  		fcState := beacon.ForkchoiceStateV1{
   463  			HeadBlockHash:      payload.BlockHash,
   464  			SafeBlockHash:      payload.ParentHash,
   465  			FinalizedBlockHash: payload.ParentHash,
   466  		}
   467  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   468  			t.Fatalf("Failed to insert block: %v", err)
   469  		}
   470  		if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
   471  			t.Fatal("Chain head should be updated")
   472  		}
   473  		if ethservice.BlockChain().CurrentFinalizedBlock().NumberU64() != payload.Number-1 {
   474  			t.Fatal("Finalized block should be updated")
   475  		}
   476  		parent = ethservice.BlockChain().CurrentBlock()
   477  	}
   478  }
   479  
   480  func TestExchangeTransitionConfig(t *testing.T) {
   481  	genesis, preMergeBlocks := generatePreMergeChain(10)
   482  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   483  	ethservice.Merger().ReachTTD()
   484  	defer n.Close()
   485  	var (
   486  		api = NewConsensusAPI(ethservice)
   487  	)
   488  	// invalid ttd
   489  	config := beacon.TransitionConfigurationV1{
   490  		TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)),
   491  		TerminalBlockHash:       common.Hash{},
   492  		TerminalBlockNumber:     0,
   493  	}
   494  	if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
   495  		t.Fatal("expected error on invalid config, invalid ttd")
   496  	}
   497  	// invalid terminal block hash
   498  	config = beacon.TransitionConfigurationV1{
   499  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   500  		TerminalBlockHash:       common.Hash{1},
   501  		TerminalBlockNumber:     0,
   502  	}
   503  	if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
   504  		t.Fatal("expected error on invalid config, invalid hash")
   505  	}
   506  	// valid config
   507  	config = beacon.TransitionConfigurationV1{
   508  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   509  		TerminalBlockHash:       common.Hash{},
   510  		TerminalBlockNumber:     0,
   511  	}
   512  	if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
   513  		t.Fatalf("expected no error on valid config, got %v", err)
   514  	}
   515  	// valid config
   516  	config = beacon.TransitionConfigurationV1{
   517  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   518  		TerminalBlockHash:       preMergeBlocks[5].Hash(),
   519  		TerminalBlockNumber:     6,
   520  	}
   521  	if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
   522  		t.Fatalf("expected no error on valid config, got %v", err)
   523  	}
   524  }
   525  
   526  /*
   527  TestNewPayloadOnInvalidChain sets up a valid chain and tries to feed blocks
   528  from an invalid chain to test if latestValidHash (LVH) works correctly.
   529  
   530  We set up the following chain where P1 ... Pn and P1” are valid while
   531  P1' is invalid.
   532  We expect
   533  (1) The LVH to point to the current inserted payload if it was valid.
   534  (2) The LVH to point to the valid parent on an invalid payload (if the parent is available).
   535  (3) If the parent is unavailable, the LVH should not be set.
   536  
   537  	CommonAncestor◄─▲── P1 ◄── P2  ◄─ P3  ◄─ ... ◄─ Pn
   538  	                │
   539  	                └── P1' ◄─ P2' ◄─ P3' ◄─ ... ◄─ Pn'
   540  	                │
   541  	                └── P1''
   542  */
   543  func TestNewPayloadOnInvalidChain(t *testing.T) {
   544  	genesis, preMergeBlocks := generatePreMergeChain(10)
   545  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   546  	ethservice.Merger().ReachTTD()
   547  	defer n.Close()
   548  
   549  	var (
   550  		api    = NewConsensusAPI(ethservice)
   551  		parent = ethservice.BlockChain().CurrentBlock()
   552  		// This EVM code generates a log when the contract is created.
   553  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   554  	)
   555  	for i := 0; i < 10; i++ {
   556  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   557  		nonce := statedb.GetNonce(testAddr)
   558  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   559  		ethservice.TxPool().AddLocal(tx)
   560  
   561  		params := beacon.PayloadAttributesV1{
   562  			Timestamp:             parent.Time() + 1,
   563  			Random:                crypto.Keccak256Hash([]byte{byte(i)}),
   564  			SuggestedFeeRecipient: parent.Coinbase(),
   565  		}
   566  
   567  		fcState := beacon.ForkchoiceStateV1{
   568  			HeadBlockHash:      parent.Hash(),
   569  			SafeBlockHash:      common.Hash{},
   570  			FinalizedBlockHash: common.Hash{},
   571  		}
   572  		resp, err := api.ForkchoiceUpdatedV1(fcState, &params)
   573  		if err != nil {
   574  			t.Fatalf("error preparing payload, err=%v", err)
   575  		}
   576  		if resp.PayloadStatus.Status != beacon.VALID {
   577  			t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status)
   578  		}
   579  		payload, err := api.GetPayloadV1(*resp.PayloadID)
   580  		if err != nil {
   581  			t.Fatalf("can't get payload: %v", err)
   582  		}
   583  		// TODO(493456442, marius) this test can be flaky since we rely on a 100ms
   584  		// allowance for block generation internally.
   585  		if len(payload.Transactions) == 0 {
   586  			t.Fatalf("payload should not be empty")
   587  		}
   588  		execResp, err := api.NewPayloadV1(*payload)
   589  		if err != nil {
   590  			t.Fatalf("can't execute payload: %v", err)
   591  		}
   592  		if execResp.Status != beacon.VALID {
   593  			t.Fatalf("invalid status: %v", execResp.Status)
   594  		}
   595  		fcState = beacon.ForkchoiceStateV1{
   596  			HeadBlockHash:      payload.BlockHash,
   597  			SafeBlockHash:      payload.ParentHash,
   598  			FinalizedBlockHash: payload.ParentHash,
   599  		}
   600  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   601  			t.Fatalf("Failed to insert block: %v", err)
   602  		}
   603  		if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
   604  			t.Fatalf("Chain head should be updated")
   605  		}
   606  		parent = ethservice.BlockChain().CurrentBlock()
   607  	}
   608  }
   609  
   610  func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributesV1) (*beacon.ExecutableDataV1, error) {
   611  	block, err := api.eth.Miner().GetSealingBlockSync(parentHash, params.Timestamp, params.SuggestedFeeRecipient, params.Random, false)
   612  	if err != nil {
   613  		return nil, err
   614  	}
   615  	return beacon.BlockToExecutableData(block), nil
   616  }
   617  
   618  func TestEmptyBlocks(t *testing.T) {
   619  	genesis, preMergeBlocks := generatePreMergeChain(10)
   620  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   621  	ethservice.Merger().ReachTTD()
   622  	defer n.Close()
   623  
   624  	commonAncestor := ethservice.BlockChain().CurrentBlock()
   625  	api := NewConsensusAPI(ethservice)
   626  
   627  	// Setup 10 blocks on the canonical chain
   628  	setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Block) {})
   629  
   630  	// (1) check LatestValidHash by sending a normal payload (P1'')
   631  	payload := getNewPayload(t, api, commonAncestor)
   632  
   633  	status, err := api.NewPayloadV1(*payload)
   634  	if err != nil {
   635  		t.Fatal(err)
   636  	}
   637  	if status.Status != beacon.VALID {
   638  		t.Errorf("invalid status: expected VALID got: %v", status.Status)
   639  	}
   640  	if !bytes.Equal(status.LatestValidHash[:], payload.BlockHash[:]) {
   641  		t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, payload.BlockHash)
   642  	}
   643  
   644  	// (2) Now send P1' which is invalid
   645  	payload = getNewPayload(t, api, commonAncestor)
   646  	payload.GasUsed += 1
   647  	payload = setBlockhash(payload)
   648  	// Now latestValidHash should be the common ancestor
   649  	status, err = api.NewPayloadV1(*payload)
   650  	if err != nil {
   651  		t.Fatal(err)
   652  	}
   653  	if status.Status != beacon.INVALID {
   654  		t.Errorf("invalid status: expected INVALID got: %v", status.Status)
   655  	}
   656  	expected := commonAncestor.Hash()
   657  	if !bytes.Equal(status.LatestValidHash[:], expected[:]) {
   658  		t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, expected)
   659  	}
   660  
   661  	// (3) Now send a payload with unknown parent
   662  	payload = getNewPayload(t, api, commonAncestor)
   663  	payload.ParentHash = common.Hash{1}
   664  	payload = setBlockhash(payload)
   665  	// Now latestValidHash should be the common ancestor
   666  	status, err = api.NewPayloadV1(*payload)
   667  	if err != nil {
   668  		t.Fatal(err)
   669  	}
   670  	if status.Status != beacon.ACCEPTED {
   671  		t.Errorf("invalid status: expected ACCEPTED got: %v", status.Status)
   672  	}
   673  	if status.LatestValidHash != nil {
   674  		t.Fatalf("invalid LVH: got %v wanted nil", status.LatestValidHash)
   675  	}
   676  }
   677  
   678  func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableDataV1 {
   679  	params := beacon.PayloadAttributesV1{
   680  		Timestamp:             parent.Time() + 1,
   681  		Random:                crypto.Keccak256Hash([]byte{byte(1)}),
   682  		SuggestedFeeRecipient: parent.Coinbase(),
   683  	}
   684  
   685  	payload, err := assembleBlock(api, parent.Hash(), &params)
   686  	if err != nil {
   687  		t.Fatal(err)
   688  	}
   689  	return payload
   690  }
   691  
   692  // setBlockhash sets the blockhash of a modified ExecutableData.
   693  // Can be used to make modified payloads look valid.
   694  func setBlockhash(data *beacon.ExecutableDataV1) *beacon.ExecutableDataV1 {
   695  	txs, _ := decodeTransactions(data.Transactions)
   696  	number := big.NewInt(0)
   697  	number.SetUint64(data.Number)
   698  	header := &types.Header{
   699  		ParentHash:  data.ParentHash,
   700  		UncleHash:   types.EmptyUncleHash,
   701  		Coinbase:    data.FeeRecipient,
   702  		Root:        data.StateRoot,
   703  		TxHash:      types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
   704  		ReceiptHash: data.ReceiptsRoot,
   705  		Bloom:       types.BytesToBloom(data.LogsBloom),
   706  		Difficulty:  common.Big0,
   707  		Number:      number,
   708  		GasLimit:    data.GasLimit,
   709  		GasUsed:     data.GasUsed,
   710  		Time:        data.Timestamp,
   711  		BaseFee:     data.BaseFeePerGas,
   712  		Extra:       data.ExtraData,
   713  		MixDigest:   data.Random,
   714  	}
   715  	block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */)
   716  	data.BlockHash = block.Hash()
   717  	return data
   718  }
   719  
   720  func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
   721  	var txs = make([]*types.Transaction, len(enc))
   722  	for i, encTx := range enc {
   723  		var tx types.Transaction
   724  		if err := tx.UnmarshalBinary(encTx); err != nil {
   725  			return nil, fmt.Errorf("invalid transaction %d: %v", i, err)
   726  		}
   727  		txs[i] = &tx
   728  	}
   729  	return txs, nil
   730  }
   731  
   732  func TestTrickRemoteBlockCache(t *testing.T) {
   733  	// Setup two nodes
   734  	genesis, preMergeBlocks := generatePreMergeChain(10)
   735  	nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks)
   736  	nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks)
   737  	ethserviceA.Merger().ReachTTD()
   738  	ethserviceB.Merger().ReachTTD()
   739  	defer nodeA.Close()
   740  	defer nodeB.Close()
   741  	for nodeB.Server().NodeInfo().Ports.Listener == 0 {
   742  		time.Sleep(250 * time.Millisecond)
   743  	}
   744  	nodeA.Server().AddPeer(nodeB.Server().Self())
   745  	nodeB.Server().AddPeer(nodeA.Server().Self())
   746  	apiA := NewConsensusAPI(ethserviceA)
   747  	apiB := NewConsensusAPI(ethserviceB)
   748  
   749  	commonAncestor := ethserviceA.BlockChain().CurrentBlock()
   750  
   751  	// Setup 10 blocks on the canonical chain
   752  	setupBlocks(t, ethserviceA, 10, commonAncestor, func(parent *types.Block) {})
   753  	commonAncestor = ethserviceA.BlockChain().CurrentBlock()
   754  
   755  	var invalidChain []*beacon.ExecutableDataV1
   756  	// create a valid payload (P1)
   757  	//payload1 := getNewPayload(t, apiA, commonAncestor)
   758  	//invalidChain = append(invalidChain, payload1)
   759  
   760  	// create an invalid payload2 (P2)
   761  	payload2 := getNewPayload(t, apiA, commonAncestor)
   762  	//payload2.ParentHash = payload1.BlockHash
   763  	payload2.GasUsed += 1
   764  	payload2 = setBlockhash(payload2)
   765  	invalidChain = append(invalidChain, payload2)
   766  
   767  	head := payload2
   768  	// create some valid payloads on top
   769  	for i := 0; i < 10; i++ {
   770  		payload := getNewPayload(t, apiA, commonAncestor)
   771  		payload.ParentHash = head.BlockHash
   772  		payload = setBlockhash(payload)
   773  		invalidChain = append(invalidChain, payload)
   774  		head = payload
   775  	}
   776  
   777  	// feed the payloads to node B
   778  	for _, payload := range invalidChain {
   779  		status, err := apiB.NewPayloadV1(*payload)
   780  		if err != nil {
   781  			panic(err)
   782  		}
   783  		if status.Status == beacon.INVALID {
   784  			panic("success")
   785  		}
   786  		// Now reorg to the head of the invalid chain
   787  		resp, err := apiB.ForkchoiceUpdatedV1(beacon.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil)
   788  		if err != nil {
   789  			t.Fatal(err)
   790  		}
   791  		if resp.PayloadStatus.Status == beacon.VALID {
   792  			t.Errorf("invalid status: expected INVALID got: %v", resp.PayloadStatus.Status)
   793  		}
   794  		time.Sleep(100 * time.Millisecond)
   795  	}
   796  }