go-ethereum/cmd/workload/historytest.go

310 lines
9.5 KiB
Go

// Copyright 2025 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/utesting"
)
// historyTest is the content of a history test.
type historyTest struct {
BlockNumbers []uint64 `json:"blockNumbers"`
BlockHashes []common.Hash `json:"blockHashes"`
TxCounts []int `json:"txCounts"`
TxHashIndex []int `json:"txHashIndex"`
TxHashes []*common.Hash `json:"txHashes"`
ReceiptsHashes []common.Hash `json:"blockReceiptsHashes"`
}
type historyTestSuite struct {
cfg testConfig
tests historyTest
}
func newHistoryTestSuite(cfg testConfig) *historyTestSuite {
s := &historyTestSuite{cfg: cfg}
if err := s.loadTests(); err != nil {
exit(err)
}
return s
}
func (s *historyTestSuite) loadTests() error {
file, err := s.cfg.fsys.Open(s.cfg.historyTestFile)
if err != nil {
return fmt.Errorf("can't open historyTestFile: %v", err)
}
defer file.Close()
if err := json.NewDecoder(file).Decode(&s.tests); err != nil {
return fmt.Errorf("invalid JSON in %s: %v", s.cfg.historyTestFile, err)
}
if len(s.tests.BlockNumbers) == 0 {
return fmt.Errorf("historyTestFile %s has no test data", s.cfg.historyTestFile)
}
return nil
}
func (s *historyTestSuite) allTests() []utesting.Test {
return []utesting.Test{
{
Name: "History/getBlockByHash",
Fn: s.testGetBlockByHash,
},
{
Name: "History/getBlockByNumber",
Fn: s.testGetBlockByNumber,
},
{
Name: "History/getBlockReceiptsByHash",
Fn: s.testGetBlockReceiptsByHash,
},
{
Name: "History/getBlockReceiptsByNumber",
Fn: s.testGetBlockReceiptsByNumber,
},
{
Name: "History/getBlockTransactionCountByHash",
Fn: s.testGetBlockTransactionCountByHash,
},
{
Name: "History/getBlockTransactionCountByNumber",
Fn: s.testGetBlockTransactionCountByNumber,
},
{
Name: "History/getTransactionByBlockHashAndIndex",
Fn: s.testGetTransactionByBlockHashAndIndex,
},
{
Name: "History/getTransactionByBlockNumberAndIndex",
Fn: s.testGetTransactionByBlockNumberAndIndex,
},
}
}
func (s *historyTestSuite) testGetBlockByHash(t *utesting.T) {
ctx := context.Background()
for i, num := range s.tests.BlockNumbers {
bhash := s.tests.BlockHashes[i]
b, err := s.cfg.client.getBlockByHash(ctx, bhash, false)
if err != nil {
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
}
if b == nil {
t.Errorf("block %d (hash %v): not found", num, bhash)
continue
}
if b.Hash != bhash || uint64(b.Number) != num {
t.Errorf("block %d (hash %v): invalid number/hash", num, bhash)
}
}
}
func (s *historyTestSuite) testGetBlockByNumber(t *utesting.T) {
ctx := context.Background()
for i, num := range s.tests.BlockNumbers {
bhash := s.tests.BlockHashes[i]
b, err := s.cfg.client.getBlockByNumber(ctx, num, false)
if err != nil {
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
}
if b == nil {
t.Errorf("block %d (hash %v): not found", num, bhash)
continue
}
if b.Hash != bhash || uint64(b.Number) != num {
t.Errorf("block %d (hash %v): invalid number/hash", num, bhash)
}
}
}
func (s *historyTestSuite) testGetBlockTransactionCountByHash(t *utesting.T) {
ctx := context.Background()
for i, num := range s.tests.BlockNumbers {
bhash := s.tests.BlockHashes[i]
count, err := s.cfg.client.getBlockTransactionCountByHash(ctx, bhash)
if err != nil {
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
}
expectedCount := uint64(s.tests.TxCounts[i])
if count != expectedCount {
t.Errorf("block %d (hash %v): wrong txcount %d, want %d", count, expectedCount)
}
}
}
func (s *historyTestSuite) testGetBlockTransactionCountByNumber(t *utesting.T) {
ctx := context.Background()
for i, num := range s.tests.BlockNumbers {
bhash := s.tests.BlockHashes[i]
count, err := s.cfg.client.getBlockTransactionCountByNumber(ctx, num)
if err != nil {
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
}
expectedCount := uint64(s.tests.TxCounts[i])
if count != expectedCount {
t.Errorf("block %d (hash %v): wrong txcount %d, want %d", count, expectedCount)
}
}
}
func (s *historyTestSuite) testGetBlockReceiptsByHash(t *utesting.T) {
ctx := context.Background()
for i, num := range s.tests.BlockNumbers {
bhash := s.tests.BlockHashes[i]
receipts, err := s.cfg.client.getBlockReceipts(ctx, bhash)
if err != nil {
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
}
hash := calcReceiptsHash(receipts)
expectedHash := s.tests.ReceiptsHashes[i]
if hash != expectedHash {
t.Errorf("block %d (hash %v): wrong receipts hash %v, want %v", num, bhash, hash, expectedHash)
}
}
}
func (s *historyTestSuite) testGetBlockReceiptsByNumber(t *utesting.T) {
ctx := context.Background()
for i, num := range s.tests.BlockNumbers {
bhash := s.tests.BlockHashes[i]
receipts, err := s.cfg.client.getBlockReceipts(ctx, hexutil.Uint64(num))
if err != nil {
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
}
hash := calcReceiptsHash(receipts)
expectedHash := s.tests.ReceiptsHashes[i]
if hash != expectedHash {
t.Errorf("block %d (hash %v): wrong receipts hash %v, want %v", num, bhash, hash, expectedHash)
}
}
}
func (s *historyTestSuite) testGetTransactionByBlockHashAndIndex(t *utesting.T) {
ctx := context.Background()
for i, num := range s.tests.BlockNumbers {
bhash := s.tests.BlockHashes[i]
txIndex := s.tests.TxHashIndex[i]
expectedHash := s.tests.TxHashes[i]
if expectedHash == nil {
continue // no txs in block
}
tx, err := s.cfg.client.getTransactionByBlockHashAndIndex(ctx, bhash, uint64(txIndex))
if err != nil {
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
}
if tx == nil {
t.Errorf("block %d (hash %v): txIndex %d not found", num, bhash, txIndex)
continue
}
if tx.Hash != *expectedHash || uint64(tx.TransactionIndex) != uint64(txIndex) {
t.Errorf("block %d (hash %v): txIndex %d has wrong txHash/Index", num, bhash, txIndex)
}
}
}
func (s *historyTestSuite) testGetTransactionByBlockNumberAndIndex(t *utesting.T) {
ctx := context.Background()
for i, num := range s.tests.BlockNumbers {
bhash := s.tests.BlockHashes[i]
txIndex := s.tests.TxHashIndex[i]
expectedHash := s.tests.TxHashes[i]
if expectedHash == nil {
continue // no txs in block
}
tx, err := s.cfg.client.getTransactionByBlockNumberAndIndex(ctx, num, uint64(txIndex))
if err != nil {
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
}
if tx == nil {
t.Errorf("block %d (hash %v): txIndex %d not found", num, bhash, txIndex)
continue
}
if tx.Hash != *expectedHash || uint64(tx.TransactionIndex) != uint64(txIndex) {
t.Errorf("block %d (hash %v): txIndex %d has wrong txHash/Index", num, bhash, txIndex)
}
}
}
type simpleBlock struct {
Number hexutil.Uint64 `json:"number"`
Hash common.Hash `json:"hash"`
}
type simpleTransaction struct {
Hash common.Hash `json:"hash"`
TransactionIndex hexutil.Uint64 `json:"transactionIndex"`
}
func (c *client) getBlockByHash(ctx context.Context, arg common.Hash, fullTx bool) (*simpleBlock, error) {
var r *simpleBlock
err := c.RPC.CallContext(ctx, &r, "eth_getBlockByHash", arg, fullTx)
return r, err
}
func (c *client) getBlockByNumber(ctx context.Context, arg uint64, fullTx bool) (*simpleBlock, error) {
var r *simpleBlock
err := c.RPC.CallContext(ctx, &r, "eth_getBlockByNumber", hexutil.Uint64(arg), fullTx)
return r, err
}
func (c *client) getTransactionByBlockHashAndIndex(ctx context.Context, block common.Hash, index uint64) (*simpleTransaction, error) {
var r *simpleTransaction
err := c.RPC.CallContext(ctx, &r, "eth_getTransactionByBlockHashAndIndex", block, hexutil.Uint64(index))
return r, err
}
func (c *client) getTransactionByBlockNumberAndIndex(ctx context.Context, block uint64, index uint64) (*simpleTransaction, error) {
var r *simpleTransaction
err := c.RPC.CallContext(ctx, &r, "eth_getTransactionByBlockNumberAndIndex", hexutil.Uint64(block), hexutil.Uint64(index))
return r, err
}
func (c *client) getBlockTransactionCountByHash(ctx context.Context, block common.Hash) (uint64, error) {
var r hexutil.Uint64
err := c.RPC.CallContext(ctx, &r, "eth_getBlockTransactionCountByHash", block)
return uint64(r), err
}
func (c *client) getBlockTransactionCountByNumber(ctx context.Context, block uint64) (uint64, error) {
var r hexutil.Uint64
err := c.RPC.CallContext(ctx, &r, "eth_getBlockTransactionCountByNumber", hexutil.Uint64(block))
return uint64(r), err
}
func (c *client) getBlockReceipts(ctx context.Context, arg any) ([]*types.Receipt, error) {
var result []*types.Receipt
err := c.RPC.CallContext(ctx, &result, "eth_getBlockReceipts", arg)
return result, err
}