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