github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/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  	"sync"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/tirogen/go-ethereum/common"
    28  	"github.com/tirogen/go-ethereum/common/hexutil"
    29  	"github.com/tirogen/go-ethereum/consensus/ethash"
    30  	"github.com/tirogen/go-ethereum/core"
    31  	"github.com/tirogen/go-ethereum/core/beacon"
    32  	"github.com/tirogen/go-ethereum/core/types"
    33  	"github.com/tirogen/go-ethereum/crypto"
    34  	"github.com/tirogen/go-ethereum/eth"
    35  	"github.com/tirogen/go-ethereum/eth/downloader"
    36  	"github.com/tirogen/go-ethereum/eth/ethconfig"
    37  	"github.com/tirogen/go-ethereum/miner"
    38  	"github.com/tirogen/go-ethereum/node"
    39  	"github.com/tirogen/go-ethereum/p2p"
    40  	"github.com/tirogen/go-ethereum/params"
    41  	"github.com/tirogen/go-ethereum/trie"
    42  )
    43  
    44  var (
    45  	// testKey is a private key to use for funding a tester account.
    46  	testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    47  
    48  	// testAddr is the Ethereum address of the tester account.
    49  	testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
    50  
    51  	testBalance = big.NewInt(2e18)
    52  )
    53  
    54  func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) {
    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  	_, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), n, generate)
    73  	totalDifficulty := big.NewInt(0)
    74  	for _, b := range blocks {
    75  		totalDifficulty.Add(totalDifficulty, b.Difficulty())
    76  	}
    77  	config.TerminalTotalDifficulty = totalDifficulty
    78  	return genesis, blocks
    79  }
    80  
    81  func TestEth2AssembleBlock(t *testing.T) {
    82  	genesis, blocks := generatePreMergeChain(10)
    83  	n, ethservice := startEthService(t, genesis, blocks)
    84  	defer n.Close()
    85  
    86  	api := NewConsensusAPI(ethservice)
    87  	signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID)
    88  	tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey)
    89  	if err != nil {
    90  		t.Fatalf("error signing transaction, err=%v", err)
    91  	}
    92  	ethservice.TxPool().AddLocal(tx)
    93  	blockParams := beacon.PayloadAttributesV1{
    94  		Timestamp: blocks[9].Time() + 5,
    95  	}
    96  	// The miner needs to pick up on the txs in the pool, so a few retries might be
    97  	// needed.
    98  	if _, testErr := assembleWithTransactions(api, blocks[9].Hash(), &blockParams, 1); testErr != nil {
    99  		t.Fatal(testErr)
   100  	}
   101  }
   102  
   103  // assembleWithTransactions tries to assemble a block, retrying until it has 'want',
   104  // number of transactions in it, or it has retried three times.
   105  func assembleWithTransactions(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributesV1, want int) (execData *beacon.ExecutableDataV1, err error) {
   106  	for retries := 3; retries > 0; retries-- {
   107  		execData, err = assembleBlock(api, parentHash, params)
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  		if have, want := len(execData.Transactions), want; have != want {
   112  			err = fmt.Errorf("invalid number of transactions, have %d want %d", have, want)
   113  			continue
   114  		}
   115  		return execData, nil
   116  	}
   117  	return nil, err
   118  }
   119  
   120  func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
   121  	genesis, blocks := generatePreMergeChain(10)
   122  	n, ethservice := startEthService(t, genesis, blocks[:9])
   123  	defer n.Close()
   124  
   125  	api := NewConsensusAPI(ethservice)
   126  
   127  	// Put the 10th block's tx in the pool and produce a new block
   128  	api.eth.TxPool().AddRemotesSync(blocks[9].Transactions())
   129  	blockParams := beacon.PayloadAttributesV1{
   130  		Timestamp: blocks[8].Time() + 5,
   131  	}
   132  	// The miner needs to pick up on the txs in the pool, so a few retries might be
   133  	// needed.
   134  	if _, err := assembleWithTransactions(api, blocks[8].Hash(), &blockParams, blocks[9].Transactions().Len()); err != nil {
   135  		t.Fatal(err)
   136  	}
   137  }
   138  
   139  func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
   140  	genesis, blocks := generatePreMergeChain(10)
   141  	n, ethservice := startEthService(t, genesis, blocks)
   142  	defer n.Close()
   143  
   144  	api := NewConsensusAPI(ethservice)
   145  	fcState := beacon.ForkchoiceStateV1{
   146  		HeadBlockHash:      blocks[5].Hash(),
   147  		SafeBlockHash:      common.Hash{},
   148  		FinalizedBlockHash: common.Hash{},
   149  	}
   150  	if resp, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   151  		t.Errorf("fork choice updated should not error: %v", err)
   152  	} else if resp.PayloadStatus.Status != beacon.INVALID_TERMINAL_BLOCK.Status {
   153  		t.Errorf("fork choice updated before total terminal difficulty should be INVALID")
   154  	}
   155  }
   156  
   157  func TestEth2PrepareAndGetPayload(t *testing.T) {
   158  	genesis, blocks := generatePreMergeChain(10)
   159  	// We need to properly set the terminal total difficulty
   160  	genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty())
   161  	n, ethservice := startEthService(t, genesis, blocks[:9])
   162  	defer n.Close()
   163  
   164  	api := NewConsensusAPI(ethservice)
   165  
   166  	// Put the 10th block's tx in the pool and produce a new block
   167  	ethservice.TxPool().AddLocals(blocks[9].Transactions())
   168  	blockParams := beacon.PayloadAttributesV1{
   169  		Timestamp: blocks[8].Time() + 5,
   170  	}
   171  	fcState := beacon.ForkchoiceStateV1{
   172  		HeadBlockHash:      blocks[8].Hash(),
   173  		SafeBlockHash:      common.Hash{},
   174  		FinalizedBlockHash: common.Hash{},
   175  	}
   176  	_, err := api.ForkchoiceUpdatedV1(fcState, &blockParams)
   177  	if err != nil {
   178  		t.Fatalf("error preparing payload, err=%v", err)
   179  	}
   180  	// give the payload some time to be built
   181  	time.Sleep(100 * time.Millisecond)
   182  	payloadID := (&miner.BuildPayloadArgs{
   183  		Parent:       fcState.HeadBlockHash,
   184  		Timestamp:    blockParams.Timestamp,
   185  		FeeRecipient: blockParams.SuggestedFeeRecipient,
   186  		Random:       blockParams.Random,
   187  	}).Id()
   188  	execData, err := api.GetPayloadV1(payloadID)
   189  	if err != nil {
   190  		t.Fatalf("error getting payload, err=%v", err)
   191  	}
   192  	if len(execData.Transactions) != blocks[9].Transactions().Len() {
   193  		t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
   194  	}
   195  	// Test invalid payloadID
   196  	var invPayload beacon.PayloadID
   197  	copy(invPayload[:], payloadID[:])
   198  	invPayload[0] = ^invPayload[0]
   199  	_, err = api.GetPayloadV1(invPayload)
   200  	if err == nil {
   201  		t.Fatal("expected error retrieving invalid payload")
   202  	}
   203  }
   204  
   205  func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) {
   206  	t.Helper()
   207  
   208  	if len(logsCh) != wantNew {
   209  		t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew)
   210  	}
   211  	if len(rmLogsCh) != wantRemoved {
   212  		t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved)
   213  	}
   214  	// Drain events.
   215  	for i := 0; i < len(logsCh); i++ {
   216  		<-logsCh
   217  	}
   218  	for i := 0; i < len(rmLogsCh); i++ {
   219  		<-rmLogsCh
   220  	}
   221  }
   222  
   223  func TestInvalidPayloadTimestamp(t *testing.T) {
   224  	genesis, preMergeBlocks := generatePreMergeChain(10)
   225  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   226  	defer n.Close()
   227  	var (
   228  		api    = NewConsensusAPI(ethservice)
   229  		parent = ethservice.BlockChain().CurrentBlock()
   230  	)
   231  	tests := []struct {
   232  		time      uint64
   233  		shouldErr bool
   234  	}{
   235  		{0, true},
   236  		{parent.Time(), true},
   237  		{parent.Time() - 1, true},
   238  
   239  		// TODO (MariusVanDerWijden) following tests are currently broken,
   240  		// fixed in upcoming merge-kiln-v2 pr
   241  		//{parent.Time() + 1, false},
   242  		//{uint64(time.Now().Unix()) + uint64(time.Minute), false},
   243  	}
   244  
   245  	for i, test := range tests {
   246  		t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) {
   247  			params := beacon.PayloadAttributesV1{
   248  				Timestamp:             test.time,
   249  				Random:                crypto.Keccak256Hash([]byte{byte(123)}),
   250  				SuggestedFeeRecipient: parent.Coinbase(),
   251  			}
   252  			fcState := beacon.ForkchoiceStateV1{
   253  				HeadBlockHash:      parent.Hash(),
   254  				SafeBlockHash:      common.Hash{},
   255  				FinalizedBlockHash: common.Hash{},
   256  			}
   257  			_, err := api.ForkchoiceUpdatedV1(fcState, &params)
   258  			if test.shouldErr && err == nil {
   259  				t.Fatalf("expected error preparing payload with invalid timestamp, err=%v", err)
   260  			} else if !test.shouldErr && err != nil {
   261  				t.Fatalf("error preparing payload with valid timestamp, err=%v", err)
   262  			}
   263  		})
   264  	}
   265  }
   266  
   267  func TestEth2NewBlock(t *testing.T) {
   268  	genesis, preMergeBlocks := generatePreMergeChain(10)
   269  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   270  	defer n.Close()
   271  
   272  	var (
   273  		api    = NewConsensusAPI(ethservice)
   274  		parent = preMergeBlocks[len(preMergeBlocks)-1]
   275  
   276  		// This EVM code generates a log when the contract is created.
   277  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   278  	)
   279  	// The event channels.
   280  	newLogCh := make(chan []*types.Log, 10)
   281  	rmLogsCh := make(chan core.RemovedLogsEvent, 10)
   282  	ethservice.BlockChain().SubscribeLogsEvent(newLogCh)
   283  	ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh)
   284  
   285  	for i := 0; i < 10; i++ {
   286  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   287  		nonce := statedb.GetNonce(testAddr)
   288  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   289  		ethservice.TxPool().AddLocal(tx)
   290  
   291  		execData, err := assembleWithTransactions(api, parent.Hash(), &beacon.PayloadAttributesV1{
   292  			Timestamp: parent.Time() + 5,
   293  		}, 1)
   294  		if err != nil {
   295  			t.Fatalf("Failed to create the executable data %v", err)
   296  		}
   297  		block, err := beacon.ExecutableDataToBlock(*execData)
   298  		if err != nil {
   299  			t.Fatalf("Failed to convert executable data to block %v", err)
   300  		}
   301  		newResp, err := api.NewPayloadV1(*execData)
   302  		switch {
   303  		case err != nil:
   304  			t.Fatalf("Failed to insert block: %v", err)
   305  		case newResp.Status != "VALID":
   306  			t.Fatalf("Failed to insert block: %v", newResp.Status)
   307  		case ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1:
   308  			t.Fatalf("Chain head shouldn't be updated")
   309  		}
   310  		checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
   311  		fcState := beacon.ForkchoiceStateV1{
   312  			HeadBlockHash:      block.Hash(),
   313  			SafeBlockHash:      block.Hash(),
   314  			FinalizedBlockHash: block.Hash(),
   315  		}
   316  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   317  			t.Fatalf("Failed to insert block: %v", err)
   318  		}
   319  		if have, want := ethservice.BlockChain().CurrentBlock().NumberU64(), block.NumberU64(); have != want {
   320  			t.Fatalf("Chain head should be updated, have %d want %d", have, want)
   321  		}
   322  		checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
   323  
   324  		parent = block
   325  	}
   326  
   327  	// Introduce fork chain
   328  	var (
   329  		head = ethservice.BlockChain().CurrentBlock().NumberU64()
   330  	)
   331  	parent = preMergeBlocks[len(preMergeBlocks)-1]
   332  	for i := 0; i < 10; i++ {
   333  		execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{
   334  			Timestamp: parent.Time() + 6,
   335  		})
   336  		if err != nil {
   337  			t.Fatalf("Failed to create the executable data %v", err)
   338  		}
   339  		block, err := beacon.ExecutableDataToBlock(*execData)
   340  		if err != nil {
   341  			t.Fatalf("Failed to convert executable data to block %v", err)
   342  		}
   343  		newResp, err := api.NewPayloadV1(*execData)
   344  		if err != nil || newResp.Status != "VALID" {
   345  			t.Fatalf("Failed to insert block: %v", err)
   346  		}
   347  		if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   348  			t.Fatalf("Chain head shouldn't be updated")
   349  		}
   350  
   351  		fcState := beacon.ForkchoiceStateV1{
   352  			HeadBlockHash:      block.Hash(),
   353  			SafeBlockHash:      block.Hash(),
   354  			FinalizedBlockHash: block.Hash(),
   355  		}
   356  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   357  			t.Fatalf("Failed to insert block: %v", err)
   358  		}
   359  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   360  			t.Fatalf("Chain head should be updated")
   361  		}
   362  		parent, head = block, block.NumberU64()
   363  	}
   364  }
   365  
   366  func TestEth2DeepReorg(t *testing.T) {
   367  	// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
   368  	// before the totalTerminalDifficulty threshold
   369  	/*
   370  		genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2)
   371  		n, ethservice := startEthService(t, genesis, preMergeBlocks)
   372  		defer n.Close()
   373  
   374  		var (
   375  			api    = NewConsensusAPI(ethservice, nil)
   376  			parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1]
   377  			head   = ethservice.BlockChain().CurrentBlock().NumberU64()
   378  		)
   379  		if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) {
   380  			t.Errorf("Block %d not pruned", parent.NumberU64())
   381  		}
   382  		for i := 0; i < 10; i++ {
   383  			execData, err := api.assembleBlock(AssembleBlockParams{
   384  				ParentHash: parent.Hash(),
   385  				Timestamp:  parent.Time() + 5,
   386  			})
   387  			if err != nil {
   388  				t.Fatalf("Failed to create the executable data %v", err)
   389  			}
   390  			block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData)
   391  			if err != nil {
   392  				t.Fatalf("Failed to convert executable data to block %v", err)
   393  			}
   394  			newResp, err := api.ExecutePayload(*execData)
   395  			if err != nil || newResp.Status != "VALID" {
   396  				t.Fatalf("Failed to insert block: %v", err)
   397  			}
   398  			if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   399  				t.Fatalf("Chain head shouldn't be updated")
   400  			}
   401  			if err := api.setHead(block.Hash()); err != nil {
   402  				t.Fatalf("Failed to set head: %v", err)
   403  			}
   404  			if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   405  				t.Fatalf("Chain head should be updated")
   406  			}
   407  			parent, head = block, block.NumberU64()
   408  		}
   409  	*/
   410  }
   411  
   412  // startEthService creates a full node instance for testing.
   413  func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) {
   414  	t.Helper()
   415  
   416  	n, err := node.New(&node.Config{
   417  		P2P: p2p.Config{
   418  			ListenAddr:  "0.0.0.0:0",
   419  			NoDiscovery: true,
   420  			MaxPeers:    25,
   421  		}})
   422  	if err != nil {
   423  		t.Fatal("can't create node:", err)
   424  	}
   425  
   426  	ethcfg := &ethconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, SyncMode: downloader.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256}
   427  	ethservice, err := eth.New(n, ethcfg)
   428  	if err != nil {
   429  		t.Fatal("can't create eth service:", err)
   430  	}
   431  	if err := n.Start(); err != nil {
   432  		t.Fatal("can't start node:", err)
   433  	}
   434  	if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil {
   435  		n.Close()
   436  		t.Fatal("can't import test blocks:", err)
   437  	}
   438  
   439  	ethservice.SetEtherbase(testAddr)
   440  	ethservice.SetSynced()
   441  	return n, ethservice
   442  }
   443  
   444  func TestFullAPI(t *testing.T) {
   445  	genesis, preMergeBlocks := generatePreMergeChain(10)
   446  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   447  	defer n.Close()
   448  	var (
   449  		parent = ethservice.BlockChain().CurrentBlock()
   450  		// This EVM code generates a log when the contract is created.
   451  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   452  	)
   453  
   454  	callback := func(parent *types.Block) {
   455  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   456  		nonce := statedb.GetNonce(testAddr)
   457  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   458  		ethservice.TxPool().AddLocal(tx)
   459  	}
   460  
   461  	setupBlocks(t, ethservice, 10, parent, callback)
   462  }
   463  
   464  func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Block, callback func(parent *types.Block)) {
   465  	api := NewConsensusAPI(ethservice)
   466  	for i := 0; i < n; i++ {
   467  		callback(parent)
   468  
   469  		payload := getNewPayload(t, api, parent)
   470  
   471  		execResp, err := api.NewPayloadV1(*payload)
   472  		if err != nil {
   473  			t.Fatalf("can't execute payload: %v", err)
   474  		}
   475  		if execResp.Status != beacon.VALID {
   476  			t.Fatalf("invalid status: %v", execResp.Status)
   477  		}
   478  		fcState := beacon.ForkchoiceStateV1{
   479  			HeadBlockHash:      payload.BlockHash,
   480  			SafeBlockHash:      payload.ParentHash,
   481  			FinalizedBlockHash: payload.ParentHash,
   482  		}
   483  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   484  			t.Fatalf("Failed to insert block: %v", err)
   485  		}
   486  		if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
   487  			t.Fatal("Chain head should be updated")
   488  		}
   489  		if ethservice.BlockChain().CurrentFinalizedBlock().NumberU64() != payload.Number-1 {
   490  			t.Fatal("Finalized block should be updated")
   491  		}
   492  		parent = ethservice.BlockChain().CurrentBlock()
   493  	}
   494  }
   495  
   496  func TestExchangeTransitionConfig(t *testing.T) {
   497  	genesis, preMergeBlocks := generatePreMergeChain(10)
   498  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   499  	defer n.Close()
   500  
   501  	// invalid ttd
   502  	api := NewConsensusAPI(ethservice)
   503  	config := beacon.TransitionConfigurationV1{
   504  		TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)),
   505  		TerminalBlockHash:       common.Hash{},
   506  		TerminalBlockNumber:     0,
   507  	}
   508  	if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
   509  		t.Fatal("expected error on invalid config, invalid ttd")
   510  	}
   511  	// invalid terminal block hash
   512  	config = beacon.TransitionConfigurationV1{
   513  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   514  		TerminalBlockHash:       common.Hash{1},
   515  		TerminalBlockNumber:     0,
   516  	}
   517  	if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
   518  		t.Fatal("expected error on invalid config, invalid hash")
   519  	}
   520  	// valid config
   521  	config = beacon.TransitionConfigurationV1{
   522  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   523  		TerminalBlockHash:       common.Hash{},
   524  		TerminalBlockNumber:     0,
   525  	}
   526  	if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
   527  		t.Fatalf("expected no error on valid config, got %v", err)
   528  	}
   529  	// valid config
   530  	config = beacon.TransitionConfigurationV1{
   531  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   532  		TerminalBlockHash:       preMergeBlocks[5].Hash(),
   533  		TerminalBlockNumber:     6,
   534  	}
   535  	if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
   536  		t.Fatalf("expected no error on valid config, got %v", err)
   537  	}
   538  }
   539  
   540  /*
   541  TestNewPayloadOnInvalidChain sets up a valid chain and tries to feed blocks
   542  from an invalid chain to test if latestValidHash (LVH) works correctly.
   543  
   544  We set up the following chain where P1 ... Pn and P1” are valid while
   545  P1' is invalid.
   546  We expect
   547  (1) The LVH to point to the current inserted payload if it was valid.
   548  (2) The LVH to point to the valid parent on an invalid payload (if the parent is available).
   549  (3) If the parent is unavailable, the LVH should not be set.
   550  
   551  	CommonAncestor◄─▲── P1 ◄── P2  ◄─ P3  ◄─ ... ◄─ Pn
   552  	                │
   553  	                └── P1' ◄─ P2' ◄─ P3' ◄─ ... ◄─ Pn'
   554  	                │
   555  	                └── P1''
   556  */
   557  func TestNewPayloadOnInvalidChain(t *testing.T) {
   558  	genesis, preMergeBlocks := generatePreMergeChain(10)
   559  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   560  	defer n.Close()
   561  
   562  	var (
   563  		api    = NewConsensusAPI(ethservice)
   564  		parent = ethservice.BlockChain().CurrentBlock()
   565  		signer = types.LatestSigner(ethservice.BlockChain().Config())
   566  		// This EVM code generates a log when the contract is created.
   567  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   568  	)
   569  	for i := 0; i < 10; i++ {
   570  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   571  		tx := types.MustSignNewTx(testKey, signer, &types.LegacyTx{
   572  			Nonce:    statedb.GetNonce(testAddr),
   573  			Value:    new(big.Int),
   574  			Gas:      1000000,
   575  			GasPrice: big.NewInt(2 * params.InitialBaseFee),
   576  			Data:     logCode,
   577  		})
   578  		ethservice.TxPool().AddRemotesSync([]*types.Transaction{tx})
   579  		var (
   580  			params = beacon.PayloadAttributesV1{
   581  				Timestamp:             parent.Time() + 1,
   582  				Random:                crypto.Keccak256Hash([]byte{byte(i)}),
   583  				SuggestedFeeRecipient: parent.Coinbase(),
   584  			}
   585  			fcState = beacon.ForkchoiceStateV1{
   586  				HeadBlockHash:      parent.Hash(),
   587  				SafeBlockHash:      common.Hash{},
   588  				FinalizedBlockHash: common.Hash{},
   589  			}
   590  			payload *beacon.ExecutableDataV1
   591  			resp    beacon.ForkChoiceResponse
   592  			err     error
   593  		)
   594  		for i := 0; ; i++ {
   595  			if resp, err = api.ForkchoiceUpdatedV1(fcState, &params); err != nil {
   596  				t.Fatalf("error preparing payload, err=%v", err)
   597  			}
   598  			if resp.PayloadStatus.Status != beacon.VALID {
   599  				t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status)
   600  			}
   601  			// give the payload some time to be built
   602  			time.Sleep(50 * time.Millisecond)
   603  			if payload, err = api.GetPayloadV1(*resp.PayloadID); err != nil {
   604  				t.Fatalf("can't get payload: %v", err)
   605  			}
   606  			if len(payload.Transactions) > 0 {
   607  				break
   608  			}
   609  			// No luck this time we need to update the params and try again.
   610  			params.Timestamp = params.Timestamp + 1
   611  			if i > 10 {
   612  				t.Fatalf("payload should not be empty")
   613  			}
   614  		}
   615  		execResp, err := api.NewPayloadV1(*payload)
   616  		if err != nil {
   617  			t.Fatalf("can't execute payload: %v", err)
   618  		}
   619  		if execResp.Status != beacon.VALID {
   620  			t.Fatalf("invalid status: %v", execResp.Status)
   621  		}
   622  		fcState = beacon.ForkchoiceStateV1{
   623  			HeadBlockHash:      payload.BlockHash,
   624  			SafeBlockHash:      payload.ParentHash,
   625  			FinalizedBlockHash: payload.ParentHash,
   626  		}
   627  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   628  			t.Fatalf("Failed to insert block: %v", err)
   629  		}
   630  		if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
   631  			t.Fatalf("Chain head should be updated")
   632  		}
   633  		parent = ethservice.BlockChain().CurrentBlock()
   634  	}
   635  }
   636  
   637  func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributesV1) (*beacon.ExecutableDataV1, error) {
   638  	args := &miner.BuildPayloadArgs{
   639  		Parent:       parentHash,
   640  		Timestamp:    params.Timestamp,
   641  		FeeRecipient: params.SuggestedFeeRecipient,
   642  		Random:       params.Random,
   643  	}
   644  	payload, err := api.eth.Miner().BuildPayload(args)
   645  	if err != nil {
   646  		return nil, err
   647  	}
   648  	return payload.ResolveFull(), nil
   649  }
   650  
   651  func TestEmptyBlocks(t *testing.T) {
   652  	genesis, preMergeBlocks := generatePreMergeChain(10)
   653  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   654  	defer n.Close()
   655  
   656  	commonAncestor := ethservice.BlockChain().CurrentBlock()
   657  	api := NewConsensusAPI(ethservice)
   658  
   659  	// Setup 10 blocks on the canonical chain
   660  	setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Block) {})
   661  
   662  	// (1) check LatestValidHash by sending a normal payload (P1'')
   663  	payload := getNewPayload(t, api, commonAncestor)
   664  
   665  	status, err := api.NewPayloadV1(*payload)
   666  	if err != nil {
   667  		t.Fatal(err)
   668  	}
   669  	if status.Status != beacon.VALID {
   670  		t.Errorf("invalid status: expected VALID got: %v", status.Status)
   671  	}
   672  	if !bytes.Equal(status.LatestValidHash[:], payload.BlockHash[:]) {
   673  		t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, payload.BlockHash)
   674  	}
   675  
   676  	// (2) Now send P1' which is invalid
   677  	payload = getNewPayload(t, api, commonAncestor)
   678  	payload.GasUsed += 1
   679  	payload = setBlockhash(payload)
   680  	// Now latestValidHash should be the common ancestor
   681  	status, err = api.NewPayloadV1(*payload)
   682  	if err != nil {
   683  		t.Fatal(err)
   684  	}
   685  	if status.Status != beacon.INVALID {
   686  		t.Errorf("invalid status: expected INVALID got: %v", status.Status)
   687  	}
   688  	// Expect 0x0 on INVALID block on top of PoW block
   689  	expected := common.Hash{}
   690  	if !bytes.Equal(status.LatestValidHash[:], expected[:]) {
   691  		t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, expected)
   692  	}
   693  
   694  	// (3) Now send a payload with unknown parent
   695  	payload = getNewPayload(t, api, commonAncestor)
   696  	payload.ParentHash = common.Hash{1}
   697  	payload = setBlockhash(payload)
   698  	// Now latestValidHash should be the common ancestor
   699  	status, err = api.NewPayloadV1(*payload)
   700  	if err != nil {
   701  		t.Fatal(err)
   702  	}
   703  	if status.Status != beacon.SYNCING {
   704  		t.Errorf("invalid status: expected SYNCING got: %v", status.Status)
   705  	}
   706  	if status.LatestValidHash != nil {
   707  		t.Fatalf("invalid LVH: got %v wanted nil", status.LatestValidHash)
   708  	}
   709  }
   710  
   711  func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableDataV1 {
   712  	params := beacon.PayloadAttributesV1{
   713  		Timestamp:             parent.Time() + 1,
   714  		Random:                crypto.Keccak256Hash([]byte{byte(1)}),
   715  		SuggestedFeeRecipient: parent.Coinbase(),
   716  	}
   717  
   718  	payload, err := assembleBlock(api, parent.Hash(), &params)
   719  	if err != nil {
   720  		t.Fatal(err)
   721  	}
   722  	return payload
   723  }
   724  
   725  // setBlockhash sets the blockhash of a modified ExecutableData.
   726  // Can be used to make modified payloads look valid.
   727  func setBlockhash(data *beacon.ExecutableDataV1) *beacon.ExecutableDataV1 {
   728  	txs, _ := decodeTransactions(data.Transactions)
   729  	number := big.NewInt(0)
   730  	number.SetUint64(data.Number)
   731  	header := &types.Header{
   732  		ParentHash:  data.ParentHash,
   733  		UncleHash:   types.EmptyUncleHash,
   734  		Coinbase:    data.FeeRecipient,
   735  		Root:        data.StateRoot,
   736  		TxHash:      types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
   737  		ReceiptHash: data.ReceiptsRoot,
   738  		Bloom:       types.BytesToBloom(data.LogsBloom),
   739  		Difficulty:  common.Big0,
   740  		Number:      number,
   741  		GasLimit:    data.GasLimit,
   742  		GasUsed:     data.GasUsed,
   743  		Time:        data.Timestamp,
   744  		BaseFee:     data.BaseFeePerGas,
   745  		Extra:       data.ExtraData,
   746  		MixDigest:   data.Random,
   747  	}
   748  	block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */)
   749  	data.BlockHash = block.Hash()
   750  	return data
   751  }
   752  
   753  func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
   754  	var txs = make([]*types.Transaction, len(enc))
   755  	for i, encTx := range enc {
   756  		var tx types.Transaction
   757  		if err := tx.UnmarshalBinary(encTx); err != nil {
   758  			return nil, fmt.Errorf("invalid transaction %d: %v", i, err)
   759  		}
   760  		txs[i] = &tx
   761  	}
   762  	return txs, nil
   763  }
   764  
   765  func TestTrickRemoteBlockCache(t *testing.T) {
   766  	// Setup two nodes
   767  	genesis, preMergeBlocks := generatePreMergeChain(10)
   768  	nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks)
   769  	nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks)
   770  	defer nodeA.Close()
   771  	defer nodeB.Close()
   772  	for nodeB.Server().NodeInfo().Ports.Listener == 0 {
   773  		time.Sleep(250 * time.Millisecond)
   774  	}
   775  	nodeA.Server().AddPeer(nodeB.Server().Self())
   776  	nodeB.Server().AddPeer(nodeA.Server().Self())
   777  	apiA := NewConsensusAPI(ethserviceA)
   778  	apiB := NewConsensusAPI(ethserviceB)
   779  
   780  	commonAncestor := ethserviceA.BlockChain().CurrentBlock()
   781  
   782  	// Setup 10 blocks on the canonical chain
   783  	setupBlocks(t, ethserviceA, 10, commonAncestor, func(parent *types.Block) {})
   784  	commonAncestor = ethserviceA.BlockChain().CurrentBlock()
   785  
   786  	var invalidChain []*beacon.ExecutableDataV1
   787  	// create a valid payload (P1)
   788  	//payload1 := getNewPayload(t, apiA, commonAncestor)
   789  	//invalidChain = append(invalidChain, payload1)
   790  
   791  	// create an invalid payload2 (P2)
   792  	payload2 := getNewPayload(t, apiA, commonAncestor)
   793  	//payload2.ParentHash = payload1.BlockHash
   794  	payload2.GasUsed += 1
   795  	payload2 = setBlockhash(payload2)
   796  	invalidChain = append(invalidChain, payload2)
   797  
   798  	head := payload2
   799  	// create some valid payloads on top
   800  	for i := 0; i < 10; i++ {
   801  		payload := getNewPayload(t, apiA, commonAncestor)
   802  		payload.ParentHash = head.BlockHash
   803  		payload = setBlockhash(payload)
   804  		invalidChain = append(invalidChain, payload)
   805  		head = payload
   806  	}
   807  
   808  	// feed the payloads to node B
   809  	for _, payload := range invalidChain {
   810  		status, err := apiB.NewPayloadV1(*payload)
   811  		if err != nil {
   812  			panic(err)
   813  		}
   814  		if status.Status == beacon.VALID {
   815  			t.Error("invalid status: VALID on an invalid chain")
   816  		}
   817  		// Now reorg to the head of the invalid chain
   818  		resp, err := apiB.ForkchoiceUpdatedV1(beacon.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil)
   819  		if err != nil {
   820  			t.Fatal(err)
   821  		}
   822  		if resp.PayloadStatus.Status == beacon.VALID {
   823  			t.Error("invalid status: VALID on an invalid chain")
   824  		}
   825  		time.Sleep(100 * time.Millisecond)
   826  	}
   827  }
   828  
   829  func TestInvalidBloom(t *testing.T) {
   830  	genesis, preMergeBlocks := generatePreMergeChain(10)
   831  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   832  	ethservice.Merger().ReachTTD()
   833  	defer n.Close()
   834  
   835  	commonAncestor := ethservice.BlockChain().CurrentBlock()
   836  	api := NewConsensusAPI(ethservice)
   837  
   838  	// Setup 10 blocks on the canonical chain
   839  	setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Block) {})
   840  
   841  	// (1) check LatestValidHash by sending a normal payload (P1'')
   842  	payload := getNewPayload(t, api, commonAncestor)
   843  	payload.LogsBloom = append(payload.LogsBloom, byte(1))
   844  	status, err := api.NewPayloadV1(*payload)
   845  	if err != nil {
   846  		t.Fatal(err)
   847  	}
   848  	if status.Status != beacon.INVALIDBLOCKHASH {
   849  		t.Errorf("invalid status: expected VALID got: %v", status.Status)
   850  	}
   851  }
   852  
   853  func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
   854  	genesis, preMergeBlocks := generatePreMergeChain(100)
   855  
   856  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   857  	defer n.Close()
   858  
   859  	genesis.Config.TerminalTotalDifficulty = preMergeBlocks[0].Difficulty() //.Sub(genesis.Config.TerminalTotalDifficulty, preMergeBlocks[len(preMergeBlocks)-1].Difficulty())
   860  	var (
   861  		api    = NewConsensusAPI(ethservice)
   862  		parent = preMergeBlocks[len(preMergeBlocks)-1]
   863  	)
   864  
   865  	// Test parent already post TTD in FCU
   866  	fcState := beacon.ForkchoiceStateV1{
   867  		HeadBlockHash:      parent.Hash(),
   868  		SafeBlockHash:      common.Hash{},
   869  		FinalizedBlockHash: common.Hash{},
   870  	}
   871  	resp, err := api.ForkchoiceUpdatedV1(fcState, nil)
   872  	if err != nil {
   873  		t.Fatalf("error sending forkchoice, err=%v", err)
   874  	}
   875  	if resp.PayloadStatus != beacon.INVALID_TERMINAL_BLOCK {
   876  		t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
   877  	}
   878  
   879  	// Test parent already post TTD in NewPayload
   880  	args := &miner.BuildPayloadArgs{
   881  		Parent:       parent.Hash(),
   882  		Timestamp:    parent.Time() + 1,
   883  		Random:       crypto.Keccak256Hash([]byte{byte(1)}),
   884  		FeeRecipient: parent.Coinbase(),
   885  	}
   886  	payload, err := api.eth.Miner().BuildPayload(args)
   887  	if err != nil {
   888  		t.Fatalf("error preparing payload, err=%v", err)
   889  	}
   890  	data := *payload.Resolve()
   891  	resp2, err := api.NewPayloadV1(data)
   892  	if err != nil {
   893  		t.Fatalf("error sending NewPayload, err=%v", err)
   894  	}
   895  	if resp2 != beacon.INVALID_TERMINAL_BLOCK {
   896  		t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
   897  	}
   898  }
   899  
   900  // TestSimultaneousNewBlock does several parallel inserts, both as
   901  // newPayLoad and forkchoiceUpdate. This is to test that the api behaves
   902  // well even of the caller is not being 'serial'.
   903  func TestSimultaneousNewBlock(t *testing.T) {
   904  	genesis, preMergeBlocks := generatePreMergeChain(10)
   905  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   906  	defer n.Close()
   907  
   908  	var (
   909  		api    = NewConsensusAPI(ethservice)
   910  		parent = preMergeBlocks[len(preMergeBlocks)-1]
   911  	)
   912  	for i := 0; i < 10; i++ {
   913  		execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{
   914  			Timestamp: parent.Time() + 5,
   915  		})
   916  		if err != nil {
   917  			t.Fatalf("Failed to create the executable data %v", err)
   918  		}
   919  		// Insert it 10 times in parallel. Should be ignored.
   920  		{
   921  			var (
   922  				wg      sync.WaitGroup
   923  				testErr error
   924  				errMu   sync.Mutex
   925  			)
   926  			wg.Add(10)
   927  			for ii := 0; ii < 10; ii++ {
   928  				go func() {
   929  					defer wg.Done()
   930  					if newResp, err := api.NewPayloadV1(*execData); err != nil {
   931  						errMu.Lock()
   932  						testErr = fmt.Errorf("Failed to insert block: %w", err)
   933  						errMu.Unlock()
   934  					} else if newResp.Status != "VALID" {
   935  						errMu.Lock()
   936  						testErr = fmt.Errorf("Failed to insert block: %v", newResp.Status)
   937  						errMu.Unlock()
   938  					}
   939  				}()
   940  			}
   941  			wg.Wait()
   942  			if testErr != nil {
   943  				t.Fatal(testErr)
   944  			}
   945  		}
   946  		block, err := beacon.ExecutableDataToBlock(*execData)
   947  		if err != nil {
   948  			t.Fatalf("Failed to convert executable data to block %v", err)
   949  		}
   950  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 {
   951  			t.Fatalf("Chain head shouldn't be updated")
   952  		}
   953  		fcState := beacon.ForkchoiceStateV1{
   954  			HeadBlockHash:      block.Hash(),
   955  			SafeBlockHash:      block.Hash(),
   956  			FinalizedBlockHash: block.Hash(),
   957  		}
   958  		{
   959  			var (
   960  				wg      sync.WaitGroup
   961  				testErr error
   962  				errMu   sync.Mutex
   963  			)
   964  			wg.Add(10)
   965  			// Do each FCU 10 times
   966  			for ii := 0; ii < 10; ii++ {
   967  				go func() {
   968  					defer wg.Done()
   969  					if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   970  						errMu.Lock()
   971  						testErr = fmt.Errorf("Failed to insert block: %w", err)
   972  						errMu.Unlock()
   973  					}
   974  				}()
   975  			}
   976  			wg.Wait()
   977  			if testErr != nil {
   978  				t.Fatal(testErr)
   979  			}
   980  		}
   981  		if have, want := ethservice.BlockChain().CurrentBlock().NumberU64(), block.NumberU64(); have != want {
   982  			t.Fatalf("Chain head should be updated, have %d want %d", have, want)
   983  		}
   984  		parent = block
   985  	}
   986  }