github.com/ethereum/go-ethereum@v1.16.1/cmd/workload/historytestgen.go (about)

     1  // Copyright 2025 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU 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  // go-ethereum 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 General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package main
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"math/big"
    24  	"os"
    25  
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/core/types"
    28  	"github.com/ethereum/go-ethereum/crypto"
    29  	"github.com/ethereum/go-ethereum/internal/flags"
    30  	"github.com/ethereum/go-ethereum/rlp"
    31  	"github.com/urfave/cli/v2"
    32  )
    33  
    34  var (
    35  	historyGenerateCommand = &cli.Command{
    36  		Name:      "historygen",
    37  		Usage:     "Generates history retrieval tests",
    38  		ArgsUsage: "<RPC endpoint URL>",
    39  		Action:    generateHistoryTests,
    40  		Flags: []cli.Flag{
    41  			historyTestFileFlag,
    42  			historyTestEarliestFlag,
    43  		},
    44  	}
    45  
    46  	historyTestFileFlag = &cli.StringFlag{
    47  		Name:     "history-tests",
    48  		Usage:    "JSON file containing filter test queries",
    49  		Value:    "history_tests.json",
    50  		Category: flags.TestingCategory,
    51  	}
    52  	historyTestEarliestFlag = &cli.IntFlag{
    53  		Name:     "earliest",
    54  		Usage:    "The earliest block to test queries",
    55  		Value:    0,
    56  		Category: flags.TestingCategory,
    57  	}
    58  )
    59  
    60  const historyTestBlockCount = 2000
    61  
    62  func generateHistoryTests(clictx *cli.Context) error {
    63  	var (
    64  		client     = makeClient(clictx)
    65  		earliest   = uint64(clictx.Int(historyTestEarliestFlag.Name))
    66  		outputFile = clictx.String(historyTestFileFlag.Name)
    67  		ctx        = context.Background()
    68  	)
    69  
    70  	test := new(historyTest)
    71  
    72  	// Create the block numbers. Here we choose 1k blocks between earliest and head.
    73  	latest, err := client.Eth.BlockNumber(ctx)
    74  	if err != nil {
    75  		exit(err)
    76  	}
    77  	if latest < historyTestBlockCount {
    78  		exit(fmt.Errorf("node seems not synced, latest block is %d", latest))
    79  	}
    80  	test.BlockNumbers = make([]uint64, 0, historyTestBlockCount)
    81  	stride := (latest - earliest) / historyTestBlockCount
    82  	for b := earliest; b < latest; b += stride {
    83  		test.BlockNumbers = append(test.BlockNumbers, b)
    84  	}
    85  
    86  	// Get blocks and assign block info into the test
    87  	fmt.Println("Fetching blocks")
    88  	blocks := make([]*types.Block, len(test.BlockNumbers))
    89  	for i, blocknum := range test.BlockNumbers {
    90  		b, err := client.Eth.BlockByNumber(ctx, new(big.Int).SetUint64(blocknum))
    91  		if err != nil {
    92  			exit(fmt.Errorf("error fetching block %d: %v", blocknum, err))
    93  		}
    94  		blocks[i] = b
    95  	}
    96  	test.BlockHashes = make([]common.Hash, len(blocks))
    97  	test.TxCounts = make([]int, len(blocks))
    98  	for i, block := range blocks {
    99  		test.BlockHashes[i] = block.Hash()
   100  		test.TxCounts[i] = len(block.Transactions())
   101  	}
   102  
   103  	// Fill tx index.
   104  	test.TxHashIndex = make([]int, len(blocks))
   105  	test.TxHashes = make([]*common.Hash, len(blocks))
   106  	for i, block := range blocks {
   107  		txs := block.Transactions()
   108  		if len(txs) == 0 {
   109  			continue
   110  		}
   111  		index := len(txs) / 2
   112  		txhash := txs[index].Hash()
   113  		test.TxHashIndex[i] = index
   114  		test.TxHashes[i] = &txhash
   115  	}
   116  
   117  	// Get receipts.
   118  	fmt.Println("Fetching receipts")
   119  	test.ReceiptsHashes = make([]common.Hash, len(blocks))
   120  	for i, blockHash := range test.BlockHashes {
   121  		receipts, err := client.getBlockReceipts(ctx, blockHash)
   122  		if err != nil {
   123  			exit(fmt.Errorf("error fetching block %v receipts: %v", blockHash, err))
   124  		}
   125  		test.ReceiptsHashes[i] = calcReceiptsHash(receipts)
   126  	}
   127  
   128  	// Write output file.
   129  	writeJSON(outputFile, test)
   130  	return nil
   131  }
   132  
   133  func calcReceiptsHash(rcpt []*types.Receipt) common.Hash {
   134  	h := crypto.NewKeccakState()
   135  	rlp.Encode(h, rcpt)
   136  	return common.Hash(h.Sum(nil))
   137  }
   138  
   139  func writeJSON(fileName string, value any) {
   140  	file, err := os.Create(fileName)
   141  	if err != nil {
   142  		exit(fmt.Errorf("error creating %s: %v", fileName, err))
   143  		return
   144  	}
   145  	defer file.Close()
   146  	json.NewEncoder(file).Encode(value)
   147  }