github.com/LampardNguyen234/go-ethereum@v1.10.16-0.20220117140830-b6a3b0260724/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  	"math/big"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/LampardNguyen234/go-ethereum/common"
    25  	"github.com/LampardNguyen234/go-ethereum/common/hexutil"
    26  	"github.com/LampardNguyen234/go-ethereum/consensus/ethash"
    27  	"github.com/LampardNguyen234/go-ethereum/core"
    28  	"github.com/LampardNguyen234/go-ethereum/core/rawdb"
    29  	"github.com/LampardNguyen234/go-ethereum/core/types"
    30  	"github.com/LampardNguyen234/go-ethereum/crypto"
    31  	"github.com/LampardNguyen234/go-ethereum/eth"
    32  	"github.com/LampardNguyen234/go-ethereum/eth/ethconfig"
    33  	"github.com/LampardNguyen234/go-ethereum/node"
    34  	"github.com/LampardNguyen234/go-ethereum/params"
    35  )
    36  
    37  var (
    38  	// testKey is a private key to use for funding a tester account.
    39  	testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    40  
    41  	// testAddr is the Ethereum address of the tester account.
    42  	testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
    43  
    44  	testBalance = big.NewInt(2e18)
    45  )
    46  
    47  func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) {
    48  	db := rawdb.NewMemoryDatabase()
    49  	config := params.AllEthashProtocolChanges
    50  	genesis := &core.Genesis{
    51  		Config:    config,
    52  		Alloc:     core.GenesisAlloc{testAddr: {Balance: testBalance}},
    53  		ExtraData: []byte("test genesis"),
    54  		Timestamp: 9000,
    55  		BaseFee:   big.NewInt(params.InitialBaseFee),
    56  	}
    57  	testNonce := uint64(0)
    58  	generate := func(i int, g *core.BlockGen) {
    59  		g.OffsetTime(5)
    60  		g.SetExtra([]byte("test"))
    61  		tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(config), testKey)
    62  		g.AddTx(tx)
    63  		testNonce++
    64  	}
    65  	gblock := genesis.ToBlock(db)
    66  	engine := ethash.NewFaker()
    67  	blocks, _ := core.GenerateChain(config, gblock, engine, db, n, generate)
    68  	totalDifficulty := big.NewInt(0)
    69  	for _, b := range blocks {
    70  		totalDifficulty.Add(totalDifficulty, b.Difficulty())
    71  	}
    72  	config.TerminalTotalDifficulty = totalDifficulty
    73  	return genesis, blocks
    74  }
    75  
    76  func TestEth2AssembleBlock(t *testing.T) {
    77  	genesis, blocks := generatePreMergeChain(10)
    78  	n, ethservice := startEthService(t, genesis, blocks)
    79  	defer n.Close()
    80  
    81  	api := NewConsensusAPI(ethservice, nil)
    82  	signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID)
    83  	tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey)
    84  	if err != nil {
    85  		t.Fatalf("error signing transaction, err=%v", err)
    86  	}
    87  	ethservice.TxPool().AddLocal(tx)
    88  	blockParams := PayloadAttributesV1{
    89  		Timestamp: blocks[9].Time() + 5,
    90  	}
    91  	execData, err := api.assembleBlock(blocks[9].Hash(), &blockParams)
    92  	if err != nil {
    93  		t.Fatalf("error producing block, err=%v", err)
    94  	}
    95  	if len(execData.Transactions) != 1 {
    96  		t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
    97  	}
    98  }
    99  
   100  func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
   101  	genesis, blocks := generatePreMergeChain(10)
   102  	n, ethservice := startEthService(t, genesis, blocks[:9])
   103  	defer n.Close()
   104  
   105  	api := NewConsensusAPI(ethservice, nil)
   106  
   107  	// Put the 10th block's tx in the pool and produce a new block
   108  	api.insertTransactions(blocks[9].Transactions())
   109  	blockParams := PayloadAttributesV1{
   110  		Timestamp: blocks[8].Time() + 5,
   111  	}
   112  	execData, err := api.assembleBlock(blocks[8].Hash(), &blockParams)
   113  	if err != nil {
   114  		t.Fatalf("error producing block, err=%v", err)
   115  	}
   116  	if len(execData.Transactions) != blocks[9].Transactions().Len() {
   117  		t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
   118  	}
   119  }
   120  
   121  func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
   122  	genesis, blocks := generatePreMergeChain(10)
   123  	n, ethservice := startEthService(t, genesis, blocks)
   124  	defer n.Close()
   125  
   126  	api := NewConsensusAPI(ethservice, nil)
   127  	fcState := ForkchoiceStateV1{
   128  		HeadBlockHash:      blocks[5].Hash(),
   129  		SafeBlockHash:      common.Hash{},
   130  		FinalizedBlockHash: common.Hash{},
   131  	}
   132  	if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err == nil {
   133  		t.Errorf("fork choice updated before total terminal difficulty should fail")
   134  	}
   135  }
   136  
   137  func TestEth2PrepareAndGetPayload(t *testing.T) {
   138  	genesis, blocks := generatePreMergeChain(10)
   139  	// We need to properly set the terminal total difficulty
   140  	genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty())
   141  	n, ethservice := startEthService(t, genesis, blocks[:9])
   142  	defer n.Close()
   143  
   144  	api := NewConsensusAPI(ethservice, nil)
   145  
   146  	// Put the 10th block's tx in the pool and produce a new block
   147  	api.insertTransactions(blocks[9].Transactions())
   148  	blockParams := PayloadAttributesV1{
   149  		Timestamp: blocks[8].Time() + 5,
   150  	}
   151  	fcState := ForkchoiceStateV1{
   152  		HeadBlockHash:      blocks[8].Hash(),
   153  		SafeBlockHash:      common.Hash{},
   154  		FinalizedBlockHash: common.Hash{},
   155  	}
   156  	_, err := api.ForkchoiceUpdatedV1(fcState, &blockParams)
   157  	if err != nil {
   158  		t.Fatalf("error preparing payload, err=%v", err)
   159  	}
   160  	payloadID := computePayloadId(fcState.HeadBlockHash, &blockParams)
   161  	execData, err := api.GetPayloadV1(hexutil.Bytes(payloadID))
   162  	if err != nil {
   163  		t.Fatalf("error getting payload, err=%v", err)
   164  	}
   165  	if len(execData.Transactions) != blocks[9].Transactions().Len() {
   166  		t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions))
   167  	}
   168  }
   169  
   170  func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) {
   171  	t.Helper()
   172  
   173  	if len(logsCh) != wantNew {
   174  		t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew)
   175  	}
   176  	if len(rmLogsCh) != wantRemoved {
   177  		t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved)
   178  	}
   179  	// Drain events.
   180  	for i := 0; i < len(logsCh); i++ {
   181  		<-logsCh
   182  	}
   183  	for i := 0; i < len(rmLogsCh); i++ {
   184  		<-rmLogsCh
   185  	}
   186  }
   187  
   188  func TestEth2NewBlock(t *testing.T) {
   189  	genesis, preMergeBlocks := generatePreMergeChain(10)
   190  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   191  	ethservice.Merger().ReachTTD()
   192  	defer n.Close()
   193  
   194  	var (
   195  		api    = NewConsensusAPI(ethservice, nil)
   196  		parent = preMergeBlocks[len(preMergeBlocks)-1]
   197  
   198  		// This EVM code generates a log when the contract is created.
   199  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   200  	)
   201  	// The event channels.
   202  	newLogCh := make(chan []*types.Log, 10)
   203  	rmLogsCh := make(chan core.RemovedLogsEvent, 10)
   204  	ethservice.BlockChain().SubscribeLogsEvent(newLogCh)
   205  	ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh)
   206  
   207  	for i := 0; i < 10; i++ {
   208  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   209  		nonce := statedb.GetNonce(testAddr)
   210  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   211  		ethservice.TxPool().AddLocal(tx)
   212  
   213  		execData, err := api.assembleBlock(parent.Hash(), &PayloadAttributesV1{
   214  			Timestamp: parent.Time() + 5,
   215  		})
   216  		if err != nil {
   217  			t.Fatalf("Failed to create the executable data %v", err)
   218  		}
   219  		block, err := ExecutableDataToBlock(*execData)
   220  		if err != nil {
   221  			t.Fatalf("Failed to convert executable data to block %v", err)
   222  		}
   223  		newResp, err := api.ExecutePayloadV1(*execData)
   224  		if err != nil || newResp.Status != "VALID" {
   225  			t.Fatalf("Failed to insert block: %v", err)
   226  		}
   227  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 {
   228  			t.Fatalf("Chain head shouldn't be updated")
   229  		}
   230  		checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
   231  		fcState := ForkchoiceStateV1{
   232  			HeadBlockHash:      block.Hash(),
   233  			SafeBlockHash:      block.Hash(),
   234  			FinalizedBlockHash: block.Hash(),
   235  		}
   236  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   237  			t.Fatalf("Failed to insert block: %v", err)
   238  		}
   239  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   240  			t.Fatalf("Chain head should be updated")
   241  		}
   242  		checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
   243  
   244  		parent = block
   245  	}
   246  
   247  	// Introduce fork chain
   248  	var (
   249  		head = ethservice.BlockChain().CurrentBlock().NumberU64()
   250  	)
   251  	parent = preMergeBlocks[len(preMergeBlocks)-1]
   252  	for i := 0; i < 10; i++ {
   253  		execData, err := api.assembleBlock(parent.Hash(), &PayloadAttributesV1{
   254  			Timestamp: parent.Time() + 6,
   255  		})
   256  		if err != nil {
   257  			t.Fatalf("Failed to create the executable data %v", err)
   258  		}
   259  		block, err := ExecutableDataToBlock(*execData)
   260  		if err != nil {
   261  			t.Fatalf("Failed to convert executable data to block %v", err)
   262  		}
   263  		newResp, err := api.ExecutePayloadV1(*execData)
   264  		if err != nil || newResp.Status != "VALID" {
   265  			t.Fatalf("Failed to insert block: %v", err)
   266  		}
   267  		if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   268  			t.Fatalf("Chain head shouldn't be updated")
   269  		}
   270  
   271  		fcState := ForkchoiceStateV1{
   272  			HeadBlockHash:      block.Hash(),
   273  			SafeBlockHash:      block.Hash(),
   274  			FinalizedBlockHash: block.Hash(),
   275  		}
   276  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   277  			t.Fatalf("Failed to insert block: %v", err)
   278  		}
   279  		if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   280  			t.Fatalf("Chain head should be updated")
   281  		}
   282  		parent, head = block, block.NumberU64()
   283  	}
   284  }
   285  
   286  func TestEth2DeepReorg(t *testing.T) {
   287  	// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
   288  	// before the totalTerminalDifficulty threshold
   289  	/*
   290  		genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2)
   291  		n, ethservice := startEthService(t, genesis, preMergeBlocks)
   292  		defer n.Close()
   293  
   294  		var (
   295  			api    = NewConsensusAPI(ethservice, nil)
   296  			parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1]
   297  			head   = ethservice.BlockChain().CurrentBlock().NumberU64()
   298  		)
   299  		if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) {
   300  			t.Errorf("Block %d not pruned", parent.NumberU64())
   301  		}
   302  		for i := 0; i < 10; i++ {
   303  			execData, err := api.assembleBlock(AssembleBlockParams{
   304  				ParentHash: parent.Hash(),
   305  				Timestamp:  parent.Time() + 5,
   306  			})
   307  			if err != nil {
   308  				t.Fatalf("Failed to create the executable data %v", err)
   309  			}
   310  			block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData)
   311  			if err != nil {
   312  				t.Fatalf("Failed to convert executable data to block %v", err)
   313  			}
   314  			newResp, err := api.ExecutePayload(*execData)
   315  			if err != nil || newResp.Status != "VALID" {
   316  				t.Fatalf("Failed to insert block: %v", err)
   317  			}
   318  			if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   319  				t.Fatalf("Chain head shouldn't be updated")
   320  			}
   321  			if err := api.setHead(block.Hash()); err != nil {
   322  				t.Fatalf("Failed to set head: %v", err)
   323  			}
   324  			if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   325  				t.Fatalf("Chain head should be updated")
   326  			}
   327  			parent, head = block, block.NumberU64()
   328  		}
   329  	*/
   330  }
   331  
   332  // startEthService creates a full node instance for testing.
   333  func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) {
   334  	t.Helper()
   335  
   336  	n, err := node.New(&node.Config{})
   337  	if err != nil {
   338  		t.Fatal("can't create node:", err)
   339  	}
   340  
   341  	ethcfg := &ethconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256}
   342  	ethservice, err := eth.New(n, ethcfg)
   343  	if err != nil {
   344  		t.Fatal("can't create eth service:", err)
   345  	}
   346  	if err := n.Start(); err != nil {
   347  		t.Fatal("can't start node:", err)
   348  	}
   349  	if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil {
   350  		n.Close()
   351  		t.Fatal("can't import test blocks:", err)
   352  	}
   353  	ethservice.SetEtherbase(testAddr)
   354  	ethservice.SetSynced()
   355  
   356  	return n, ethservice
   357  }
   358  
   359  func TestFullAPI(t *testing.T) {
   360  	genesis, preMergeBlocks := generatePreMergeChain(10)
   361  	n, ethservice := startEthService(t, genesis, preMergeBlocks)
   362  	ethservice.Merger().ReachTTD()
   363  	defer n.Close()
   364  	var (
   365  		api    = NewConsensusAPI(ethservice, nil)
   366  		parent = ethservice.BlockChain().CurrentBlock()
   367  		// This EVM code generates a log when the contract is created.
   368  		logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
   369  	)
   370  	for i := 0; i < 10; i++ {
   371  		statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
   372  		nonce := statedb.GetNonce(testAddr)
   373  		tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
   374  		ethservice.TxPool().AddLocal(tx)
   375  
   376  		params := PayloadAttributesV1{
   377  			Timestamp:             parent.Time() + 1,
   378  			Random:                crypto.Keccak256Hash([]byte{byte(i)}),
   379  			SuggestedFeeRecipient: parent.Coinbase(),
   380  		}
   381  		fcState := ForkchoiceStateV1{
   382  			HeadBlockHash:      parent.Hash(),
   383  			SafeBlockHash:      common.Hash{},
   384  			FinalizedBlockHash: common.Hash{},
   385  		}
   386  		resp, err := api.ForkchoiceUpdatedV1(fcState, &params)
   387  		if err != nil {
   388  			t.Fatalf("error preparing payload, err=%v", err)
   389  		}
   390  		if resp.Status != SUCCESS.Status {
   391  			t.Fatalf("error preparing payload, invalid status: %v", resp.Status)
   392  		}
   393  		payloadID := computePayloadId(parent.Hash(), &params)
   394  		payload, err := api.GetPayloadV1(hexutil.Bytes(payloadID))
   395  		if err != nil {
   396  			t.Fatalf("can't get payload: %v", err)
   397  		}
   398  		execResp, err := api.ExecutePayloadV1(*payload)
   399  		if err != nil {
   400  			t.Fatalf("can't execute payload: %v", err)
   401  		}
   402  		if execResp.Status != VALID.Status {
   403  			t.Fatalf("invalid status: %v", execResp.Status)
   404  		}
   405  		fcState = ForkchoiceStateV1{
   406  			HeadBlockHash:      payload.BlockHash,
   407  			SafeBlockHash:      payload.ParentHash,
   408  			FinalizedBlockHash: payload.ParentHash,
   409  		}
   410  		if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   411  			t.Fatalf("Failed to insert block: %v", err)
   412  		}
   413  		if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
   414  			t.Fatalf("Chain head should be updated")
   415  		}
   416  		parent = ethservice.BlockChain().CurrentBlock()
   417  
   418  	}
   419  }