github.com/codysnider/go-ethereum@v1.10.18-0.20220420071915-14f4ae99222a/eth/catalyst/api_test.go (about)

     1  // Copyright 2020 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  	"fmt"
    21  	"math/big"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/common/hexutil"
    27  	"github.com/ethereum/go-ethereum/consensus/ethash"
    28  	"github.com/ethereum/go-ethereum/core"
    29  	"github.com/ethereum/go-ethereum/core/beacon"
    30  	"github.com/ethereum/go-ethereum/core/rawdb"
    31  	"github.com/ethereum/go-ethereum/core/types"
    32  	"github.com/ethereum/go-ethereum/crypto"
    33  	"github.com/ethereum/go-ethereum/eth"
    34  	"github.com/ethereum/go-ethereum/eth/ethconfig"
    35  	"github.com/ethereum/go-ethereum/node"
    36  	"github.com/ethereum/go-ethereum/params"
    37  )
    38  
    39  var (
    40  	// testKey is a private key to use for funding a tester account.
    41  	testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    42  
    43  	// testAddr is the Ethereum address of the tester account.
    44  	testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
    45  
    46  	testBalance = big.NewInt(2e18)
    47  )
    48  
    49  func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) {
    50  	db := rawdb.NewMemoryDatabase()
    51  	config := params.AllEthashProtocolChanges
    52  	genesis := &core.Genesis{
    53  		Config:     config,
    54  		Alloc:      core.GenesisAlloc{testAddr: {Balance: testBalance}},
    55  		ExtraData:  []byte("test genesis"),
    56  		Timestamp:  9000,
    57  		BaseFee:    big.NewInt(params.InitialBaseFee),
    58  		Difficulty: big.NewInt(0),
    59  	}
    60  	testNonce := uint64(0)
    61  	generate := func(i int, g *core.BlockGen) {
    62  		g.OffsetTime(5)
    63  		g.SetExtra([]byte("test"))
    64  		tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(config), testKey)
    65  		g.AddTx(tx)
    66  		testNonce++
    67  	}
    68  	gblock := genesis.ToBlock(db)
    69  	engine := ethash.NewFaker()
    70  	blocks, _ := core.GenerateChain(config, gblock, engine, db, n, generate)
    71  	totalDifficulty := big.NewInt(0)
    72  	for _, b := range blocks {
    73  		totalDifficulty.Add(totalDifficulty, b.Difficulty())
    74  	}
    75  	config.TerminalTotalDifficulty = totalDifficulty
    76  	return genesis, blocks
    77  }
    78  
    79  func TestEth2AssembleBlock(t *testing.T) {
    80  	genesis, blocks := generatePreMergeChain(10)
    81  	n, ethservice := startEthService(t, genesis, blocks)
    82  	defer n.Close()
    83  
    84  	api := NewConsensusAPI(ethservice)
    85  	signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID)
    86  	tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey)
    87  	if err != nil {
    88  		t.Fatalf("error signing transaction, err=%v", err)
    89  	}
    90  	ethservice.TxPool().AddLocal(tx)
    91  	blockParams := beacon.PayloadAttributesV1{
    92  		Timestamp: blocks[9].Time() + 5,
    93  	}
    94  	execData, err := api.assembleBlock(blocks[9].Hash(), &blockParams)
    95  	if err != nil {
    96  		t.Fatalf("error producing block, err=%v", err)
    97  	}
    98  	if len(execData.Transactions) != 1 {
    99  		t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
   100  	}
   101  }
   102  
   103  func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
   104  	genesis, blocks := generatePreMergeChain(10)
   105  	n, ethservice := startEthService(t, genesis, blocks[:9])
   106  	defer n.Close()
   107  
   108  	api := NewConsensusAPI(ethservice)
   109  
   110  	// Put the 10th block's tx in the pool and produce a new block
   111  	api.insertTransactions(blocks[9].Transactions())
   112  	blockParams := beacon.PayloadAttributesV1{
   113  		Timestamp: blocks[8].Time() + 5,
   114  	}
   115  	execData, err := api.assembleBlock(blocks[8].Hash(), &blockParams)
   116  	if err != nil {
   117  		t.Fatalf("error producing block, err=%v", err)
   118  	}
   119  	if len(execData.Transactions) != blocks[9].Transactions().Len() {
   120  		t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
   121  	}
   122  }
   123  
   124  func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
   125  	genesis, blocks := generatePreMergeChain(10)
   126  	n, ethservice := startEthService(t, genesis, blocks)
   127  	defer n.Close()
   128  
   129  	api := NewConsensusAPI(ethservice)
   130  	fcState := beacon.ForkchoiceStateV1{
   131  		HeadBlockHash:      blocks[5].Hash(),
   132  		SafeBlockHash:      common.Hash{},
   133  		FinalizedBlockHash: common.Hash{},
   134  	}
   135  	if resp, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   136  		t.Errorf("fork choice updated should not error: %v", err)
   137  	} else if resp.PayloadStatus.Status != beacon.INVALIDTERMINALBLOCK {
   138  		t.Errorf("fork choice updated before total terminal difficulty should be INVALID")
   139  	}
   140  }
   141  
   142  func TestEth2PrepareAndGetPayload(t *testing.T) {
   143  	// TODO (MariusVanDerWijden) TestEth2PrepareAndGetPayload is currently broken, fixed in upcoming merge-kiln-v2 pr
   144  	/*
   145  		genesis, blocks := generatePreMergeChain(10)
   146  		// We need to properly set the terminal total difficulty
   147  		genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty())
   148  		n, ethservice := startEthService(t, genesis, blocks[:9])
   149  		defer n.Close()
   150  
   151  		api := NewConsensusAPI(ethservice)
   152  
   153  		// Put the 10th block's tx in the pool and produce a new block
   154  		api.insertTransactions(blocks[9].Transactions())
   155  		blockParams := beacon.PayloadAttributesV1{
   156  			Timestamp: blocks[8].Time() + 5,
   157  		}
   158  		fcState := beacon.ForkchoiceStateV1{
   159  			HeadBlockHash:      blocks[8].Hash(),
   160  			SafeBlockHash:      common.Hash{},
   161  			FinalizedBlockHash: common.Hash{},
   162  		}
   163  		_, err := api.ForkchoiceUpdatedV1(fcState, &blockParams)
   164  		if err != nil {
   165  			t.Fatalf("error preparing payload, err=%v", err)
   166  		}
   167  		payloadID := computePayloadId(fcState.HeadBlockHash, &blockParams)
   168  		execData, err := api.GetPayloadV1(payloadID)
   169  		if err != nil {
   170  			t.Fatalf("error getting payload, err=%v", err)
   171  		}
   172  		if len(execData.Transactions) != blocks[9].Transactions().Len() {
   173  			t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
   174  		}
   175  		// Test invalid payloadID
   176  		var invPayload beacon.PayloadID
   177  		copy(invPayload[:], payloadID[:])
   178  		invPayload[0] = ^invPayload[0]
   179  		_, err = api.GetPayloadV1(invPayload)
   180  		if err == nil {
   181  			t.Fatal("expected error retrieving invalid payload")
   182  		}
   183  	*/
   184  }
   185  
   186  func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) {
   187  	t.Helper()
   188  
   189  	if len(logsCh) != wantNew {
   190  		t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew)
   191  	}
   192  	if len(rmLogsCh) != wantRemoved {
   193  		t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved)
   194  	}
   195  	// Drain events.
   196  	for i := 0; i < len(logsCh); i++ {
   197  		<-logsCh
   198  	}
   199  	for i := 0; i < len(rmLogsCh); i++ {
   200  		<-rmLogsCh
   201  	}
   202  }
   203  
   204  func TestInvalidPayloadTimestamp(t *testing.T) {
   205  	genesis, preMergeBlocks := generatePreMergeChain(10)
   206  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   207  	ethservice.Merger().ReachTTD()
   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  	ethservice.Merger().ReachTTD()
   253  	defer n.Close()
   254  
   255  	var (
   256  		api    = NewConsensusAPI(ethservice)
   257  		parent = preMergeBlocks[len(preMergeBlocks)-1]
   258  
   259  		// This EVM code generates a log when the contract is created.
   260  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   261  	)
   262  	// The event channels.
   263  	newLogCh := make(chan []*types.Log, 10)
   264  	rmLogsCh := make(chan core.RemovedLogsEvent, 10)
   265  	ethservice.BlockChain().SubscribeLogsEvent(newLogCh)
   266  	ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh)
   267  
   268  	for i := 0; i < 10; i++ {
   269  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   270  		nonce := statedb.GetNonce(testAddr)
   271  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   272  		ethservice.TxPool().AddLocal(tx)
   273  
   274  		execData, err := api.assembleBlock(parent.Hash(), &beacon.PayloadAttributesV1{
   275  			Timestamp: parent.Time() + 5,
   276  		})
   277  		if err != nil {
   278  			t.Fatalf("Failed to create the executable data %v", err)
   279  		}
   280  		block, err := beacon.ExecutableDataToBlock(*execData)
   281  		if err != nil {
   282  			t.Fatalf("Failed to convert executable data to block %v", err)
   283  		}
   284  		newResp, err := api.NewPayloadV1(*execData)
   285  		if err != nil || newResp.Status != "VALID" {
   286  			t.Fatalf("Failed to insert block: %v", err)
   287  		}
   288  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 {
   289  			t.Fatalf("Chain head shouldn't be updated")
   290  		}
   291  		checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
   292  		fcState := beacon.ForkchoiceStateV1{
   293  			HeadBlockHash:      block.Hash(),
   294  			SafeBlockHash:      block.Hash(),
   295  			FinalizedBlockHash: block.Hash(),
   296  		}
   297  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   298  			t.Fatalf("Failed to insert block: %v", err)
   299  		}
   300  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   301  			t.Fatalf("Chain head should be updated")
   302  		}
   303  		checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
   304  
   305  		parent = block
   306  	}
   307  
   308  	// Introduce fork chain
   309  	var (
   310  		head = ethservice.BlockChain().CurrentBlock().NumberU64()
   311  	)
   312  	parent = preMergeBlocks[len(preMergeBlocks)-1]
   313  	for i := 0; i < 10; i++ {
   314  		execData, err := api.assembleBlock(parent.Hash(), &beacon.PayloadAttributesV1{
   315  			Timestamp: parent.Time() + 6,
   316  		})
   317  		if err != nil {
   318  			t.Fatalf("Failed to create the executable data %v", err)
   319  		}
   320  		block, err := beacon.ExecutableDataToBlock(*execData)
   321  		if err != nil {
   322  			t.Fatalf("Failed to convert executable data to block %v", err)
   323  		}
   324  		newResp, err := api.NewPayloadV1(*execData)
   325  		if err != nil || newResp.Status != "VALID" {
   326  			t.Fatalf("Failed to insert block: %v", err)
   327  		}
   328  		if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   329  			t.Fatalf("Chain head shouldn't be updated")
   330  		}
   331  
   332  		fcState := beacon.ForkchoiceStateV1{
   333  			HeadBlockHash:      block.Hash(),
   334  			SafeBlockHash:      block.Hash(),
   335  			FinalizedBlockHash: block.Hash(),
   336  		}
   337  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   338  			t.Fatalf("Failed to insert block: %v", err)
   339  		}
   340  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   341  			t.Fatalf("Chain head should be updated")
   342  		}
   343  		parent, head = block, block.NumberU64()
   344  	}
   345  }
   346  
   347  func TestEth2DeepReorg(t *testing.T) {
   348  	// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
   349  	// before the totalTerminalDifficulty threshold
   350  	/*
   351  		genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2)
   352  		n, ethservice := startEthService(t, genesis, preMergeBlocks)
   353  		defer n.Close()
   354  
   355  		var (
   356  			api    = NewConsensusAPI(ethservice, nil)
   357  			parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1]
   358  			head   = ethservice.BlockChain().CurrentBlock().NumberU64()
   359  		)
   360  		if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) {
   361  			t.Errorf("Block %d not pruned", parent.NumberU64())
   362  		}
   363  		for i := 0; i < 10; i++ {
   364  			execData, err := api.assembleBlock(AssembleBlockParams{
   365  				ParentHash: parent.Hash(),
   366  				Timestamp:  parent.Time() + 5,
   367  			})
   368  			if err != nil {
   369  				t.Fatalf("Failed to create the executable data %v", err)
   370  			}
   371  			block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData)
   372  			if err != nil {
   373  				t.Fatalf("Failed to convert executable data to block %v", err)
   374  			}
   375  			newResp, err := api.ExecutePayload(*execData)
   376  			if err != nil || newResp.Status != "VALID" {
   377  				t.Fatalf("Failed to insert block: %v", err)
   378  			}
   379  			if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   380  				t.Fatalf("Chain head shouldn't be updated")
   381  			}
   382  			if err := api.setHead(block.Hash()); err != nil {
   383  				t.Fatalf("Failed to set head: %v", err)
   384  			}
   385  			if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   386  				t.Fatalf("Chain head should be updated")
   387  			}
   388  			parent, head = block, block.NumberU64()
   389  		}
   390  	*/
   391  }
   392  
   393  // startEthService creates a full node instance for testing.
   394  func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) {
   395  	t.Helper()
   396  
   397  	n, err := node.New(&node.Config{})
   398  	if err != nil {
   399  		t.Fatal("can't create node:", err)
   400  	}
   401  
   402  	ethcfg := &ethconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256}
   403  	ethservice, err := eth.New(n, ethcfg)
   404  	if err != nil {
   405  		t.Fatal("can't create eth service:", err)
   406  	}
   407  	if err := n.Start(); err != nil {
   408  		t.Fatal("can't start node:", err)
   409  	}
   410  	if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil {
   411  		n.Close()
   412  		t.Fatal("can't import test blocks:", err)
   413  	}
   414  	ethservice.SetEtherbase(testAddr)
   415  	ethservice.SetSynced()
   416  
   417  	return n, ethservice
   418  }
   419  
   420  func TestFullAPI(t *testing.T) {
   421  	genesis, preMergeBlocks := generatePreMergeChain(10)
   422  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   423  	ethservice.Merger().ReachTTD()
   424  	defer n.Close()
   425  	var (
   426  		api    = NewConsensusAPI(ethservice)
   427  		parent = ethservice.BlockChain().CurrentBlock()
   428  		// This EVM code generates a log when the contract is created.
   429  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   430  	)
   431  	for i := 0; i < 10; i++ {
   432  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   433  		nonce := statedb.GetNonce(testAddr)
   434  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   435  		ethservice.TxPool().AddLocal(tx)
   436  
   437  		params := beacon.PayloadAttributesV1{
   438  			Timestamp:             parent.Time() + 1,
   439  			Random:                crypto.Keccak256Hash([]byte{byte(i)}),
   440  			SuggestedFeeRecipient: parent.Coinbase(),
   441  		}
   442  
   443  		fcState := beacon.ForkchoiceStateV1{
   444  			HeadBlockHash:      parent.Hash(),
   445  			SafeBlockHash:      common.Hash{},
   446  			FinalizedBlockHash: common.Hash{},
   447  		}
   448  		resp, err := api.ForkchoiceUpdatedV1(fcState, &params)
   449  		if err != nil {
   450  			t.Fatalf("error preparing payload, err=%v", err)
   451  		}
   452  		if resp.PayloadStatus.Status != beacon.VALID {
   453  			t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status)
   454  		}
   455  		payload, err := api.GetPayloadV1(*resp.PayloadID)
   456  		if err != nil {
   457  			t.Fatalf("can't get payload: %v", err)
   458  		}
   459  		execResp, err := api.NewPayloadV1(*payload)
   460  		if err != nil {
   461  			t.Fatalf("can't execute payload: %v", err)
   462  		}
   463  		if execResp.Status != beacon.VALID {
   464  			t.Fatalf("invalid status: %v", execResp.Status)
   465  		}
   466  		fcState = beacon.ForkchoiceStateV1{
   467  			HeadBlockHash:      payload.BlockHash,
   468  			SafeBlockHash:      payload.ParentHash,
   469  			FinalizedBlockHash: payload.ParentHash,
   470  		}
   471  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   472  			t.Fatalf("Failed to insert block: %v", err)
   473  		}
   474  		if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
   475  			t.Fatalf("Chain head should be updated")
   476  		}
   477  		parent = ethservice.BlockChain().CurrentBlock()
   478  	}
   479  }
   480  
   481  func TestExchangeTransitionConfig(t *testing.T) {
   482  	genesis, preMergeBlocks := generatePreMergeChain(10)
   483  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   484  	ethservice.Merger().ReachTTD()
   485  	defer n.Close()
   486  	var (
   487  		api = NewConsensusAPI(ethservice)
   488  	)
   489  	// invalid ttd
   490  	config := beacon.TransitionConfigurationV1{
   491  		TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)),
   492  		TerminalBlockHash:       common.Hash{},
   493  		TerminalBlockNumber:     0,
   494  	}
   495  	if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
   496  		t.Fatal("expected error on invalid config, invalid ttd")
   497  	}
   498  	// invalid terminal block hash
   499  	config = beacon.TransitionConfigurationV1{
   500  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   501  		TerminalBlockHash:       common.Hash{1},
   502  		TerminalBlockNumber:     0,
   503  	}
   504  	if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
   505  		t.Fatal("expected error on invalid config, invalid hash")
   506  	}
   507  	// valid config
   508  	config = beacon.TransitionConfigurationV1{
   509  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   510  		TerminalBlockHash:       common.Hash{},
   511  		TerminalBlockNumber:     0,
   512  	}
   513  	if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
   514  		t.Fatalf("expected no error on valid config, got %v", err)
   515  	}
   516  	// valid config
   517  	config = beacon.TransitionConfigurationV1{
   518  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   519  		TerminalBlockHash:       preMergeBlocks[5].Hash(),
   520  		TerminalBlockNumber:     6,
   521  	}
   522  	if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
   523  		t.Fatalf("expected no error on valid config, got %v", err)
   524  	}
   525  }