mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 22:56:43 +00:00
eth/tracers: add bad block testing utilities and standard trace tests
This commit introduces functions for creating test transactions and setting up bad blocks for testing purposes. It also adds a new test case for standard tracing of bad blocks, ensuring that various scenarios are covered, including tracing with and without transactions, and handling non-existent blocks. Signed-off-by: katsumata <12413150+winor30@users.noreply.github.com>
This commit is contained in:
parent
0983cd789e
commit
0e136dbf48
1 changed files with 248 additions and 0 deletions
|
|
@ -48,6 +48,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi/override"
|
"github.com/ethereum/go-ethereum/internal/ethapi/override"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -1334,3 +1335,250 @@ func TestStandardTraceBlockToFile(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createTestTransactions creates test transactions for bad block testing
|
||||||
|
func createTestTransactions(t *testing.T, count int) []*types.Transaction {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
var (
|
||||||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
testAddr = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43")
|
||||||
|
)
|
||||||
|
|
||||||
|
var transactions []*types.Transaction
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
|
||||||
|
Nonce: uint64(i),
|
||||||
|
To: &testAddr,
|
||||||
|
Value: big.NewInt(int64(100 * (i + 1))), // Different values for each tx
|
||||||
|
Gas: 21000,
|
||||||
|
GasPrice: big.NewInt(1000000000),
|
||||||
|
Data: []byte{},
|
||||||
|
}), types.HomesteadSigner{}, key)
|
||||||
|
transactions = append(transactions, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactions
|
||||||
|
}
|
||||||
|
|
||||||
|
// createSingleTransaction creates a single transaction for testing
|
||||||
|
func createSingleTransaction(t *testing.T, nonce uint64, value *big.Int, gasLimit uint64, data []byte) *types.Transaction {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
var (
|
||||||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
testAddr = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43")
|
||||||
|
)
|
||||||
|
|
||||||
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
|
||||||
|
Nonce: nonce,
|
||||||
|
To: &testAddr,
|
||||||
|
Value: value,
|
||||||
|
Gas: gasLimit,
|
||||||
|
GasPrice: big.NewInt(1000000000),
|
||||||
|
Data: data,
|
||||||
|
}), types.HomesteadSigner{}, key)
|
||||||
|
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupBadBlock creates a bad block and stores it in the database for testing
|
||||||
|
func setupBadBlock(t *testing.T, backend *testBackend, transactions []*types.Transaction) (*types.Block, []common.Hash) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
var txHashes []common.Hash
|
||||||
|
for _, tx := range transactions {
|
||||||
|
txHashes = append(txHashes, tx.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the latest block from the test chain as the parent
|
||||||
|
latestBlock := backend.chain.CurrentBlock()
|
||||||
|
parentHash := latestBlock.Hash()
|
||||||
|
parentNumber := latestBlock.Number.Uint64()
|
||||||
|
|
||||||
|
// Create a bad block header that references the existing parent
|
||||||
|
badHeader := &types.Header{
|
||||||
|
Number: big.NewInt(int64(parentNumber + 1)),
|
||||||
|
Time: latestBlock.Time + 1,
|
||||||
|
Extra: []byte("bad block for testing"),
|
||||||
|
GasLimit: latestBlock.GasLimit,
|
||||||
|
GasUsed: uint64(len(transactions) * 21000),
|
||||||
|
Difficulty: big.NewInt(1000),
|
||||||
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
TxHash: types.DeriveSha(types.Transactions(transactions), trie.NewStackTrie(nil)),
|
||||||
|
ReceiptHash: types.EmptyReceiptsHash,
|
||||||
|
ParentHash: parentHash,
|
||||||
|
BaseFee: latestBlock.BaseFee, // Copy base fee to avoid nil pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the bad block
|
||||||
|
body := &types.Body{
|
||||||
|
Transactions: transactions,
|
||||||
|
Uncles: []*types.Header{},
|
||||||
|
}
|
||||||
|
badBlock := types.NewBlock(badHeader, body, nil, trie.NewStackTrie(nil))
|
||||||
|
|
||||||
|
// Store the bad block in the database
|
||||||
|
rawdb.WriteBadBlock(backend.chaindb, badBlock)
|
||||||
|
|
||||||
|
// Verify the bad block was stored
|
||||||
|
storedBlock := rawdb.ReadBadBlock(backend.chaindb, badBlock.Hash())
|
||||||
|
if storedBlock == nil {
|
||||||
|
t.Fatalf("Failed to store bad block in database")
|
||||||
|
}
|
||||||
|
|
||||||
|
return badBlock, txHashes
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStandardTraceBadBlockToFile(t *testing.T) {
|
||||||
|
// Set up accounts for testing
|
||||||
|
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
address := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
funds := big.NewInt(1000000000000000)
|
||||||
|
testAddr := common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43")
|
||||||
|
testCode := []byte{byte(vm.PUSH1), 0x42, byte(vm.POP), byte(vm.STOP)}
|
||||||
|
|
||||||
|
// Create test backend with proper accounts
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{
|
||||||
|
address: {Balance: funds},
|
||||||
|
testAddr: {
|
||||||
|
Code: testCode,
|
||||||
|
Balance: big.NewInt(0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
|
||||||
|
// Empty block generator
|
||||||
|
})
|
||||||
|
defer backend.chain.Stop()
|
||||||
|
|
||||||
|
api := NewAPI(backend)
|
||||||
|
|
||||||
|
// Create test transactions
|
||||||
|
testTxs := createTestTransactions(t, 2)
|
||||||
|
emptyTxs := []*types.Transaction{}
|
||||||
|
|
||||||
|
// Create bad blocks for testing
|
||||||
|
badBlockWithTxs, txHashes := setupBadBlock(t, backend, testTxs)
|
||||||
|
badBlockWithoutTxs, _ := setupBadBlock(t, backend, emptyTxs)
|
||||||
|
nonExistentHash := common.HexToHash("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
|
||||||
|
|
||||||
|
var testSuite = []struct {
|
||||||
|
name string
|
||||||
|
badBlockHash common.Hash
|
||||||
|
config *StdTraceConfig
|
||||||
|
expectError bool
|
||||||
|
expectedError string
|
||||||
|
expectedTraces []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "BasicBadBlockTrace",
|
||||||
|
badBlockHash: badBlockWithTxs.Hash(),
|
||||||
|
config: nil,
|
||||||
|
expectError: false,
|
||||||
|
expectedTraces: []string{
|
||||||
|
`{"pc":0,"op":96,"gas":"0x0","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1","error":"out of gas"}
|
||||||
|
{"output":"","gasUsed":"0x0","error":"out of gas"}
|
||||||
|
`,
|
||||||
|
`{"pc":0,"op":96,"gas":"0x0","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1","error":"out of gas"}
|
||||||
|
{"output":"","gasUsed":"0x0","error":"out of gas"}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "BadBlockNotFound",
|
||||||
|
badBlockHash: nonExistentHash,
|
||||||
|
config: nil,
|
||||||
|
expectError: true,
|
||||||
|
expectedError: "bad block 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef not found",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "BadBlockWithSpecificTransaction",
|
||||||
|
badBlockHash: badBlockWithTxs.Hash(),
|
||||||
|
config: &StdTraceConfig{
|
||||||
|
TxHash: txHashes[0], // Trace only first transaction
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
expectedTraces: []string{
|
||||||
|
`{"pc":0,"op":96,"gas":"0x0","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1","error":"out of gas"}
|
||||||
|
{"output":"","gasUsed":"0x0","error":"out of gas"}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "BadBlockWithoutTransactions",
|
||||||
|
badBlockHash: badBlockWithoutTxs.Hash(),
|
||||||
|
config: nil,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "BadBlockWithConfiguration",
|
||||||
|
badBlockHash: badBlockWithTxs.Hash(),
|
||||||
|
config: &StdTraceConfig{
|
||||||
|
Config: logger.Config{
|
||||||
|
DisableStack: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
expectedTraces: []string{
|
||||||
|
`{"pc":0,"op":96,"gas":"0x0","gasCost":"0x3","memSize":0,"stack":null,"depth":1,"refund":0,"opName":"PUSH1","error":"out of gas"}
|
||||||
|
{"output":"","gasUsed":"0x0","error":"out of gas"}
|
||||||
|
`,
|
||||||
|
`{"pc":0,"op":96,"gas":"0x0","gasCost":"0x3","memSize":0,"stack":null,"depth":1,"refund":0,"opName":"PUSH1","error":"out of gas"}
|
||||||
|
{"output":"","gasUsed":"0x0","error":"out of gas"}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testSuite {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
files, err := api.StandardTraceBadBlockToFile(context.Background(), tc.badBlockHash, tc.config)
|
||||||
|
|
||||||
|
// Check error expectations
|
||||||
|
if tc.expectError {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error, got nil")
|
||||||
|
}
|
||||||
|
if err.Error() != tc.expectedError {
|
||||||
|
t.Fatalf("Expected error '%s', got '%s'", tc.expectedError, err.Error())
|
||||||
|
}
|
||||||
|
if files != nil {
|
||||||
|
t.Fatalf("Expected nil files for error case, got %v", files)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check success case
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(files) != len(tc.expectedTraces) {
|
||||||
|
t.Fatalf("Expected %d trace files, got %d", len(tc.expectedTraces), len(files))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify trace content if files are expected
|
||||||
|
for i, fileName := range files {
|
||||||
|
data, err := os.ReadFile(fileName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to read trace file %s: %v", fileName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i < len(tc.expectedTraces) {
|
||||||
|
traceContent := string(data)
|
||||||
|
if traceContent != tc.expectedTraces[i] {
|
||||||
|
t.Fatalf("Trace file %d content mismatch.\nExpected:\n%s\nGot:\n%s", i, tc.expectedTraces[i], traceContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
os.Remove(fileName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue