github.com/bcnmy/go-ethereum@v1.10.27/les/catalyst/api_test.go (about)

     1  // Copyright 2022 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  
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/consensus/ethash"
    25  	"github.com/ethereum/go-ethereum/core"
    26  	"github.com/ethereum/go-ethereum/core/beacon"
    27  	"github.com/ethereum/go-ethereum/core/rawdb"
    28  	"github.com/ethereum/go-ethereum/core/types"
    29  	"github.com/ethereum/go-ethereum/crypto"
    30  	"github.com/ethereum/go-ethereum/eth/downloader"
    31  	"github.com/ethereum/go-ethereum/eth/ethconfig"
    32  	"github.com/ethereum/go-ethereum/les"
    33  	"github.com/ethereum/go-ethereum/node"
    34  	"github.com/ethereum/go-ethereum/params"
    35  	"github.com/ethereum/go-ethereum/trie"
    36  )
    37  
    38  var (
    39  	// testKey is a private key to use for funding a tester account.
    40  	testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    41  
    42  	// testAddr is the Ethereum address of the tester account.
    43  	testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
    44  
    45  	testBalance = big.NewInt(2e18)
    46  )
    47  
    48  func generatePreMergeChain(n int) (*core.Genesis, []*types.Header, []*types.Block) {
    49  	db := rawdb.NewMemoryDatabase()
    50  	config := params.AllEthashProtocolChanges
    51  	genesis := &core.Genesis{
    52  		Config:    config,
    53  		Alloc:     core.GenesisAlloc{testAddr: {Balance: testBalance}},
    54  		ExtraData: []byte("test genesis"),
    55  		Timestamp: 9000,
    56  		BaseFee:   big.NewInt(params.InitialBaseFee),
    57  	}
    58  	gblock := genesis.MustCommit(db)
    59  	engine := ethash.NewFaker()
    60  	blocks, _ := core.GenerateChain(config, gblock, engine, db, n, nil)
    61  	totalDifficulty := big.NewInt(0)
    62  
    63  	var headers []*types.Header
    64  	for _, b := range blocks {
    65  		totalDifficulty.Add(totalDifficulty, b.Difficulty())
    66  		headers = append(headers, b.Header())
    67  	}
    68  	config.TerminalTotalDifficulty = totalDifficulty
    69  
    70  	return genesis, headers, blocks
    71  }
    72  
    73  func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
    74  	genesis, headers, blocks := generatePreMergeChain(10)
    75  	n, lesService := startLesService(t, genesis, headers)
    76  	defer n.Close()
    77  
    78  	api := NewConsensusAPI(lesService)
    79  	fcState := beacon.ForkchoiceStateV1{
    80  		HeadBlockHash:      blocks[5].Hash(),
    81  		SafeBlockHash:      common.Hash{},
    82  		FinalizedBlockHash: common.Hash{},
    83  	}
    84  	if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err == nil {
    85  		t.Errorf("fork choice updated before total terminal difficulty should fail")
    86  	}
    87  }
    88  
    89  func TestExecutePayloadV1(t *testing.T) {
    90  	genesis, headers, blocks := generatePreMergeChain(10)
    91  	n, lesService := startLesService(t, genesis, headers[:9])
    92  	lesService.Merger().ReachTTD()
    93  	defer n.Close()
    94  
    95  	api := NewConsensusAPI(lesService)
    96  	fcState := beacon.ForkchoiceStateV1{
    97  		HeadBlockHash:      blocks[8].Hash(),
    98  		SafeBlockHash:      common.Hash{},
    99  		FinalizedBlockHash: common.Hash{},
   100  	}
   101  	if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   102  		t.Errorf("Failed to update head %v", err)
   103  	}
   104  	block := blocks[9]
   105  
   106  	fakeBlock := types.NewBlock(&types.Header{
   107  		ParentHash:  block.ParentHash(),
   108  		UncleHash:   crypto.Keccak256Hash(nil),
   109  		Coinbase:    block.Coinbase(),
   110  		Root:        block.Root(),
   111  		TxHash:      crypto.Keccak256Hash(nil),
   112  		ReceiptHash: crypto.Keccak256Hash(nil),
   113  		Bloom:       block.Bloom(),
   114  		Difficulty:  big.NewInt(0),
   115  		Number:      block.Number(),
   116  		GasLimit:    block.GasLimit(),
   117  		GasUsed:     block.GasUsed(),
   118  		Time:        block.Time(),
   119  		Extra:       block.Extra(),
   120  		MixDigest:   block.MixDigest(),
   121  		Nonce:       types.BlockNonce{},
   122  		BaseFee:     block.BaseFee(),
   123  	}, nil, nil, nil, trie.NewStackTrie(nil))
   124  
   125  	_, err := api.ExecutePayloadV1(beacon.ExecutableDataV1{
   126  		ParentHash:    fakeBlock.ParentHash(),
   127  		FeeRecipient:  fakeBlock.Coinbase(),
   128  		StateRoot:     fakeBlock.Root(),
   129  		ReceiptsRoot:  fakeBlock.ReceiptHash(),
   130  		LogsBloom:     fakeBlock.Bloom().Bytes(),
   131  		Random:        fakeBlock.MixDigest(),
   132  		Number:        fakeBlock.NumberU64(),
   133  		GasLimit:      fakeBlock.GasLimit(),
   134  		GasUsed:       fakeBlock.GasUsed(),
   135  		Timestamp:     fakeBlock.Time(),
   136  		ExtraData:     fakeBlock.Extra(),
   137  		BaseFeePerGas: fakeBlock.BaseFee(),
   138  		BlockHash:     fakeBlock.Hash(),
   139  		Transactions:  encodeTransactions(fakeBlock.Transactions()),
   140  	})
   141  	if err != nil {
   142  		t.Errorf("Failed to execute payload %v", err)
   143  	}
   144  	headHeader := api.les.BlockChain().CurrentHeader()
   145  	if headHeader.Number.Uint64() != fakeBlock.NumberU64()-1 {
   146  		t.Fatal("Unexpected chain head update")
   147  	}
   148  	fcState = beacon.ForkchoiceStateV1{
   149  		HeadBlockHash:      fakeBlock.Hash(),
   150  		SafeBlockHash:      common.Hash{},
   151  		FinalizedBlockHash: common.Hash{},
   152  	}
   153  	if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
   154  		t.Fatal("Failed to update head")
   155  	}
   156  	headHeader = api.les.BlockChain().CurrentHeader()
   157  	if headHeader.Number.Uint64() != fakeBlock.NumberU64() {
   158  		t.Fatal("Failed to update chain head")
   159  	}
   160  }
   161  
   162  func TestEth2DeepReorg(t *testing.T) {
   163  	// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
   164  	// before the totalTerminalDifficulty threshold
   165  	/*
   166  		genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2)
   167  		n, ethservice := startEthService(t, genesis, preMergeBlocks)
   168  		defer n.Close()
   169  
   170  		var (
   171  			api    = NewConsensusAPI(ethservice, nil)
   172  			parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1]
   173  			head   = ethservice.BlockChain().CurrentBlock().NumberU64()
   174  		)
   175  		if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) {
   176  			t.Errorf("Block %d not pruned", parent.NumberU64())
   177  		}
   178  		for i := 0; i < 10; i++ {
   179  			execData, err := api.assembleBlock(AssembleBlockParams{
   180  				ParentHash: parent.Hash(),
   181  				Timestamp:  parent.Time() + 5,
   182  			})
   183  			if err != nil {
   184  				t.Fatalf("Failed to create the executable data %v", err)
   185  			}
   186  			block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData)
   187  			if err != nil {
   188  				t.Fatalf("Failed to convert executable data to block %v", err)
   189  			}
   190  			newResp, err := api.ExecutePayload(*execData)
   191  			if err != nil || newResp.Status != "VALID" {
   192  				t.Fatalf("Failed to insert block: %v", err)
   193  			}
   194  			if ethservice.BlockChain().CurrentBlock().NumberU64() != head {
   195  				t.Fatalf("Chain head shouldn't be updated")
   196  			}
   197  			if err := api.setCanonical(block.Hash()); err != nil {
   198  				t.Fatalf("Failed to set head: %v", err)
   199  			}
   200  			if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() {
   201  				t.Fatalf("Chain head should be updated")
   202  			}
   203  			parent, head = block, block.NumberU64()
   204  		}
   205  	*/
   206  }
   207  
   208  // startEthService creates a full node instance for testing.
   209  func startLesService(t *testing.T, genesis *core.Genesis, headers []*types.Header) (*node.Node, *les.LightEthereum) {
   210  	t.Helper()
   211  
   212  	n, err := node.New(&node.Config{})
   213  	if err != nil {
   214  		t.Fatal("can't create node:", err)
   215  	}
   216  	ethcfg := &ethconfig.Config{
   217  		Genesis:        genesis,
   218  		Ethash:         ethash.Config{PowMode: ethash.ModeFake},
   219  		SyncMode:       downloader.LightSync,
   220  		TrieDirtyCache: 256,
   221  		TrieCleanCache: 256,
   222  		LightPeers:     10,
   223  	}
   224  	lesService, err := les.New(n, ethcfg)
   225  	if err != nil {
   226  		t.Fatal("can't create eth service:", err)
   227  	}
   228  	if err := n.Start(); err != nil {
   229  		t.Fatal("can't start node:", err)
   230  	}
   231  	if _, err := lesService.BlockChain().InsertHeaderChain(headers, 0); err != nil {
   232  		n.Close()
   233  		t.Fatal("can't import test headers:", err)
   234  	}
   235  	return n, lesService
   236  }
   237  
   238  func encodeTransactions(txs []*types.Transaction) [][]byte {
   239  	var enc = make([][]byte, len(txs))
   240  	for i, tx := range txs {
   241  		enc[i], _ = tx.MarshalBinary()
   242  	}
   243  	return enc
   244  }