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 }