github.com/theQRL/go-zond@v0.2.1/zond/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  	"context"
    22  	crand "crypto/rand"
    23  	"fmt"
    24  	"math/big"
    25  	"math/rand"
    26  	"reflect"
    27  	"sync"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/theQRL/go-zond/beacon/engine"
    32  	"github.com/theQRL/go-zond/common"
    33  	"github.com/theQRL/go-zond/common/hexutil"
    34  	beaconConsensus "github.com/theQRL/go-zond/consensus/beacon"
    35  	"github.com/theQRL/go-zond/core"
    36  	"github.com/theQRL/go-zond/core/types"
    37  	"github.com/theQRL/go-zond/crypto"
    38  	"github.com/theQRL/go-zond/crypto/pqcrypto"
    39  	"github.com/theQRL/go-zond/miner"
    40  	"github.com/theQRL/go-zond/node"
    41  	"github.com/theQRL/go-zond/p2p"
    42  	"github.com/theQRL/go-zond/params"
    43  	"github.com/theQRL/go-zond/rpc"
    44  	"github.com/theQRL/go-zond/trie"
    45  	"github.com/theQRL/go-zond/zond"
    46  	"github.com/theQRL/go-zond/zond/downloader"
    47  	"github.com/theQRL/go-zond/zond/zondconfig"
    48  )
    49  
    50  var (
    51  	// testKey is a private key to use for funding a tester account.
    52  	testKey, _ = pqcrypto.HexToDilithium("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    53  
    54  	// testAddr is the Zond address of the tester account.
    55  	testAddr = common.Address(testKey.GetAddress())
    56  
    57  	testBalance = big.NewInt(2e18)
    58  )
    59  
    60  func generateChain(n int) (*core.Genesis, []*types.Block) {
    61  	config := *params.AllBeaconProtocolChanges
    62  	engine := beaconConsensus.NewFaker()
    63  	genesis := &core.Genesis{
    64  		Config: &config,
    65  		Alloc: core.GenesisAlloc{
    66  			testAddr: {Balance: testBalance},
    67  		},
    68  		ExtraData: []byte("test genesis"),
    69  		Timestamp: 9000,
    70  		BaseFee:   big.NewInt(params.InitialBaseFee),
    71  	}
    72  	testNonce := uint64(0)
    73  	generate := func(i int, g *core.BlockGen) {
    74  		g.OffsetTime(5)
    75  		g.SetExtra([]byte("test"))
    76  		to, _ := common.NewAddressFromString("Z9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a")
    77  		tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{
    78  			Nonce:     testNonce,
    79  			To:        &to,
    80  			Value:     big.NewInt(1),
    81  			Gas:       params.TxGas,
    82  			GasFeeCap: big.NewInt(8750000000),
    83  			GasTipCap: big.NewInt(params.GWei),
    84  			Data:      nil}), types.LatestSigner(&config), testKey)
    85  		g.AddTx(tx)
    86  		testNonce++
    87  	}
    88  	_, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, n, generate)
    89  
    90  	return genesis, blocks
    91  }
    92  
    93  func TestEth2AssembleBlock(t *testing.T) {
    94  	genesis, blocks := generateChain(10)
    95  	n, zondservice := startZondService(t, genesis, blocks)
    96  	defer n.Close()
    97  
    98  	api := NewConsensusAPI(zondservice)
    99  	signer := types.NewShanghaiSigner(zondservice.BlockChain().Config().ChainID)
   100  	to := blocks[9].Coinbase()
   101  	tx, err := types.SignTx(types.NewTx(&types.DynamicFeeTx{Nonce: uint64(10), To: &to, Value: big.NewInt(1000), Gas: params.TxGas, GasFeeCap: big.NewInt(875000000), Data: nil}), signer, testKey)
   102  	if err != nil {
   103  		t.Fatalf("error signing transaction, err=%v", err)
   104  	}
   105  	zondservice.TxPool().Add([]*types.Transaction{tx}, true, false)
   106  	blockParams := engine.PayloadAttributes{
   107  		Timestamp: blocks[9].Time() + 5,
   108  	}
   109  	// The miner needs to pick up on the txs in the pool, so a few retries might be
   110  	// needed.
   111  	if _, testErr := assembleWithTransactions(api, blocks[9].Hash(), &blockParams, 1); testErr != nil {
   112  		t.Fatal(testErr)
   113  	}
   114  }
   115  
   116  // assembleWithTransactions tries to assemble a block, retrying until it has 'want',
   117  // number of transactions in it, or it has retried three times.
   118  func assembleWithTransactions(api *ConsensusAPI, parentHash common.Hash, params *engine.PayloadAttributes, want int) (execData *engine.ExecutableData, err error) {
   119  	for retries := 3; retries > 0; retries-- {
   120  		execData, err = assembleBlock(api, parentHash, params)
   121  		if err != nil {
   122  			return nil, err
   123  		}
   124  		if have, want := len(execData.Transactions), want; have != want {
   125  			err = fmt.Errorf("invalid number of transactions, have %d want %d", have, want)
   126  			continue
   127  		}
   128  		return execData, nil
   129  	}
   130  	return nil, err
   131  }
   132  
   133  func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
   134  	genesis, blocks := generateChain(10)
   135  	n, zondservice := startZondService(t, genesis, blocks[:9])
   136  	defer n.Close()
   137  
   138  	api := NewConsensusAPI(zondservice)
   139  
   140  	// Put the 10th block's tx in the pool and produce a new block
   141  	txs := blocks[9].Transactions()
   142  	api.zond.TxPool().Add(txs, false, true)
   143  	blockParams := engine.PayloadAttributes{
   144  		Timestamp: blocks[8].Time() + 5,
   145  	}
   146  	// The miner needs to pick up on the txs in the pool, so a few retries might be
   147  	// needed.
   148  	if _, err := assembleWithTransactions(api, blocks[8].Hash(), &blockParams, blocks[9].Transactions().Len()); err != nil {
   149  		t.Fatal(err)
   150  	}
   151  }
   152  
   153  func TestEth2PrepareAndGetPayload(t *testing.T) {
   154  	genesis, blocks := generateChain(10)
   155  	n, zondservice := startZondService(t, genesis, blocks[:9])
   156  	defer n.Close()
   157  
   158  	api := NewConsensusAPI(zondservice)
   159  
   160  	// Put the 10th block's tx in the pool and produce a new block
   161  	txs := blocks[9].Transactions()
   162  	zondservice.TxPool().Add(txs, true, false)
   163  	blockParams := engine.PayloadAttributes{
   164  		Timestamp:   blocks[8].Time() + 5,
   165  		Withdrawals: []*types.Withdrawal{},
   166  	}
   167  	fcState := engine.ForkchoiceStateV1{
   168  		HeadBlockHash:      blocks[8].Hash(),
   169  		SafeBlockHash:      common.Hash{},
   170  		FinalizedBlockHash: common.Hash{},
   171  	}
   172  	_, err := api.ForkchoiceUpdatedV2(fcState, &blockParams)
   173  	if err != nil {
   174  		t.Fatalf("error preparing payload, err=%v", err)
   175  	}
   176  	// give the payload some time to be built
   177  	time.Sleep(100 * time.Millisecond)
   178  	payloadID := (&miner.BuildPayloadArgs{
   179  		Parent:       fcState.HeadBlockHash,
   180  		Timestamp:    blockParams.Timestamp,
   181  		FeeRecipient: blockParams.SuggestedFeeRecipient,
   182  		Random:       blockParams.Random,
   183  	}).Id()
   184  	execData, err := api.GetPayloadV2(payloadID)
   185  	if err != nil {
   186  		t.Fatalf("error getting payload, err=%v", err)
   187  	}
   188  	if len(execData.ExecutionPayload.Transactions) != blocks[9].Transactions().Len() {
   189  		t.Fatalf("invalid number of transactions %d != 1", len(execData.ExecutionPayload.Transactions))
   190  	}
   191  	// Test invalid payloadID
   192  	var invPayload engine.PayloadID
   193  	copy(invPayload[:], payloadID[:])
   194  	invPayload[0] = ^invPayload[0]
   195  	_, err = api.GetPayloadV2(invPayload)
   196  	if err == nil {
   197  		t.Fatal("expected error retrieving invalid payload")
   198  	}
   199  }
   200  
   201  func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) {
   202  	t.Helper()
   203  
   204  	if len(logsCh) != wantNew {
   205  		t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew)
   206  	}
   207  	if len(rmLogsCh) != wantRemoved {
   208  		t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved)
   209  	}
   210  	// Drain events.
   211  	for i := 0; i < len(logsCh); i++ {
   212  		<-logsCh
   213  	}
   214  	for i := 0; i < len(rmLogsCh); i++ {
   215  		<-rmLogsCh
   216  	}
   217  }
   218  
   219  func TestInvalidPayloadTimestamp(t *testing.T) {
   220  	genesis, blocks := generateChain(10)
   221  	n, zondservice := startZondService(t, genesis, blocks)
   222  	defer n.Close()
   223  	var (
   224  		api    = NewConsensusAPI(zondservice)
   225  		parent = zondservice.BlockChain().CurrentBlock()
   226  	)
   227  	tests := []struct {
   228  		time      uint64
   229  		shouldErr bool
   230  	}{
   231  		{0, true},
   232  		{parent.Time, true},
   233  		{parent.Time - 1, true},
   234  		{parent.Time + 1, false},
   235  		{uint64(time.Now().Unix()) + uint64(time.Minute), false},
   236  	}
   237  
   238  	for i, test := range tests {
   239  		t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) {
   240  			params := engine.PayloadAttributes{
   241  				Timestamp:             test.time,
   242  				Random:                crypto.Keccak256Hash([]byte{byte(123)}),
   243  				SuggestedFeeRecipient: parent.Coinbase,
   244  				Withdrawals:           []*types.Withdrawal{},
   245  			}
   246  			fcState := engine.ForkchoiceStateV1{
   247  				HeadBlockHash:      parent.Hash(),
   248  				SafeBlockHash:      common.Hash{},
   249  				FinalizedBlockHash: common.Hash{},
   250  			}
   251  			_, err := api.ForkchoiceUpdatedV2(fcState, &params)
   252  			if test.shouldErr && err == nil {
   253  				t.Fatalf("expected error preparing payload with invalid timestamp, err=%v", err)
   254  			} else if !test.shouldErr && err != nil {
   255  				t.Fatalf("error preparing payload with valid timestamp, err=%v", err)
   256  			}
   257  		})
   258  	}
   259  }
   260  
   261  func TestEth2NewBlock(t *testing.T) {
   262  	genesis, blocks := generateChain(10)
   263  	n, zondservice := startZondService(t, genesis, blocks)
   264  	defer n.Close()
   265  
   266  	var (
   267  		api    = NewConsensusAPI(zondservice)
   268  		parent = blocks[len(blocks)-1]
   269  
   270  		// This ZVM code generates a log when the contract is created.
   271  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   272  	)
   273  	// The event channels.
   274  	newLogCh := make(chan []*types.Log, 10)
   275  	rmLogsCh := make(chan core.RemovedLogsEvent, 10)
   276  	zondservice.BlockChain().SubscribeLogsEvent(newLogCh)
   277  	zondservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh)
   278  
   279  	for i := 0; i < 10; i++ {
   280  		statedb, _ := zondservice.BlockChain().StateAt(parent.Root())
   281  		nonce := statedb.GetNonce(testAddr)
   282  		signer := types.LatestSigner(zondservice.BlockChain().Config())
   283  		tx := types.NewTx(&types.DynamicFeeTx{
   284  			Nonce:     nonce,
   285  			Value:     new(big.Int),
   286  			Gas:       1000000,
   287  			GasFeeCap: big.NewInt(2 * params.InitialBaseFee),
   288  			Data:      logCode,
   289  		})
   290  		signedTx, _ := types.SignTx(tx, signer, testKey)
   291  		zondservice.TxPool().Add([]*types.Transaction{signedTx}, true, false)
   292  
   293  		execData, err := assembleWithTransactions(api, parent.Hash(), &engine.PayloadAttributes{
   294  			Timestamp: parent.Time() + 5,
   295  		}, 1)
   296  		if err != nil {
   297  			t.Fatalf("Failed to create the executable data %v", err)
   298  		}
   299  		block, err := engine.ExecutableDataToBlock(*execData)
   300  		if err != nil {
   301  			t.Fatalf("Failed to convert executable data to block %v", err)
   302  		}
   303  		newResp, err := api.NewPayloadV2(*execData)
   304  		switch {
   305  		case err != nil:
   306  			t.Fatalf("Failed to insert block: %v", err)
   307  		case newResp.Status != "VALID":
   308  			t.Fatalf("Failed to insert block: %v", newResp.Status)
   309  		case zondservice.BlockChain().CurrentBlock().Number.Uint64() != block.NumberU64()-1:
   310  			t.Fatalf("Chain head shouldn't be updated")
   311  		}
   312  		checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
   313  		fcState := engine.ForkchoiceStateV1{
   314  			HeadBlockHash:      block.Hash(),
   315  			SafeBlockHash:      block.Hash(),
   316  			FinalizedBlockHash: block.Hash(),
   317  		}
   318  		if _, err := api.ForkchoiceUpdatedV2(fcState, nil); err != nil {
   319  			t.Fatalf("Failed to insert block: %v", err)
   320  		}
   321  		if have, want := zondservice.BlockChain().CurrentBlock().Number.Uint64(), block.NumberU64(); have != want {
   322  			t.Fatalf("Chain head should be updated, have %d want %d", have, want)
   323  		}
   324  		checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
   325  
   326  		parent = block
   327  	}
   328  
   329  	// Introduce fork chain
   330  	var (
   331  		head = zondservice.BlockChain().CurrentBlock().Number.Uint64()
   332  	)
   333  	parent = blocks[len(blocks)-1]
   334  	for i := 0; i < 10; i++ {
   335  		execData, err := assembleBlock(api, parent.Hash(), &engine.PayloadAttributes{
   336  			Timestamp: parent.Time() + 6,
   337  		})
   338  		if err != nil {
   339  			t.Fatalf("Failed to create the executable data %v", err)
   340  		}
   341  		block, err := engine.ExecutableDataToBlock(*execData)
   342  		if err != nil {
   343  			t.Fatalf("Failed to convert executable data to block %v", err)
   344  		}
   345  		newResp, err := api.NewPayloadV2(*execData)
   346  		if err != nil || newResp.Status != "VALID" {
   347  			t.Fatalf("Failed to insert block: %v", err)
   348  		}
   349  		if zondservice.BlockChain().CurrentBlock().Number.Uint64() != head {
   350  			t.Fatalf("Chain head shouldn't be updated")
   351  		}
   352  
   353  		fcState := engine.ForkchoiceStateV1{
   354  			HeadBlockHash:      block.Hash(),
   355  			SafeBlockHash:      block.Hash(),
   356  			FinalizedBlockHash: block.Hash(),
   357  		}
   358  		if _, err := api.ForkchoiceUpdatedV2(fcState, nil); err != nil {
   359  			t.Fatalf("Failed to insert block: %v", err)
   360  		}
   361  		if zondservice.BlockChain().CurrentBlock().Number.Uint64() != block.NumberU64() {
   362  			t.Fatalf("Chain head should be updated")
   363  		}
   364  		parent, head = block, block.NumberU64()
   365  	}
   366  }
   367  
   368  func TestEth2DeepReorg(t *testing.T) {
   369  	// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
   370  	// before the totalTerminalDifficulty threshold
   371  	/*
   372  		genesis, blocks := generateChain(core.TriesInMemory * 2)
   373  		n, zondservice := startZondService(t, genesis, blocks)
   374  		defer n.Close()
   375  
   376  		var (
   377  			api    = NewConsensusAPI(zondservice)
   378  			parent = blocks[len(blocks)-core.TriesInMemory-1]
   379  			head   = zondservice.BlockChain().CurrentBlock().Number.Uint64()
   380  		)
   381  		if zondservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) {
   382  			t.Errorf("Block %d not pruned", parent.NumberU64())
   383  		}
   384  		for i := 0; i < 10; i++ {
   385  			execData, err := api.assembleBlock(AssembleBlockParams{
   386  				ParentHash: parent.Hash(),
   387  				Timestamp:  parent.Time() + 5,
   388  			})
   389  			if err != nil {
   390  				t.Fatalf("Failed to create the executable data %v", err)
   391  			}
   392  			block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData)
   393  			if err != nil {
   394  				t.Fatalf("Failed to convert executable data to block %v", err)
   395  			}
   396  			newResp, err := api.ExecutePayload(*execData)
   397  			if err != nil || newResp.Status != "VALID" {
   398  				t.Fatalf("Failed to insert block: %v", err)
   399  			}
   400  			if zondservice.BlockChain().CurrentBlock().Number.Uint64() != head {
   401  				t.Fatalf("Chain head shouldn't be updated")
   402  			}
   403  			if err := api.setHead(block.Hash()); err != nil {
   404  				t.Fatalf("Failed to set head: %v", err)
   405  			}
   406  			if zondservice.BlockChain().CurrentBlock().Number.Uint64() != block.NumberU64() {
   407  				t.Fatalf("Chain head should be updated")
   408  			}
   409  			parent, head = block, block.NumberU64()
   410  		}
   411  	*/
   412  }
   413  
   414  // startZondService creates a full node instance for testing.
   415  func startZondService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *zond.Zond) {
   416  	t.Helper()
   417  
   418  	n, err := node.New(&node.Config{
   419  		P2P: p2p.Config{
   420  			ListenAddr:  "0.0.0.0:0",
   421  			NoDiscovery: true,
   422  			MaxPeers:    25,
   423  		}})
   424  	if err != nil {
   425  		t.Fatal("can't create node:", err)
   426  	}
   427  
   428  	mcfg := miner.DefaultConfig
   429  	mcfg.PendingFeeRecipient = testAddr
   430  	zondcfg := &zondconfig.Config{Genesis: genesis, SyncMode: downloader.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: mcfg}
   431  	zondservice, err := zond.New(n, zondcfg)
   432  	if err != nil {
   433  		t.Fatal("can't create zond service:", err)
   434  	}
   435  	if err := n.Start(); err != nil {
   436  		t.Fatal("can't start node:", err)
   437  	}
   438  	if _, err := zondservice.BlockChain().InsertChain(blocks); err != nil {
   439  		n.Close()
   440  		t.Fatal("can't import test blocks:", err)
   441  	}
   442  
   443  	zondservice.SetSynced()
   444  	return n, zondservice
   445  }
   446  
   447  func TestFullAPI(t *testing.T) {
   448  	genesis, blocks := generateChain(10)
   449  	n, zondservice := startZondService(t, genesis, blocks)
   450  	defer n.Close()
   451  	var (
   452  		parent = zondservice.BlockChain().CurrentBlock()
   453  		// This ZVM code generates a log when the contract is created.
   454  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   455  	)
   456  
   457  	callback := func(parent *types.Header) {
   458  		statedb, _ := zondservice.BlockChain().StateAt(parent.Root)
   459  		nonce := statedb.GetNonce(testAddr)
   460  		signer := types.LatestSigner(zondservice.BlockChain().Config())
   461  		tx := types.NewTx(&types.DynamicFeeTx{
   462  			Nonce: nonce,
   463  			Value: new(big.Int),
   464  			Gas:   1000000,
   465  			Data:  logCode,
   466  		})
   467  		signedTx, _ := types.SignTx(tx, signer, testKey)
   468  		zondservice.TxPool().Add([]*types.Transaction{signedTx}, true, false)
   469  	}
   470  
   471  	setupBlocks(t, zondservice, 10, parent, callback, nil)
   472  }
   473  
   474  func setupBlocks(t *testing.T, zondservice *zond.Zond, n int, parent *types.Header, callback func(parent *types.Header), withdrawals [][]*types.Withdrawal) []*types.Header {
   475  	api := NewConsensusAPI(zondservice)
   476  	var blocks []*types.Header
   477  	for i := 0; i < n; i++ {
   478  		callback(parent)
   479  		var w []*types.Withdrawal
   480  		if withdrawals != nil {
   481  			w = withdrawals[i]
   482  		}
   483  
   484  		payload := getNewPayload(t, api, parent, w)
   485  		execResp, err := api.NewPayloadV2(*payload)
   486  		if err != nil {
   487  			t.Fatalf("can't execute payload: %v", err)
   488  		}
   489  		if execResp.Status != engine.VALID {
   490  			t.Fatalf("invalid status: %v", execResp.Status)
   491  		}
   492  		fcState := engine.ForkchoiceStateV1{
   493  			HeadBlockHash:      payload.BlockHash,
   494  			SafeBlockHash:      payload.ParentHash,
   495  			FinalizedBlockHash: payload.ParentHash,
   496  		}
   497  		if _, err := api.ForkchoiceUpdatedV2(fcState, nil); err != nil {
   498  			t.Fatalf("Failed to insert block: %v", err)
   499  		}
   500  		if zondservice.BlockChain().CurrentBlock().Number.Uint64() != payload.Number {
   501  			t.Fatal("Chain head should be updated")
   502  		}
   503  		if zondservice.BlockChain().CurrentFinalBlock().Number.Uint64() != payload.Number-1 {
   504  			t.Fatal("Finalized block should be updated")
   505  		}
   506  		parent = zondservice.BlockChain().CurrentBlock()
   507  		blocks = append(blocks, parent)
   508  	}
   509  	return blocks
   510  }
   511  
   512  /*
   513  TestNewPayloadOnInvalidChain sets up a valid chain and tries to feed blocks
   514  from an invalid chain to test if latestValidHash (LVH) works correctly.
   515  
   516  We set up the following chain where P1 ... Pn and P1” are valid while
   517  P1' is invalid.
   518  We expect
   519  (1) The LVH to point to the current inserted payload if it was valid.
   520  (2) The LVH to point to the valid parent on an invalid payload (if the parent is available).
   521  (3) If the parent is unavailable, the LVH should not be set.
   522  
   523  	CommonAncestor◄─▲── P1 ◄── P2  ◄─ P3  ◄─ ... ◄─ Pn
   524  	                │
   525  	                └── P1' ◄─ P2' ◄─ P3' ◄─ ... ◄─ Pn'
   526  	                │
   527  	                └── P1''
   528  */
   529  func TestNewPayloadOnInvalidChain(t *testing.T) {
   530  	genesis, blocks := generateChain(10)
   531  	n, zondservice := startZondService(t, genesis, blocks)
   532  	defer n.Close()
   533  
   534  	var (
   535  		api    = NewConsensusAPI(zondservice)
   536  		parent = zondservice.BlockChain().CurrentBlock()
   537  		signer = types.LatestSigner(zondservice.BlockChain().Config())
   538  		// This ZVM code generates a log when the contract is created.
   539  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   540  	)
   541  	for i := 0; i < 10; i++ {
   542  		statedb, _ := zondservice.BlockChain().StateAt(parent.Root)
   543  		tx := types.MustSignNewTx(testKey, signer, &types.DynamicFeeTx{
   544  			Nonce:     statedb.GetNonce(testAddr),
   545  			Value:     new(big.Int),
   546  			Gas:       1000000,
   547  			GasFeeCap: big.NewInt(2 * params.InitialBaseFee),
   548  			GasTipCap: big.NewInt(params.GWei),
   549  			Data:      logCode,
   550  		})
   551  		zondservice.TxPool().Add([]*types.Transaction{tx}, false, true)
   552  		var (
   553  			params = engine.PayloadAttributes{
   554  				Timestamp:             parent.Time + 1,
   555  				Random:                crypto.Keccak256Hash([]byte{byte(i)}),
   556  				SuggestedFeeRecipient: parent.Coinbase,
   557  				Withdrawals:           []*types.Withdrawal{},
   558  			}
   559  			fcState = engine.ForkchoiceStateV1{
   560  				HeadBlockHash:      parent.Hash(),
   561  				SafeBlockHash:      common.Hash{},
   562  				FinalizedBlockHash: common.Hash{},
   563  			}
   564  			payload *engine.ExecutionPayloadEnvelope
   565  			resp    engine.ForkChoiceResponse
   566  			err     error
   567  		)
   568  		for i := 0; ; i++ {
   569  			if resp, err = api.ForkchoiceUpdatedV2(fcState, &params); err != nil {
   570  				t.Fatalf("error preparing payload, err=%v", err)
   571  			}
   572  			if resp.PayloadStatus.Status != engine.VALID {
   573  				t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status)
   574  			}
   575  			// give the payload some time to be built
   576  			time.Sleep(50 * time.Millisecond)
   577  			if payload, err = api.GetPayloadV2(*resp.PayloadID); err != nil {
   578  				t.Fatalf("can't get payload: %v", err)
   579  			}
   580  			if len(payload.ExecutionPayload.Transactions) > 0 {
   581  				break
   582  			}
   583  			// No luck this time we need to update the params and try again.
   584  			params.Timestamp = params.Timestamp + 1
   585  			if i > 10 {
   586  				t.Fatalf("payload should not be empty")
   587  			}
   588  		}
   589  		execResp, err := api.NewPayloadV2(*payload.ExecutionPayload)
   590  		if err != nil {
   591  			t.Fatalf("can't execute payload: %v", err)
   592  		}
   593  		if execResp.Status != engine.VALID {
   594  			t.Fatalf("invalid status: %v", execResp.Status)
   595  		}
   596  		fcState = engine.ForkchoiceStateV1{
   597  			HeadBlockHash:      payload.ExecutionPayload.BlockHash,
   598  			SafeBlockHash:      payload.ExecutionPayload.ParentHash,
   599  			FinalizedBlockHash: payload.ExecutionPayload.ParentHash,
   600  		}
   601  		if _, err := api.ForkchoiceUpdatedV2(fcState, nil); err != nil {
   602  			t.Fatalf("Failed to insert block: %v", err)
   603  		}
   604  		if zondservice.BlockChain().CurrentBlock().Number.Uint64() != payload.ExecutionPayload.Number {
   605  			t.Fatalf("Chain head should be updated")
   606  		}
   607  		parent = zondservice.BlockChain().CurrentBlock()
   608  	}
   609  }
   610  
   611  func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *engine.PayloadAttributes) (*engine.ExecutableData, error) {
   612  	args := &miner.BuildPayloadArgs{
   613  		Parent:       parentHash,
   614  		Timestamp:    params.Timestamp,
   615  		FeeRecipient: params.SuggestedFeeRecipient,
   616  		Random:       params.Random,
   617  		Withdrawals:  params.Withdrawals,
   618  	}
   619  	payload, err := api.zond.Miner().BuildPayload(args)
   620  	if err != nil {
   621  		return nil, err
   622  	}
   623  	return payload.ResolveFull().ExecutionPayload, nil
   624  }
   625  
   626  func TestEmptyBlocks(t *testing.T) {
   627  	genesis, blocks := generateChain(10)
   628  	n, zondservice := startZondService(t, genesis, blocks)
   629  	defer n.Close()
   630  
   631  	commonAncestor := zondservice.BlockChain().CurrentBlock()
   632  	api := NewConsensusAPI(zondservice)
   633  
   634  	// Setup 10 blocks on the canonical chain
   635  	setupBlocks(t, zondservice, 10, commonAncestor, func(parent *types.Header) {}, nil)
   636  
   637  	// (1) check LatestValidHash by sending a normal payload (P1'')
   638  	payload := getNewPayload(t, api, commonAncestor, nil)
   639  
   640  	status, err := api.NewPayloadV2(*payload)
   641  	if err != nil {
   642  		t.Fatal(err)
   643  	}
   644  	if status.Status != engine.VALID {
   645  		t.Errorf("invalid status: expected VALID got: %v", status.Status)
   646  	}
   647  	if !bytes.Equal(status.LatestValidHash[:], payload.BlockHash[:]) {
   648  		t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, payload.BlockHash)
   649  	}
   650  
   651  	// (2) Now send P1' which is invalid
   652  	payload = getNewPayload(t, api, commonAncestor, nil)
   653  	payload.GasUsed += 1
   654  	payload = setBlockhash(payload)
   655  	// Now latestValidHash should be the common ancestor
   656  	status, err = api.NewPayloadV2(*payload)
   657  	if err != nil {
   658  		t.Fatal(err)
   659  	}
   660  	if status.Status != engine.INVALID {
   661  		t.Errorf("invalid status: expected INVALID got: %v", status.Status)
   662  	}
   663  	// Expect parent header hash on INVALID block on top of PoS block
   664  	expected := commonAncestor.Hash()
   665  	if !bytes.Equal(status.LatestValidHash[:], expected[:]) {
   666  		t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, expected)
   667  	}
   668  
   669  	// (3) Now send a payload with unknown parent
   670  	payload = getNewPayload(t, api, commonAncestor, nil)
   671  	payload.ParentHash = common.Hash{1}
   672  	payload = setBlockhash(payload)
   673  	// Now latestValidHash should be the common ancestor
   674  	status, err = api.NewPayloadV2(*payload)
   675  	if err != nil {
   676  		t.Fatal(err)
   677  	}
   678  	if status.Status != engine.SYNCING {
   679  		t.Errorf("invalid status: expected SYNCING got: %v", status.Status)
   680  	}
   681  	if status.LatestValidHash != nil {
   682  		t.Fatalf("invalid LVH: got %v wanted nil", status.LatestValidHash)
   683  	}
   684  }
   685  
   686  func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Header, withdrawals []*types.Withdrawal) *engine.ExecutableData {
   687  	params := engine.PayloadAttributes{
   688  		Timestamp:             parent.Time + 1,
   689  		Random:                crypto.Keccak256Hash([]byte{byte(1)}),
   690  		SuggestedFeeRecipient: parent.Coinbase,
   691  		Withdrawals:           withdrawals,
   692  	}
   693  
   694  	payload, err := assembleBlock(api, parent.Hash(), &params)
   695  	if err != nil {
   696  		t.Fatal(err)
   697  	}
   698  	return payload
   699  }
   700  
   701  // setBlockhash sets the blockhash of a modified ExecutableData.
   702  // Can be used to make modified payloads look valid.
   703  func setBlockhash(data *engine.ExecutableData) *engine.ExecutableData {
   704  	txs, _ := decodeTransactions(data.Transactions)
   705  	number := big.NewInt(0)
   706  	number.SetUint64(data.Number)
   707  	header := &types.Header{
   708  		ParentHash:      data.ParentHash,
   709  		Coinbase:        data.FeeRecipient,
   710  		Root:            data.StateRoot,
   711  		TxHash:          types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
   712  		ReceiptHash:     data.ReceiptsRoot,
   713  		Bloom:           types.BytesToBloom(data.LogsBloom),
   714  		Number:          number,
   715  		GasLimit:        data.GasLimit,
   716  		GasUsed:         data.GasUsed,
   717  		Time:            data.Timestamp,
   718  		BaseFee:         data.BaseFeePerGas,
   719  		Extra:           data.ExtraData,
   720  		Random:          data.Random,
   721  		WithdrawalsHash: &types.EmptyWithdrawalsHash,
   722  	}
   723  	block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs})
   724  	data.BlockHash = block.Hash()
   725  	return data
   726  }
   727  
   728  func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
   729  	var txs = make([]*types.Transaction, len(enc))
   730  	for i, encTx := range enc {
   731  		var tx types.Transaction
   732  		if err := tx.UnmarshalBinary(encTx); err != nil {
   733  			return nil, fmt.Errorf("invalid transaction %d: %v", i, err)
   734  		}
   735  		txs[i] = &tx
   736  	}
   737  	return txs, nil
   738  }
   739  
   740  func TestTrickRemoteBlockCache(t *testing.T) {
   741  	// Setup two nodes
   742  	genesis, blocks := generateChain(10)
   743  	nodeA, zondserviceA := startZondService(t, genesis, blocks)
   744  	nodeB, zondserviceB := startZondService(t, genesis, blocks)
   745  	defer nodeA.Close()
   746  	defer nodeB.Close()
   747  	for nodeB.Server().NodeInfo().Ports.Listener == 0 {
   748  		time.Sleep(250 * time.Millisecond)
   749  	}
   750  	nodeA.Server().AddPeer(nodeB.Server().Self())
   751  	nodeB.Server().AddPeer(nodeA.Server().Self())
   752  	apiA := NewConsensusAPI(zondserviceA)
   753  	apiB := NewConsensusAPI(zondserviceB)
   754  
   755  	commonAncestor := zondserviceA.BlockChain().CurrentBlock()
   756  
   757  	// Setup 10 blocks on the canonical chain
   758  	setupBlocks(t, zondserviceA, 10, commonAncestor, func(parent *types.Header) {}, nil)
   759  	commonAncestor = zondserviceA.BlockChain().CurrentBlock()
   760  
   761  	var invalidChain []*engine.ExecutableData
   762  	// create a valid payload (P1)
   763  	//payload1 := getNewPayload(t, apiA, commonAncestor)
   764  	//invalidChain = append(invalidChain, payload1)
   765  
   766  	// create an invalid payload2 (P2)
   767  	payload2 := getNewPayload(t, apiA, commonAncestor, nil)
   768  	//payload2.ParentHash = payload1.BlockHash
   769  	payload2.GasUsed += 1
   770  	payload2 = setBlockhash(payload2)
   771  	invalidChain = append(invalidChain, payload2)
   772  
   773  	head := payload2
   774  	// create some valid payloads on top
   775  	for i := 0; i < 10; i++ {
   776  		payload := getNewPayload(t, apiA, commonAncestor, nil)
   777  		payload.ParentHash = head.BlockHash
   778  		payload = setBlockhash(payload)
   779  		invalidChain = append(invalidChain, payload)
   780  		head = payload
   781  	}
   782  
   783  	// feed the payloads to node B
   784  	for _, payload := range invalidChain {
   785  		status, err := apiB.NewPayloadV2(*payload)
   786  		if err != nil {
   787  			panic(err)
   788  		}
   789  		if status.Status == engine.VALID {
   790  			t.Error("invalid status: VALID on an invalid chain")
   791  		}
   792  		// Now reorg to the head of the invalid chain
   793  		resp, err := apiB.ForkchoiceUpdatedV2(engine.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil)
   794  		if err != nil {
   795  			t.Fatal(err)
   796  		}
   797  		if resp.PayloadStatus.Status == engine.VALID {
   798  			t.Error("invalid status: VALID on an invalid chain")
   799  		}
   800  		time.Sleep(100 * time.Millisecond)
   801  	}
   802  }
   803  
   804  func TestInvalidBloom(t *testing.T) {
   805  	genesis, blocks := generateChain(10)
   806  	n, zondservice := startZondService(t, genesis, blocks)
   807  	defer n.Close()
   808  
   809  	commonAncestor := zondservice.BlockChain().CurrentBlock()
   810  	api := NewConsensusAPI(zondservice)
   811  
   812  	// Setup 10 blocks on the canonical chain
   813  	setupBlocks(t, zondservice, 10, commonAncestor, func(parent *types.Header) {}, nil)
   814  
   815  	// (1) check LatestValidHash by sending a normal payload (P1'')
   816  	payload := getNewPayload(t, api, commonAncestor, nil)
   817  	payload.LogsBloom = append(payload.LogsBloom, byte(1))
   818  	status, err := api.NewPayloadV2(*payload)
   819  	if err != nil {
   820  		t.Fatal(err)
   821  	}
   822  	if status.Status != engine.INVALID {
   823  		t.Errorf("invalid status: expected INVALID got: %v", status.Status)
   824  	}
   825  }
   826  
   827  // TestSimultaneousNewBlock does several parallel inserts, both as
   828  // newPayLoad and forkchoiceUpdate. This is to test that the api behaves
   829  // well even of the caller is not being 'serial'.
   830  func TestSimultaneousNewBlock(t *testing.T) {
   831  	genesis, blocks := generateChain(10)
   832  	n, zondservice := startZondService(t, genesis, blocks)
   833  	defer n.Close()
   834  
   835  	var (
   836  		api    = NewConsensusAPI(zondservice)
   837  		parent = blocks[len(blocks)-1]
   838  	)
   839  	for i := 0; i < 10; i++ {
   840  		execData, err := assembleBlock(api, parent.Hash(), &engine.PayloadAttributes{
   841  			Timestamp: parent.Time() + 5,
   842  		})
   843  		if err != nil {
   844  			t.Fatalf("Failed to create the executable data %v", err)
   845  		}
   846  		// Insert it 10 times in parallel. Should be ignored.
   847  		{
   848  			var (
   849  				wg      sync.WaitGroup
   850  				testErr error
   851  				errMu   sync.Mutex
   852  			)
   853  			wg.Add(10)
   854  			for ii := 0; ii < 10; ii++ {
   855  				go func() {
   856  					defer wg.Done()
   857  					if newResp, err := api.NewPayloadV2(*execData); err != nil {
   858  						errMu.Lock()
   859  						testErr = fmt.Errorf("failed to insert block: %w", err)
   860  						errMu.Unlock()
   861  					} else if newResp.Status != "VALID" {
   862  						errMu.Lock()
   863  						testErr = fmt.Errorf("failed to insert block: %v", newResp.Status)
   864  						errMu.Unlock()
   865  					}
   866  				}()
   867  			}
   868  			wg.Wait()
   869  			if testErr != nil {
   870  				t.Fatal(testErr)
   871  			}
   872  		}
   873  		block, err := engine.ExecutableDataToBlock(*execData)
   874  		if err != nil {
   875  			t.Fatalf("Failed to convert executable data to block %v", err)
   876  		}
   877  		if zondservice.BlockChain().CurrentBlock().Number.Uint64() != block.NumberU64()-1 {
   878  			t.Fatalf("Chain head shouldn't be updated")
   879  		}
   880  		fcState := engine.ForkchoiceStateV1{
   881  			HeadBlockHash:      block.Hash(),
   882  			SafeBlockHash:      block.Hash(),
   883  			FinalizedBlockHash: block.Hash(),
   884  		}
   885  		{
   886  			var (
   887  				wg      sync.WaitGroup
   888  				testErr error
   889  				errMu   sync.Mutex
   890  			)
   891  			wg.Add(10)
   892  			// Do each FCU 10 times
   893  			for ii := 0; ii < 10; ii++ {
   894  				go func() {
   895  					defer wg.Done()
   896  					if _, err := api.ForkchoiceUpdatedV2(fcState, nil); err != nil {
   897  						errMu.Lock()
   898  						testErr = fmt.Errorf("failed to insert block: %w", err)
   899  						errMu.Unlock()
   900  					}
   901  				}()
   902  			}
   903  			wg.Wait()
   904  			if testErr != nil {
   905  				t.Fatal(testErr)
   906  			}
   907  		}
   908  		if have, want := zondservice.BlockChain().CurrentBlock().Number.Uint64(), block.NumberU64(); have != want {
   909  			t.Fatalf("Chain head should be updated, have %d want %d", have, want)
   910  		}
   911  		parent = block
   912  	}
   913  }
   914  
   915  // TestWithdrawals creates and verifies two post-Shanghai blocks. The first
   916  // includes zero withdrawals and the second includes two.
   917  func TestWithdrawals(t *testing.T) {
   918  	genesis, blocks := generateChain(10)
   919  
   920  	n, zondservice := startZondService(t, genesis, blocks)
   921  	defer n.Close()
   922  
   923  	api := NewConsensusAPI(zondservice)
   924  
   925  	// 10: Build Shanghai block with no withdrawals.
   926  	parent := zondservice.BlockChain().CurrentHeader()
   927  	blockParams := engine.PayloadAttributes{
   928  		Timestamp:   parent.Time + 5,
   929  		Withdrawals: make([]*types.Withdrawal, 0),
   930  	}
   931  	fcState := engine.ForkchoiceStateV1{
   932  		HeadBlockHash: parent.Hash(),
   933  	}
   934  	resp, err := api.ForkchoiceUpdatedV2(fcState, &blockParams)
   935  	if err != nil {
   936  		t.Fatalf("error preparing payload, err=%v", err)
   937  	}
   938  	if resp.PayloadStatus.Status != engine.VALID {
   939  		t.Fatalf("unexpected status (got: %s, want: %s)", resp.PayloadStatus.Status, engine.VALID)
   940  	}
   941  
   942  	// 10: verify state root is the same as parent
   943  	payloadID := (&miner.BuildPayloadArgs{
   944  		Parent:       fcState.HeadBlockHash,
   945  		Timestamp:    blockParams.Timestamp,
   946  		FeeRecipient: blockParams.SuggestedFeeRecipient,
   947  		Random:       blockParams.Random,
   948  		Withdrawals:  blockParams.Withdrawals,
   949  	}).Id()
   950  	execData, err := api.GetPayloadV2(payloadID)
   951  	if err != nil {
   952  		t.Fatalf("error getting payload, err=%v", err)
   953  	}
   954  	if execData.ExecutionPayload.StateRoot != parent.Root {
   955  		t.Fatalf("mismatch state roots (got: %s, want: %s)", execData.ExecutionPayload.StateRoot, blocks[8].Root())
   956  	}
   957  
   958  	// 10: verify locally built block
   959  	if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
   960  		t.Fatalf("error validating payload: %v", err)
   961  	} else if status.Status != engine.VALID {
   962  		t.Fatalf("invalid payload")
   963  	}
   964  
   965  	// 11: build shanghai block with withdrawal
   966  	aa := common.Address{0xaa}
   967  	bb := common.Address{0xbb}
   968  	blockParams = engine.PayloadAttributes{
   969  		Timestamp: execData.ExecutionPayload.Timestamp + 5,
   970  		Withdrawals: []*types.Withdrawal{
   971  			{
   972  				Index:   0,
   973  				Address: aa,
   974  				Amount:  32,
   975  			},
   976  			{
   977  				Index:   1,
   978  				Address: bb,
   979  				Amount:  33,
   980  			},
   981  		},
   982  	}
   983  	fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
   984  	_, err = api.ForkchoiceUpdatedV2(fcState, &blockParams)
   985  	if err != nil {
   986  		t.Fatalf("error preparing payload, err=%v", err)
   987  	}
   988  
   989  	// 11: verify locally build block.
   990  	payloadID = (&miner.BuildPayloadArgs{
   991  		Parent:       fcState.HeadBlockHash,
   992  		Timestamp:    blockParams.Timestamp,
   993  		FeeRecipient: blockParams.SuggestedFeeRecipient,
   994  		Random:       blockParams.Random,
   995  		Withdrawals:  blockParams.Withdrawals,
   996  	}).Id()
   997  	execData, err = api.GetPayloadV2(payloadID)
   998  	if err != nil {
   999  		t.Fatalf("error getting payload, err=%v", err)
  1000  	}
  1001  	if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
  1002  		t.Fatalf("error validating payload: %v", err)
  1003  	} else if status.Status != engine.VALID {
  1004  		t.Fatalf("invalid payload")
  1005  	}
  1006  
  1007  	// 11: set block as head.
  1008  	fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
  1009  	_, err = api.ForkchoiceUpdatedV2(fcState, nil)
  1010  	if err != nil {
  1011  		t.Fatalf("error preparing payload, err=%v", err)
  1012  	}
  1013  
  1014  	// 11: verify withdrawals were processed.
  1015  	db, _, err := zondservice.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(execData.ExecutionPayload.Number))
  1016  	if err != nil {
  1017  		t.Fatalf("unable to load db: %v", err)
  1018  	}
  1019  	for i, w := range blockParams.Withdrawals {
  1020  		// w.Amount is in gwei, balance in wei
  1021  		if db.GetBalance(w.Address).Uint64() != w.Amount*params.GWei {
  1022  			t.Fatalf("failed to process withdrawal %d", i)
  1023  		}
  1024  	}
  1025  }
  1026  
  1027  func TestNilWithdrawals(t *testing.T) {
  1028  	genesis, blocks := generateChain(10)
  1029  
  1030  	n, zondservice := startZondService(t, genesis, blocks)
  1031  	defer n.Close()
  1032  
  1033  	api := NewConsensusAPI(zondservice)
  1034  	parent := zondservice.BlockChain().CurrentHeader()
  1035  	aa := common.Address{0xaa}
  1036  
  1037  	type test struct {
  1038  		blockParams engine.PayloadAttributes
  1039  		wantErr     bool
  1040  	}
  1041  	tests := []test{
  1042  		{
  1043  			blockParams: engine.PayloadAttributes{
  1044  				Timestamp:   parent.Time + 5,
  1045  				Withdrawals: nil,
  1046  			},
  1047  			wantErr: true,
  1048  		},
  1049  		{
  1050  			blockParams: engine.PayloadAttributes{
  1051  				Timestamp:   parent.Time + 5,
  1052  				Withdrawals: make([]*types.Withdrawal, 0),
  1053  			},
  1054  			wantErr: false,
  1055  		},
  1056  		{
  1057  			blockParams: engine.PayloadAttributes{
  1058  				Timestamp: parent.Time + 5,
  1059  				Withdrawals: []*types.Withdrawal{
  1060  					{
  1061  						Index:   0,
  1062  						Address: aa,
  1063  						Amount:  32,
  1064  					},
  1065  				},
  1066  			},
  1067  			wantErr: false,
  1068  		},
  1069  	}
  1070  
  1071  	fcState := engine.ForkchoiceStateV1{
  1072  		HeadBlockHash: parent.Hash(),
  1073  	}
  1074  
  1075  	for _, test := range tests {
  1076  		_, err := api.ForkchoiceUpdatedV2(fcState, &test.blockParams)
  1077  		if test.wantErr {
  1078  			if err == nil {
  1079  				t.Fatal("wanted error on fcuv2 with invalid withdrawals")
  1080  			}
  1081  			continue
  1082  		}
  1083  		if err != nil {
  1084  			t.Fatalf("error preparing payload, err=%v", err)
  1085  		}
  1086  
  1087  		// 11: verify locally build block.
  1088  		payloadID := (&miner.BuildPayloadArgs{
  1089  			Parent:       fcState.HeadBlockHash,
  1090  			Timestamp:    test.blockParams.Timestamp,
  1091  			FeeRecipient: test.blockParams.SuggestedFeeRecipient,
  1092  			Random:       test.blockParams.Random,
  1093  		}).Id()
  1094  		execData, err := api.GetPayloadV2(payloadID)
  1095  		if err != nil {
  1096  			t.Fatalf("error getting payload, err=%v", err)
  1097  		}
  1098  		if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
  1099  			t.Fatalf("error validating payload: %v", err.(*engine.EngineAPIError).ErrorData())
  1100  		} else if status.Status != engine.VALID {
  1101  			t.Fatalf("invalid payload")
  1102  		}
  1103  	}
  1104  }
  1105  
  1106  func setupBodies(t *testing.T) (*node.Node, *zond.Zond, []*types.Block) {
  1107  	genesis, blocks := generateChain(10)
  1108  	n, zondservice := startZondService(t, genesis, blocks)
  1109  
  1110  	var (
  1111  		parent = zondservice.BlockChain().CurrentBlock()
  1112  		// This ZVM code generates a log when the contract is created.
  1113  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
  1114  	)
  1115  
  1116  	callback := func(parent *types.Header) {
  1117  		statedb, _ := zondservice.BlockChain().StateAt(parent.Root)
  1118  		nonce := statedb.GetNonce(testAddr)
  1119  		signer := types.LatestSigner(zondservice.BlockChain().Config())
  1120  		tx := types.NewTx(&types.DynamicFeeTx{
  1121  			Nonce: nonce,
  1122  			Value: new(big.Int),
  1123  			Gas:   1000000,
  1124  			Data:  logCode,
  1125  		})
  1126  		signedTx, _ := types.SignTx(tx, signer, testKey)
  1127  		zondservice.TxPool().Add([]*types.Transaction{signedTx}, false, false)
  1128  	}
  1129  
  1130  	withdrawals := make([][]*types.Withdrawal, 10)
  1131  	withdrawals[0] = nil // should be filtered out by miner
  1132  	withdrawals[1] = make([]*types.Withdrawal, 0)
  1133  	for i := 2; i < len(withdrawals); i++ {
  1134  		addr := make([]byte, 20)
  1135  		crand.Read(addr)
  1136  		withdrawals[i] = []*types.Withdrawal{
  1137  			{Index: rand.Uint64(), Validator: rand.Uint64(), Amount: rand.Uint64(), Address: common.BytesToAddress(addr)},
  1138  		}
  1139  	}
  1140  
  1141  	postShanghaiHeaders := setupBlocks(t, zondservice, 10, parent, callback, withdrawals)
  1142  	postShanghaiBlocks := make([]*types.Block, len(postShanghaiHeaders))
  1143  	for i, header := range postShanghaiHeaders {
  1144  		postShanghaiBlocks[i] = zondservice.BlockChain().GetBlock(header.Hash(), header.Number.Uint64())
  1145  	}
  1146  	return n, zondservice, append(blocks, postShanghaiBlocks...)
  1147  }
  1148  
  1149  func allHashes(blocks []*types.Block) []common.Hash {
  1150  	var hashes []common.Hash
  1151  	for _, b := range blocks {
  1152  		hashes = append(hashes, b.Hash())
  1153  	}
  1154  	return hashes
  1155  }
  1156  func allBodies(blocks []*types.Block) []*types.Body {
  1157  	var bodies []*types.Body
  1158  	for _, b := range blocks {
  1159  		bodies = append(bodies, b.Body())
  1160  	}
  1161  	return bodies
  1162  }
  1163  
  1164  func TestGetBlockBodiesByHash(t *testing.T) {
  1165  	node, zond, blocks := setupBodies(t)
  1166  	api := NewConsensusAPI(zond)
  1167  	defer node.Close()
  1168  
  1169  	tests := []struct {
  1170  		results []*types.Body
  1171  		hashes  []common.Hash
  1172  	}{
  1173  		// First pos block
  1174  		{
  1175  			results: []*types.Body{zond.BlockChain().GetBlockByNumber(0).Body()},
  1176  			hashes:  []common.Hash{zond.BlockChain().GetBlockByNumber(0).Hash()},
  1177  		},
  1178  		// pos blocks
  1179  		{
  1180  			results: []*types.Body{blocks[0].Body(), blocks[9].Body(), blocks[14].Body()},
  1181  			hashes:  []common.Hash{blocks[0].Hash(), blocks[9].Hash(), blocks[14].Hash()},
  1182  		},
  1183  		// unavailable block
  1184  		{
  1185  			results: []*types.Body{blocks[0].Body(), nil, blocks[14].Body()},
  1186  			hashes:  []common.Hash{blocks[0].Hash(), {1, 2}, blocks[14].Hash()},
  1187  		},
  1188  		// same block multiple times
  1189  		{
  1190  			results: []*types.Body{blocks[0].Body(), nil, blocks[0].Body(), blocks[0].Body()},
  1191  			hashes:  []common.Hash{blocks[0].Hash(), {1, 2}, blocks[0].Hash(), blocks[0].Hash()},
  1192  		},
  1193  		// all blocks
  1194  		{
  1195  			results: allBodies(blocks),
  1196  			hashes:  allHashes(blocks),
  1197  		},
  1198  	}
  1199  
  1200  	for k, test := range tests {
  1201  		result := api.GetPayloadBodiesByHashV1(test.hashes)
  1202  		for i, r := range result {
  1203  			if !equalBody(test.results[i], r) {
  1204  				t.Fatalf("test %v: invalid response: expected %+v got %+v", k, test.results[i], r)
  1205  			}
  1206  		}
  1207  	}
  1208  }
  1209  
  1210  func TestGetBlockBodiesByRange(t *testing.T) {
  1211  	node, zond, blocks := setupBodies(t)
  1212  	api := NewConsensusAPI(zond)
  1213  	defer node.Close()
  1214  
  1215  	tests := []struct {
  1216  		results []*types.Body
  1217  		start   hexutil.Uint64
  1218  		count   hexutil.Uint64
  1219  	}{
  1220  		{
  1221  			results: []*types.Body{blocks[9].Body()},
  1222  			start:   10,
  1223  			count:   1,
  1224  		},
  1225  		// Genesis
  1226  		{
  1227  			results: []*types.Body{blocks[0].Body()},
  1228  			start:   1,
  1229  			count:   1,
  1230  		},
  1231  		// First post-merge block
  1232  		{
  1233  			results: []*types.Body{blocks[9].Body()},
  1234  			start:   10,
  1235  			count:   1,
  1236  		},
  1237  		// Pre & post merge blocks
  1238  		{
  1239  			results: []*types.Body{blocks[7].Body(), blocks[8].Body(), blocks[9].Body(), blocks[10].Body()},
  1240  			start:   8,
  1241  			count:   4,
  1242  		},
  1243  		// unavailable block
  1244  		{
  1245  			results: []*types.Body{blocks[18].Body(), blocks[19].Body()},
  1246  			start:   19,
  1247  			count:   3,
  1248  		},
  1249  		// unavailable block
  1250  		{
  1251  			results: []*types.Body{blocks[19].Body()},
  1252  			start:   20,
  1253  			count:   2,
  1254  		},
  1255  		{
  1256  			results: []*types.Body{blocks[19].Body()},
  1257  			start:   20,
  1258  			count:   1,
  1259  		},
  1260  		// whole range unavailable
  1261  		{
  1262  			results: make([]*types.Body, 0),
  1263  			start:   22,
  1264  			count:   2,
  1265  		},
  1266  		// allBlocks
  1267  		{
  1268  			results: allBodies(blocks),
  1269  			start:   1,
  1270  			count:   hexutil.Uint64(len(blocks)),
  1271  		},
  1272  	}
  1273  
  1274  	for k, test := range tests {
  1275  		result, err := api.GetPayloadBodiesByRangeV1(test.start, test.count)
  1276  		if err != nil {
  1277  			t.Fatal(err)
  1278  		}
  1279  		if len(result) == len(test.results) {
  1280  			for i, r := range result {
  1281  				if !equalBody(test.results[i], r) {
  1282  					t.Fatalf("test %d: invalid response: expected \n%+v\ngot\n%+v", k, test.results[i], r)
  1283  				}
  1284  			}
  1285  		} else {
  1286  			t.Fatalf("test %d: invalid length want %v got %v", k, len(test.results), len(result))
  1287  		}
  1288  	}
  1289  }
  1290  
  1291  func TestGetBlockBodiesByRangeInvalidParams(t *testing.T) {
  1292  	node, zond, _ := setupBodies(t)
  1293  	api := NewConsensusAPI(zond)
  1294  	defer node.Close()
  1295  	tests := []struct {
  1296  		start hexutil.Uint64
  1297  		count hexutil.Uint64
  1298  		want  *engine.EngineAPIError
  1299  	}{
  1300  		// Genesis
  1301  		{
  1302  			start: 0,
  1303  			count: 1,
  1304  			want:  engine.InvalidParams,
  1305  		},
  1306  		// No block requested
  1307  		{
  1308  			start: 1,
  1309  			count: 0,
  1310  			want:  engine.InvalidParams,
  1311  		},
  1312  		// Genesis & no block
  1313  		{
  1314  			start: 0,
  1315  			count: 0,
  1316  			want:  engine.InvalidParams,
  1317  		},
  1318  		// More than 1024 blocks
  1319  		{
  1320  			start: 1,
  1321  			count: 1025,
  1322  			want:  engine.TooLargeRequest,
  1323  		},
  1324  	}
  1325  	for i, tc := range tests {
  1326  		result, err := api.GetPayloadBodiesByRangeV1(tc.start, tc.count)
  1327  		if err == nil {
  1328  			t.Fatalf("test %d: expected error, got %v", i, result)
  1329  		}
  1330  		if have, want := err.Error(), tc.want.Error(); have != want {
  1331  			t.Fatalf("test %d: have %s, want %s", i, have, want)
  1332  		}
  1333  	}
  1334  }
  1335  
  1336  func equalBody(a *types.Body, b *engine.ExecutionPayloadBodyV1) bool {
  1337  	if a == nil && b == nil {
  1338  		return true
  1339  	} else if a == nil || b == nil {
  1340  		return false
  1341  	}
  1342  	if len(a.Transactions) != len(b.TransactionData) {
  1343  		return false
  1344  	}
  1345  	for i, tx := range a.Transactions {
  1346  		data, _ := tx.MarshalBinary()
  1347  		if !bytes.Equal(data, b.TransactionData[i]) {
  1348  			return false
  1349  		}
  1350  	}
  1351  	return reflect.DeepEqual(a.Withdrawals, b.Withdrawals)
  1352  }