github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/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  	t.Skip("bor due to burn contract")
    81  
    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  	execData, err := api.assembleBlock(blocks[9].Hash(), &blockParams)
    97  	if err != nil {
    98  		t.Fatalf("error producing block, err=%v", err)
    99  	}
   100  	if len(execData.Transactions) != 1 {
   101  		t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
   102  	}
   103  }
   104  
   105  func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
   106  	t.Skip("bor due to burn contract")
   107  
   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.insertTransactions(blocks[9].Transactions())
   116  	blockParams := beacon.PayloadAttributesV1{
   117  		Timestamp: blocks[8].Time() + 5,
   118  	}
   119  	execData, err := api.assembleBlock(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.INVALIDTERMINALBLOCK {
   142  		t.Errorf("fork choice updated before total terminal difficulty should be INVALID")
   143  	}
   144  }
   145  
   146  func TestEth2PrepareAndGetPayload(t *testing.T) {
   147  	// TODO (MariusVanDerWijden) TestEth2PrepareAndGetPayload is currently broken, fixed in upcoming merge-kiln-v2 pr
   148  	/*
   149  		genesis, blocks := generatePreMergeChain(10)
   150  		// We need to properly set the terminal total difficulty
   151  		genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty())
   152  		n, ethservice := startEthService(t, genesis, blocks[:9])
   153  		defer n.Close()
   154  
   155  		api := NewConsensusAPI(ethservice)
   156  
   157  		// Put the 10th block's tx in the pool and produce a new block
   158  		api.insertTransactions(blocks[9].Transactions())
   159  		blockParams := beacon.PayloadAttributesV1{
   160  			Timestamp: blocks[8].Time() + 5,
   161  		}
   162  		fcState := beacon.ForkchoiceStateV1{
   163  			HeadBlockHash:      blocks[8].Hash(),
   164  			SafeBlockHash:      common.Hash{},
   165  			FinalizedBlockHash: common.Hash{},
   166  		}
   167  		_, err := api.ForkchoiceUpdatedV1(fcState, &blockParams)
   168  		if err != nil {
   169  			t.Fatalf("error preparing payload, err=%v", err)
   170  		}
   171  		payloadID := computePayloadId(fcState.HeadBlockHash, &blockParams)
   172  		execData, err := api.GetPayloadV1(payloadID)
   173  		if err != nil {
   174  			t.Fatalf("error getting payload, err=%v", err)
   175  		}
   176  		if len(execData.Transactions) != blocks[9].Transactions().Len() {
   177  			t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
   178  		}
   179  		// Test invalid payloadID
   180  		var invPayload beacon.PayloadID
   181  		copy(invPayload[:], payloadID[:])
   182  		invPayload[0] = ^invPayload[0]
   183  		_, err = api.GetPayloadV1(invPayload)
   184  		if err == nil {
   185  			t.Fatal("expected error retrieving invalid payload")
   186  		}
   187  	*/
   188  }
   189  
   190  func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) {
   191  	t.Helper()
   192  
   193  	if len(logsCh) != wantNew {
   194  		t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew)
   195  	}
   196  	if len(rmLogsCh) != wantRemoved {
   197  		t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved)
   198  	}
   199  	// Drain events.
   200  	for i := 0; i < len(logsCh); i++ {
   201  		<-logsCh
   202  	}
   203  	for i := 0; i < len(rmLogsCh); i++ {
   204  		<-rmLogsCh
   205  	}
   206  }
   207  
   208  func TestInvalidPayloadTimestamp(t *testing.T) {
   209  	genesis, preMergeBlocks := generatePreMergeChain(10)
   210  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   211  	ethservice.Merger().ReachTTD()
   212  	defer n.Close()
   213  	var (
   214  		api    = NewConsensusAPI(ethservice)
   215  		parent = ethservice.BlockChain().CurrentBlock()
   216  	)
   217  	tests := []struct {
   218  		time      uint64
   219  		shouldErr bool
   220  	}{
   221  		{0, true},
   222  		{parent.Time(), true},
   223  		{parent.Time() - 1, true},
   224  
   225  		// TODO (MariusVanDerWijden) following tests are currently broken,
   226  		// fixed in upcoming merge-kiln-v2 pr
   227  		//{parent.Time() + 1, false},
   228  		//{uint64(time.Now().Unix()) + uint64(time.Minute), false},
   229  	}
   230  
   231  	for i, test := range tests {
   232  		t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) {
   233  			params := beacon.PayloadAttributesV1{
   234  				Timestamp:             test.time,
   235  				Random:                crypto.Keccak256Hash([]byte{byte(123)}),
   236  				SuggestedFeeRecipient: parent.Coinbase(),
   237  			}
   238  			fcState := beacon.ForkchoiceStateV1{
   239  				HeadBlockHash:      parent.Hash(),
   240  				SafeBlockHash:      common.Hash{},
   241  				FinalizedBlockHash: common.Hash{},
   242  			}
   243  			_, err := api.ForkchoiceUpdatedV1(fcState, &params)
   244  			if test.shouldErr && err == nil {
   245  				t.Fatalf("expected error preparing payload with invalid timestamp, err=%v", err)
   246  			} else if !test.shouldErr && err != nil {
   247  				t.Fatalf("error preparing payload with valid timestamp, err=%v", err)
   248  			}
   249  		})
   250  	}
   251  }
   252  
   253  func TestEth2NewBlock(t *testing.T) {
   254  	t.Skip("ETH2 in Bor")
   255  	genesis, preMergeBlocks := generatePreMergeChain(10)
   256  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   257  	ethservice.Merger().ReachTTD()
   258  	defer n.Close()
   259  
   260  	var (
   261  		api    = NewConsensusAPI(ethservice)
   262  		parent = preMergeBlocks[len(preMergeBlocks)-1]
   263  
   264  		// This EVM code generates a log when the contract is created.
   265  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   266  	)
   267  	// The event channels.
   268  	newLogCh := make(chan []*types.Log, 10)
   269  	rmLogsCh := make(chan core.RemovedLogsEvent, 10)
   270  	ethservice.BlockChain().SubscribeLogsEvent(newLogCh)
   271  	ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh)
   272  
   273  	for i := 0; i < 10; i++ {
   274  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   275  		nonce := statedb.GetNonce(testAddr)
   276  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   277  		ethservice.TxPool().AddLocal(tx)
   278  
   279  		execData, err := api.assembleBlock(parent.Hash(), &beacon.PayloadAttributesV1{
   280  			Timestamp: parent.Time() + 5,
   281  		})
   282  		if err != nil {
   283  			t.Fatalf("Failed to create the executable data %v", err)
   284  		}
   285  		block, err := beacon.ExecutableDataToBlock(*execData)
   286  		if err != nil {
   287  			t.Fatalf("Failed to convert executable data to block %v", err)
   288  		}
   289  		newResp, err := api.NewPayloadV1(*execData)
   290  		if err != nil || newResp.Status != "VALID" {
   291  			t.Fatalf("Failed to insert block: %v", err)
   292  		}
   293  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 {
   294  			t.Fatalf("Chain head shouldn't be updated")
   295  		}
   296  		checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
   297  		fcState := beacon.ForkchoiceStateV1{
   298  			HeadBlockHash:      block.Hash(),
   299  			SafeBlockHash:      block.Hash(),
   300  			FinalizedBlockHash: block.Hash(),
   301  		}
   302  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   303  			t.Fatalf("Failed to insert block: %v", err)
   304  		}
   305  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   306  			t.Fatalf("Chain head should be updated")
   307  		}
   308  		checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
   309  
   310  		parent = block
   311  	}
   312  
   313  	// Introduce fork chain
   314  	var (
   315  		head = ethservice.BlockChain().CurrentBlock().NumberU64()
   316  	)
   317  	parent = preMergeBlocks[len(preMergeBlocks)-1]
   318  	for i := 0; i < 10; i++ {
   319  		execData, err := api.assembleBlock(parent.Hash(), &beacon.PayloadAttributesV1{
   320  			Timestamp: parent.Time() + 6,
   321  		})
   322  		if err != nil {
   323  			t.Fatalf("Failed to create the executable data %v", err)
   324  		}
   325  		block, err := beacon.ExecutableDataToBlock(*execData)
   326  		if err != nil {
   327  			t.Fatalf("Failed to convert executable data to block %v", err)
   328  		}
   329  		newResp, err := api.NewPayloadV1(*execData)
   330  		if err != nil || newResp.Status != "VALID" {
   331  			t.Fatalf("Failed to insert block: %v", err)
   332  		}
   333  		if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   334  			t.Fatalf("Chain head shouldn't be updated")
   335  		}
   336  
   337  		fcState := beacon.ForkchoiceStateV1{
   338  			HeadBlockHash:      block.Hash(),
   339  			SafeBlockHash:      block.Hash(),
   340  			FinalizedBlockHash: block.Hash(),
   341  		}
   342  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   343  			t.Fatalf("Failed to insert block: %v", err)
   344  		}
   345  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   346  			t.Fatalf("Chain head should be updated")
   347  		}
   348  		parent, head = block, block.NumberU64()
   349  	}
   350  }
   351  
   352  func TestEth2DeepReorg(t *testing.T) {
   353  	// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
   354  	// before the totalTerminalDifficulty threshold
   355  	/*
   356  		genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2)
   357  		n, ethservice := startEthService(t, genesis, preMergeBlocks)
   358  		defer n.Close()
   359  
   360  		var (
   361  			api    = NewConsensusAPI(ethservice, nil)
   362  			parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1]
   363  			head   = ethservice.BlockChain().CurrentBlock().NumberU64()
   364  		)
   365  		if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) {
   366  			t.Errorf("Block %d not pruned", parent.NumberU64())
   367  		}
   368  		for i := 0; i < 10; i++ {
   369  			execData, err := api.assembleBlock(AssembleBlockParams{
   370  				ParentHash: parent.Hash(),
   371  				Timestamp:  parent.Time() + 5,
   372  			})
   373  			if err != nil {
   374  				t.Fatalf("Failed to create the executable data %v", err)
   375  			}
   376  			block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData)
   377  			if err != nil {
   378  				t.Fatalf("Failed to convert executable data to block %v", err)
   379  			}
   380  			newResp, err := api.ExecutePayload(*execData)
   381  			if err != nil || newResp.Status != "VALID" {
   382  				t.Fatalf("Failed to insert block: %v", err)
   383  			}
   384  			if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   385  				t.Fatalf("Chain head shouldn't be updated")
   386  			}
   387  			if err := api.setHead(block.Hash()); err != nil {
   388  				t.Fatalf("Failed to set head: %v", err)
   389  			}
   390  			if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   391  				t.Fatalf("Chain head should be updated")
   392  			}
   393  			parent, head = block, block.NumberU64()
   394  		}
   395  	*/
   396  }
   397  
   398  // startEthService creates a full node instance for testing.
   399  func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) {
   400  	t.Helper()
   401  
   402  	n, err := node.New(&node.Config{})
   403  	if err != nil {
   404  		t.Fatal("can't create node:", err)
   405  	}
   406  
   407  	ethcfg := &ethconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256}
   408  	ethservice, err := eth.New(n, ethcfg)
   409  	if err != nil {
   410  		t.Fatal("can't create eth service:", err)
   411  	}
   412  	if err := n.Start(); err != nil {
   413  		t.Fatal("can't start node:", err)
   414  	}
   415  	if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil {
   416  		n.Close()
   417  		t.Fatal("can't import test blocks:", err)
   418  	}
   419  	ethservice.SetEtherbase(testAddr)
   420  	ethservice.SetSynced()
   421  
   422  	return n, ethservice
   423  }
   424  
   425  func TestFullAPI(t *testing.T) {
   426  	t.Skip("ETH2 in Bor")
   427  
   428  	genesis, preMergeBlocks := generatePreMergeChain(10)
   429  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   430  	ethservice.Merger().ReachTTD()
   431  	defer n.Close()
   432  	var (
   433  		api    = NewConsensusAPI(ethservice)
   434  		parent = ethservice.BlockChain().CurrentBlock()
   435  		// This EVM code generates a log when the contract is created.
   436  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   437  	)
   438  	for i := 0; i < 10; i++ {
   439  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   440  		nonce := statedb.GetNonce(testAddr)
   441  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   442  		ethservice.TxPool().AddLocal(tx)
   443  
   444  		params := beacon.PayloadAttributesV1{
   445  			Timestamp:             parent.Time() + 1,
   446  			Random:                crypto.Keccak256Hash([]byte{byte(i)}),
   447  			SuggestedFeeRecipient: parent.Coinbase(),
   448  		}
   449  
   450  		fcState := beacon.ForkchoiceStateV1{
   451  			HeadBlockHash:      parent.Hash(),
   452  			SafeBlockHash:      common.Hash{},
   453  			FinalizedBlockHash: common.Hash{},
   454  		}
   455  		resp, err := api.ForkchoiceUpdatedV1(fcState, &params)
   456  		if err != nil {
   457  			t.Fatalf("error preparing payload, err=%v", err)
   458  		}
   459  		if resp.PayloadStatus.Status != beacon.VALID {
   460  			t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status)
   461  		}
   462  		payload, err := api.GetPayloadV1(*resp.PayloadID)
   463  		if err != nil {
   464  			t.Fatalf("can't get payload: %v", err)
   465  		}
   466  		execResp, err := api.NewPayloadV1(*payload)
   467  		if err != nil {
   468  			t.Fatalf("can't execute payload: %v", err)
   469  		}
   470  		if execResp.Status != beacon.VALID {
   471  			t.Fatalf("invalid status: %v", execResp.Status)
   472  		}
   473  		fcState = beacon.ForkchoiceStateV1{
   474  			HeadBlockHash:      payload.BlockHash,
   475  			SafeBlockHash:      payload.ParentHash,
   476  			FinalizedBlockHash: payload.ParentHash,
   477  		}
   478  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   479  			t.Fatalf("Failed to insert block: %v", err)
   480  		}
   481  		if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
   482  			t.Fatalf("Chain head should be updated")
   483  		}
   484  		parent = ethservice.BlockChain().CurrentBlock()
   485  	}
   486  }
   487  
   488  func TestExchangeTransitionConfig(t *testing.T) {
   489  	genesis, preMergeBlocks := generatePreMergeChain(10)
   490  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   491  	ethservice.Merger().ReachTTD()
   492  	defer n.Close()
   493  	var (
   494  		api = NewConsensusAPI(ethservice)
   495  	)
   496  	// invalid ttd
   497  	config := beacon.TransitionConfigurationV1{
   498  		TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)),
   499  		TerminalBlockHash:       common.Hash{},
   500  		TerminalBlockNumber:     0,
   501  	}
   502  	if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
   503  		t.Fatal("expected error on invalid config, invalid ttd")
   504  	}
   505  	// invalid terminal block hash
   506  	config = beacon.TransitionConfigurationV1{
   507  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   508  		TerminalBlockHash:       common.Hash{1},
   509  		TerminalBlockNumber:     0,
   510  	}
   511  	if _, err := api.ExchangeTransitionConfigurationV1(config); err == nil {
   512  		t.Fatal("expected error on invalid config, invalid hash")
   513  	}
   514  	// valid config
   515  	config = beacon.TransitionConfigurationV1{
   516  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   517  		TerminalBlockHash:       common.Hash{},
   518  		TerminalBlockNumber:     0,
   519  	}
   520  	if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
   521  		t.Fatalf("expected no error on valid config, got %v", err)
   522  	}
   523  	// valid config
   524  	config = beacon.TransitionConfigurationV1{
   525  		TerminalTotalDifficulty: (*hexutil.Big)(genesis.Config.TerminalTotalDifficulty),
   526  		TerminalBlockHash:       preMergeBlocks[5].Hash(),
   527  		TerminalBlockNumber:     6,
   528  	}
   529  	if _, err := api.ExchangeTransitionConfigurationV1(config); err != nil {
   530  		t.Fatalf("expected no error on valid config, got %v", err)
   531  	}
   532  }