github.com/MikyChow/arbitrum-go-ethereum@v0.0.0-20230306102812-078da49636de/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/MikyChow/arbitrum-go-ethereum/common"
    27  	"github.com/MikyChow/arbitrum-go-ethereum/common/hexutil"
    28  	"github.com/MikyChow/arbitrum-go-ethereum/consensus/ethash"
    29  	"github.com/MikyChow/arbitrum-go-ethereum/core"
    30  	"github.com/MikyChow/arbitrum-go-ethereum/core/beacon"
    31  	"github.com/MikyChow/arbitrum-go-ethereum/core/rawdb"
    32  	"github.com/MikyChow/arbitrum-go-ethereum/core/types"
    33  	"github.com/MikyChow/arbitrum-go-ethereum/crypto"
    34  	"github.com/MikyChow/arbitrum-go-ethereum/eth"
    35  	"github.com/MikyChow/arbitrum-go-ethereum/eth/downloader"
    36  	"github.com/MikyChow/arbitrum-go-ethereum/eth/ethconfig"
    37  	"github.com/MikyChow/arbitrum-go-ethereum/node"
    38  	"github.com/MikyChow/arbitrum-go-ethereum/p2p"
    39  	"github.com/MikyChow/arbitrum-go-ethereum/params"
    40  	"github.com/MikyChow/arbitrum-go-ethereum/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.MustCommit(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  	defer n.Close()
   209  	var (
   210  		api    = NewConsensusAPI(ethservice)
   211  		parent = ethservice.BlockChain().CurrentBlock()
   212  	)
   213  	tests := []struct {
   214  		time      uint64
   215  		shouldErr bool
   216  	}{
   217  		{0, true},
   218  		{parent.Time(), true},
   219  		{parent.Time() - 1, true},
   220  
   221  		// TODO (MariusVanDerWijden) following tests are currently broken,
   222  		// fixed in upcoming merge-kiln-v2 pr
   223  		//{parent.Time() + 1, false},
   224  		//{uint64(time.Now().Unix()) + uint64(time.Minute), false},
   225  	}
   226  
   227  	for i, test := range tests {
   228  		t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) {
   229  			params := beacon.PayloadAttributesV1{
   230  				Timestamp:             test.time,
   231  				Random:                crypto.Keccak256Hash([]byte{byte(123)}),
   232  				SuggestedFeeRecipient: parent.Coinbase(),
   233  			}
   234  			fcState := beacon.ForkchoiceStateV1{
   235  				HeadBlockHash:      parent.Hash(),
   236  				SafeBlockHash:      common.Hash{},
   237  				FinalizedBlockHash: common.Hash{},
   238  			}
   239  			_, err := api.ForkchoiceUpdatedV1(fcState, &params)
   240  			if test.shouldErr && err == nil {
   241  				t.Fatalf("expected error preparing payload with invalid timestamp, err=%v", err)
   242  			} else if !test.shouldErr && err != nil {
   243  				t.Fatalf("error preparing payload with valid timestamp, err=%v", err)
   244  			}
   245  		})
   246  	}
   247  }
   248  
   249  func TestEth2NewBlock(t *testing.T) {
   250  	genesis, preMergeBlocks := generatePreMergeChain(10)
   251  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   252  	defer n.Close()
   253  
   254  	var (
   255  		api    = NewConsensusAPI(ethservice)
   256  		parent = preMergeBlocks[len(preMergeBlocks)-1]
   257  
   258  		// This EVM code generates a log when the contract is created.
   259  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   260  	)
   261  	// The event channels.
   262  	newLogCh := make(chan []*types.Log, 10)
   263  	rmLogsCh := make(chan core.RemovedLogsEvent, 10)
   264  	ethservice.BlockChain().SubscribeLogsEvent(newLogCh)
   265  	ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh)
   266  
   267  	for i := 0; i < 10; i++ {
   268  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   269  		nonce := statedb.GetNonce(testAddr)
   270  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   271  		ethservice.TxPool().AddLocal(tx)
   272  
   273  		execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{
   274  			Timestamp: parent.Time() + 5,
   275  		})
   276  		if err != nil {
   277  			t.Fatalf("Failed to create the executable data %v", err)
   278  		}
   279  		block, err := beacon.ExecutableDataToBlock(*execData)
   280  		if err != nil {
   281  			t.Fatalf("Failed to convert executable data to block %v", err)
   282  		}
   283  		newResp, err := api.NewPayloadV1(*execData)
   284  		if err != nil || newResp.Status != "VALID" {
   285  			t.Fatalf("Failed to insert block: %v", err)
   286  		}
   287  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 {
   288  			t.Fatalf("Chain head shouldn't be updated")
   289  		}
   290  		checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
   291  		fcState := beacon.ForkchoiceStateV1{
   292  			HeadBlockHash:      block.Hash(),
   293  			SafeBlockHash:      block.Hash(),
   294  			FinalizedBlockHash: block.Hash(),
   295  		}
   296  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   297  			t.Fatalf("Failed to insert block: %v", err)
   298  		}
   299  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   300  			t.Fatalf("Chain head should be updated")
   301  		}
   302  		checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
   303  
   304  		parent = block
   305  	}
   306  
   307  	// Introduce fork chain
   308  	var (
   309  		head = ethservice.BlockChain().CurrentBlock().NumberU64()
   310  	)
   311  	parent = preMergeBlocks[len(preMergeBlocks)-1]
   312  	for i := 0; i < 10; i++ {
   313  		execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{
   314  			Timestamp: parent.Time() + 6,
   315  		})
   316  		if err != nil {
   317  			t.Fatalf("Failed to create the executable data %v", err)
   318  		}
   319  		block, err := beacon.ExecutableDataToBlock(*execData)
   320  		if err != nil {
   321  			t.Fatalf("Failed to convert executable data to block %v", err)
   322  		}
   323  		newResp, err := api.NewPayloadV1(*execData)
   324  		if err != nil || newResp.Status != "VALID" {
   325  			t.Fatalf("Failed to insert block: %v", err)
   326  		}
   327  		if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   328  			t.Fatalf("Chain head shouldn't be updated")
   329  		}
   330  
   331  		fcState := beacon.ForkchoiceStateV1{
   332  			HeadBlockHash:      block.Hash(),
   333  			SafeBlockHash:      block.Hash(),
   334  			FinalizedBlockHash: block.Hash(),
   335  		}
   336  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   337  			t.Fatalf("Failed to insert block: %v", err)
   338  		}
   339  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   340  			t.Fatalf("Chain head should be updated")
   341  		}
   342  		parent, head = block, block.NumberU64()
   343  	}
   344  }
   345  
   346  func TestEth2DeepReorg(t *testing.T) {
   347  	// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
   348  	// before the totalTerminalDifficulty threshold
   349  	/*
   350  		genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2)
   351  		n, ethservice := startEthService(t, genesis, preMergeBlocks)
   352  		defer n.Close()
   353  
   354  		var (
   355  			api    = NewConsensusAPI(ethservice, nil)
   356  			parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1]
   357  			head   = ethservice.BlockChain().CurrentBlock().NumberU64()
   358  		)
   359  		if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) {
   360  			t.Errorf("Block %d not pruned", parent.NumberU64())
   361  		}
   362  		for i := 0; i < 10; i++ {
   363  			execData, err := api.assembleBlock(AssembleBlockParams{
   364  				ParentHash: parent.Hash(),
   365  				Timestamp:  parent.Time() + 5,
   366  			})
   367  			if err != nil {
   368  				t.Fatalf("Failed to create the executable data %v", err)
   369  			}
   370  			block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData)
   371  			if err != nil {
   372  				t.Fatalf("Failed to convert executable data to block %v", err)
   373  			}
   374  			newResp, err := api.ExecutePayload(*execData)
   375  			if err != nil || newResp.Status != "VALID" {
   376  				t.Fatalf("Failed to insert block: %v", err)
   377  			}
   378  			if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   379  				t.Fatalf("Chain head shouldn't be updated")
   380  			}
   381  			if err := api.setHead(block.Hash()); err != nil {
   382  				t.Fatalf("Failed to set head: %v", err)
   383  			}
   384  			if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   385  				t.Fatalf("Chain head should be updated")
   386  			}
   387  			parent, head = block, block.NumberU64()
   388  		}
   389  	*/
   390  }
   391  
   392  // startEthService creates a full node instance for testing.
   393  func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) {
   394  	t.Helper()
   395  
   396  	n, err := node.New(&node.Config{
   397  		P2P: p2p.Config{
   398  			ListenAddr:  "0.0.0.0:0",
   399  			NoDiscovery: true,
   400  			MaxPeers:    25,
   401  		}})
   402  	if err != nil {
   403  		t.Fatal("can't create node:", err)
   404  	}
   405  
   406  	ethcfg := &ethconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, SyncMode: downloader.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256}
   407  	ethservice, err := eth.New(n, ethcfg)
   408  	if err != nil {
   409  		t.Fatal("can't create eth service:", err)
   410  	}
   411  	if err := n.Start(); err != nil {
   412  		t.Fatal("can't start node:", err)
   413  	}
   414  	if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil {
   415  		n.Close()
   416  		t.Fatal("can't import test blocks:", err)
   417  	}
   418  	time.Sleep(500 * time.Millisecond) // give txpool enough time to consume head event
   419  
   420  	ethservice.SetEtherbase(testAddr)
   421  	ethservice.SetSynced()
   422  	return n, ethservice
   423  }
   424  
   425  func TestFullAPI(t *testing.T) {
   426  	genesis, preMergeBlocks := generatePreMergeChain(10)
   427  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   428  	defer n.Close()
   429  	var (
   430  		parent = ethservice.BlockChain().CurrentBlock()
   431  		// This EVM code generates a log when the contract is created.
   432  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   433  	)
   434  
   435  	callback := func(parent *types.Block) {
   436  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   437  		nonce := statedb.GetNonce(testAddr)
   438  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   439  		ethservice.TxPool().AddLocal(tx)
   440  	}
   441  
   442  	setupBlocks(t, ethservice, 10, parent, callback)
   443  }
   444  
   445  func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Block, callback func(parent *types.Block)) {
   446  	api := NewConsensusAPI(ethservice)
   447  	for i := 0; i < n; i++ {
   448  		callback(parent)
   449  
   450  		payload := getNewPayload(t, api, parent)
   451  
   452  		execResp, err := api.NewPayloadV1(*payload)
   453  		if err != nil {
   454  			t.Fatalf("can't execute payload: %v", err)
   455  		}
   456  		if execResp.Status != beacon.VALID {
   457  			t.Fatalf("invalid status: %v", execResp.Status)
   458  		}
   459  		fcState := beacon.ForkchoiceStateV1{
   460  			HeadBlockHash:      payload.BlockHash,
   461  			SafeBlockHash:      payload.ParentHash,
   462  			FinalizedBlockHash: payload.ParentHash,
   463  		}
   464  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   465  			t.Fatalf("Failed to insert block: %v", err)
   466  		}
   467  		if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
   468  			t.Fatal("Chain head should be updated")
   469  		}
   470  		if ethservice.BlockChain().CurrentFinalizedBlock().NumberU64() != payload.Number-1 {
   471  			t.Fatal("Finalized block should be updated")
   472  		}
   473  		parent = ethservice.BlockChain().CurrentBlock()
   474  	}
   475  }
   476  
   477  func TestExchangeTransitionConfig(t *testing.T) {
   478  	genesis, preMergeBlocks := generatePreMergeChain(10)
   479  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   480  	defer n.Close()
   481  	var (
   482  		api = NewConsensusAPI(ethservice)
   483  	)
   484  	// invalid ttd
   485  	config := beacon.TransitionConfigurationV1{
   486  		TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)),
   487  		TerminalBlockHash:       common.Hash{},
   488  		TerminalBlockNumber:     0,
   489  	}
   490  	if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
   491  		t.Fatal("expected error on invalid config, invalid ttd")
   492  	}
   493  	// invalid terminal block hash
   494  	config = beacon.TransitionConfigurationV1{
   495  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   496  		TerminalBlockHash:       common.Hash{1},
   497  		TerminalBlockNumber:     0,
   498  	}
   499  	if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
   500  		t.Fatal("expected error on invalid config, invalid hash")
   501  	}
   502  	// valid config
   503  	config = beacon.TransitionConfigurationV1{
   504  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   505  		TerminalBlockHash:       common.Hash{},
   506  		TerminalBlockNumber:     0,
   507  	}
   508  	if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
   509  		t.Fatalf("expected no error on valid config, got %v", err)
   510  	}
   511  	// valid config
   512  	config = beacon.TransitionConfigurationV1{
   513  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   514  		TerminalBlockHash:       preMergeBlocks[5].Hash(),
   515  		TerminalBlockNumber:     6,
   516  	}
   517  	if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
   518  		t.Fatalf("expected no error on valid config, got %v", err)
   519  	}
   520  }
   521  
   522  /*
   523  TestNewPayloadOnInvalidChain sets up a valid chain and tries to feed blocks
   524  from an invalid chain to test if latestValidHash (LVH) works correctly.
   525  
   526  We set up the following chain where P1 ... Pn and P1” are valid while
   527  P1' is invalid.
   528  We expect
   529  (1) The LVH to point to the current inserted payload if it was valid.
   530  (2) The LVH to point to the valid parent on an invalid payload (if the parent is available).
   531  (3) If the parent is unavailable, the LVH should not be set.
   532  
   533  CommonAncestor◄─▲── P1 ◄── P2  ◄─ P3  ◄─ ... ◄─ Pn
   534  
   535  	│
   536  	└── P1' ◄─ P2' ◄─ P3' ◄─ ... ◄─ Pn'
   537  	│
   538  	└── P1''
   539  */
   540  func TestNewPayloadOnInvalidChain(t *testing.T) {
   541  	genesis, preMergeBlocks := generatePreMergeChain(10)
   542  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   543  	defer n.Close()
   544  
   545  	var (
   546  		api    = NewConsensusAPI(ethservice)
   547  		parent = ethservice.BlockChain().CurrentBlock()
   548  		// This EVM code generates a log when the contract is created.
   549  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   550  	)
   551  	for i := 0; i < 10; i++ {
   552  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   553  		nonce := statedb.GetNonce(testAddr)
   554  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   555  		ethservice.TxPool().AddLocal(tx)
   556  
   557  		params := beacon.PayloadAttributesV1{
   558  			Timestamp:             parent.Time() + 1,
   559  			Random:                crypto.Keccak256Hash([]byte{byte(i)}),
   560  			SuggestedFeeRecipient: parent.Coinbase(),
   561  		}
   562  
   563  		fcState := beacon.ForkchoiceStateV1{
   564  			HeadBlockHash:      parent.Hash(),
   565  			SafeBlockHash:      common.Hash{},
   566  			FinalizedBlockHash: common.Hash{},
   567  		}
   568  		resp, err := api.ForkchoiceUpdatedV1(fcState, &params)
   569  		if err != nil {
   570  			t.Fatalf("error preparing payload, err=%v", err)
   571  		}
   572  		if resp.PayloadStatus.Status != beacon.VALID {
   573  			t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status)
   574  		}
   575  		payload, err := api.GetPayloadV1(*resp.PayloadID)
   576  		if err != nil {
   577  			t.Fatalf("can't get payload: %v", err)
   578  		}
   579  		// TODO(493456442, marius) this test can be flaky since we rely on a 100ms
   580  		// allowance for block generation internally.
   581  		if len(payload.Transactions) == 0 {
   582  			t.Fatalf("payload should not be empty")
   583  		}
   584  		execResp, err := api.NewPayloadV1(*payload)
   585  		if err != nil {
   586  			t.Fatalf("can't execute payload: %v", err)
   587  		}
   588  		if execResp.Status != beacon.VALID {
   589  			t.Fatalf("invalid status: %v", execResp.Status)
   590  		}
   591  		fcState = beacon.ForkchoiceStateV1{
   592  			HeadBlockHash:      payload.BlockHash,
   593  			SafeBlockHash:      payload.ParentHash,
   594  			FinalizedBlockHash: payload.ParentHash,
   595  		}
   596  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   597  			t.Fatalf("Failed to insert block: %v", err)
   598  		}
   599  		if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
   600  			t.Fatalf("Chain head should be updated")
   601  		}
   602  		parent = ethservice.BlockChain().CurrentBlock()
   603  	}
   604  }
   605  
   606  func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributesV1) (*beacon.ExecutableDataV1, error) {
   607  	block, err := api.eth.Miner().GetSealingBlockSync(parentHash, params.Timestamp, params.SuggestedFeeRecipient, params.Random, false)
   608  	if err != nil {
   609  		return nil, err
   610  	}
   611  	return beacon.BlockToExecutableData(block), nil
   612  }
   613  
   614  func TestEmptyBlocks(t *testing.T) {
   615  	genesis, preMergeBlocks := generatePreMergeChain(10)
   616  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   617  	defer n.Close()
   618  
   619  	commonAncestor := ethservice.BlockChain().CurrentBlock()
   620  	api := NewConsensusAPI(ethservice)
   621  
   622  	// Setup 10 blocks on the canonical chain
   623  	setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Block) {})
   624  
   625  	// (1) check LatestValidHash by sending a normal payload (P1'')
   626  	payload := getNewPayload(t, api, commonAncestor)
   627  
   628  	status, err := api.NewPayloadV1(*payload)
   629  	if err != nil {
   630  		t.Fatal(err)
   631  	}
   632  	if status.Status != beacon.VALID {
   633  		t.Errorf("invalid status: expected VALID got: %v", status.Status)
   634  	}
   635  	if !bytes.Equal(status.LatestValidHash[:], payload.BlockHash[:]) {
   636  		t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, payload.BlockHash)
   637  	}
   638  
   639  	// (2) Now send P1' which is invalid
   640  	payload = getNewPayload(t, api, commonAncestor)
   641  	payload.GasUsed += 1
   642  	payload = setBlockhash(payload)
   643  	// Now latestValidHash should be the common ancestor
   644  	status, err = api.NewPayloadV1(*payload)
   645  	if err != nil {
   646  		t.Fatal(err)
   647  	}
   648  	if status.Status != beacon.INVALID {
   649  		t.Errorf("invalid status: expected INVALID got: %v", status.Status)
   650  	}
   651  	// Expect 0x0 on INVALID block on top of PoW block
   652  	expected := common.Hash{}
   653  	if !bytes.Equal(status.LatestValidHash[:], expected[:]) {
   654  		t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, expected)
   655  	}
   656  
   657  	// (3) Now send a payload with unknown parent
   658  	payload = getNewPayload(t, api, commonAncestor)
   659  	payload.ParentHash = common.Hash{1}
   660  	payload = setBlockhash(payload)
   661  	// Now latestValidHash should be the common ancestor
   662  	status, err = api.NewPayloadV1(*payload)
   663  	if err != nil {
   664  		t.Fatal(err)
   665  	}
   666  	if status.Status != beacon.SYNCING {
   667  		t.Errorf("invalid status: expected SYNCING got: %v", status.Status)
   668  	}
   669  	if status.LatestValidHash != nil {
   670  		t.Fatalf("invalid LVH: got %v wanted nil", status.LatestValidHash)
   671  	}
   672  }
   673  
   674  func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableDataV1 {
   675  	params := beacon.PayloadAttributesV1{
   676  		Timestamp:             parent.Time() + 1,
   677  		Random:                crypto.Keccak256Hash([]byte{byte(1)}),
   678  		SuggestedFeeRecipient: parent.Coinbase(),
   679  	}
   680  
   681  	payload, err := assembleBlock(api, parent.Hash(), &params)
   682  	if err != nil {
   683  		t.Fatal(err)
   684  	}
   685  	return payload
   686  }
   687  
   688  // setBlockhash sets the blockhash of a modified ExecutableData.
   689  // Can be used to make modified payloads look valid.
   690  func setBlockhash(data *beacon.ExecutableDataV1) *beacon.ExecutableDataV1 {
   691  	txs, _ := decodeTransactions(data.Transactions)
   692  	number := big.NewInt(0)
   693  	number.SetUint64(data.Number)
   694  	header := &types.Header{
   695  		ParentHash:  data.ParentHash,
   696  		UncleHash:   types.EmptyUncleHash,
   697  		Coinbase:    data.FeeRecipient,
   698  		Root:        data.StateRoot,
   699  		TxHash:      types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
   700  		ReceiptHash: data.ReceiptsRoot,
   701  		Bloom:       types.BytesToBloom(data.LogsBloom),
   702  		Difficulty:  common.Big0,
   703  		Number:      number,
   704  		GasLimit:    data.GasLimit,
   705  		GasUsed:     data.GasUsed,
   706  		Time:        data.Timestamp,
   707  		BaseFee:     data.BaseFeePerGas,
   708  		Extra:       data.ExtraData,
   709  		MixDigest:   data.Random,
   710  	}
   711  	block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */)
   712  	data.BlockHash = block.Hash()
   713  	return data
   714  }
   715  
   716  func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
   717  	var txs = make([]*types.Transaction, len(enc))
   718  	for i, encTx := range enc {
   719  		var tx types.Transaction
   720  		if err := tx.UnmarshalBinary(encTx); err != nil {
   721  			return nil, fmt.Errorf("invalid transaction %d: %v", i, err)
   722  		}
   723  		txs[i] = &tx
   724  	}
   725  	return txs, nil
   726  }
   727  
   728  func TestTrickRemoteBlockCache(t *testing.T) {
   729  	// Setup two nodes
   730  	genesis, preMergeBlocks := generatePreMergeChain(10)
   731  	nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks)
   732  	nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks)
   733  	defer nodeA.Close()
   734  	defer nodeB.Close()
   735  	for nodeB.Server().NodeInfo().Ports.Listener == 0 {
   736  		time.Sleep(250 * time.Millisecond)
   737  	}
   738  	nodeA.Server().AddPeer(nodeB.Server().Self())
   739  	nodeB.Server().AddPeer(nodeA.Server().Self())
   740  	apiA := NewConsensusAPI(ethserviceA)
   741  	apiB := NewConsensusAPI(ethserviceB)
   742  
   743  	commonAncestor := ethserviceA.BlockChain().CurrentBlock()
   744  
   745  	// Setup 10 blocks on the canonical chain
   746  	setupBlocks(t, ethserviceA, 10, commonAncestor, func(parent *types.Block) {})
   747  	commonAncestor = ethserviceA.BlockChain().CurrentBlock()
   748  
   749  	var invalidChain []*beacon.ExecutableDataV1
   750  	// create a valid payload (P1)
   751  	//payload1 := getNewPayload(t, apiA, commonAncestor)
   752  	//invalidChain = append(invalidChain, payload1)
   753  
   754  	// create an invalid payload2 (P2)
   755  	payload2 := getNewPayload(t, apiA, commonAncestor)
   756  	//payload2.ParentHash = payload1.BlockHash
   757  	payload2.GasUsed += 1
   758  	payload2 = setBlockhash(payload2)
   759  	invalidChain = append(invalidChain, payload2)
   760  
   761  	head := payload2
   762  	// create some valid payloads on top
   763  	for i := 0; i < 10; i++ {
   764  		payload := getNewPayload(t, apiA, commonAncestor)
   765  		payload.ParentHash = head.BlockHash
   766  		payload = setBlockhash(payload)
   767  		invalidChain = append(invalidChain, payload)
   768  		head = payload
   769  	}
   770  
   771  	// feed the payloads to node B
   772  	for _, payload := range invalidChain {
   773  		status, err := apiB.NewPayloadV1(*payload)
   774  		if err != nil {
   775  			panic(err)
   776  		}
   777  		if status.Status == beacon.VALID {
   778  			t.Error("invalid status: VALID on an invalid chain")
   779  		}
   780  		// Now reorg to the head of the invalid chain
   781  		resp, err := apiB.ForkchoiceUpdatedV1(beacon.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil)
   782  		if err != nil {
   783  			t.Fatal(err)
   784  		}
   785  		if resp.PayloadStatus.Status == beacon.VALID {
   786  			t.Error("invalid status: VALID on an invalid chain")
   787  		}
   788  		time.Sleep(100 * time.Millisecond)
   789  	}
   790  }
   791  
   792  func TestInvalidBloom(t *testing.T) {
   793  	genesis, preMergeBlocks := generatePreMergeChain(10)
   794  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   795  	ethservice.Merger().ReachTTD()
   796  	defer n.Close()
   797  
   798  	commonAncestor := ethservice.BlockChain().CurrentBlock()
   799  	api := NewConsensusAPI(ethservice)
   800  
   801  	// Setup 10 blocks on the canonical chain
   802  	setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Block) {})
   803  
   804  	// (1) check LatestValidHash by sending a normal payload (P1'')
   805  	payload := getNewPayload(t, api, commonAncestor)
   806  	payload.LogsBloom = append(payload.LogsBloom, byte(1))
   807  	status, err := api.NewPayloadV1(*payload)
   808  	if err != nil {
   809  		t.Fatal(err)
   810  	}
   811  	if status.Status != beacon.INVALIDBLOCKHASH {
   812  		t.Errorf("invalid status: expected VALID got: %v", status.Status)
   813  	}
   814  }
   815  
   816  func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
   817  	genesis, preMergeBlocks := generatePreMergeChain(100)
   818  	fmt.Println(genesis.Config.TerminalTotalDifficulty)
   819  	genesis.Config.TerminalTotalDifficulty = preMergeBlocks[0].Difficulty() //.Sub(genesis.Config.TerminalTotalDifficulty, preMergeBlocks[len(preMergeBlocks)-1].Difficulty())
   820  
   821  	fmt.Println(genesis.Config.TerminalTotalDifficulty)
   822  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   823  	defer n.Close()
   824  
   825  	var (
   826  		api    = NewConsensusAPI(ethservice)
   827  		parent = preMergeBlocks[len(preMergeBlocks)-1]
   828  	)
   829  
   830  	// Test parent already post TTD in FCU
   831  	fcState := beacon.ForkchoiceStateV1{
   832  		HeadBlockHash:      parent.Hash(),
   833  		SafeBlockHash:      common.Hash{},
   834  		FinalizedBlockHash: common.Hash{},
   835  	}
   836  	resp, err := api.ForkchoiceUpdatedV1(fcState, nil)
   837  	if err != nil {
   838  		t.Fatalf("error sending forkchoice, err=%v", err)
   839  	}
   840  	if resp.PayloadStatus != beacon.INVALID_TERMINAL_BLOCK {
   841  		t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
   842  	}
   843  
   844  	// Test parent already post TTD in NewPayload
   845  	params := beacon.PayloadAttributesV1{
   846  		Timestamp:             parent.Time() + 1,
   847  		Random:                crypto.Keccak256Hash([]byte{byte(1)}),
   848  		SuggestedFeeRecipient: parent.Coinbase(),
   849  	}
   850  	empty, err := api.eth.Miner().GetSealingBlockSync(parent.Hash(), params.Timestamp, params.SuggestedFeeRecipient, params.Random, true)
   851  	if err != nil {
   852  		t.Fatalf("error preparing payload, err=%v", err)
   853  	}
   854  	data := *beacon.BlockToExecutableData(empty)
   855  	resp2, err := api.NewPayloadV1(data)
   856  	if err != nil {
   857  		t.Fatalf("error sending NewPayload, err=%v", err)
   858  	}
   859  	if resp2 != beacon.INVALID_TERMINAL_BLOCK {
   860  		t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
   861  	}
   862  }