From 0e136dbf483cd92eb9e8ab301510a06d010b6ce9 Mon Sep 17 00:00:00 2001 From: katsumata <12413150+winor30@users.noreply.github.com> Date: Mon, 9 Jun 2025 01:52:28 +0900 Subject: [PATCH 1/3] 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> --- eth/tracers/api_test.go | 248 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index d20d5eaff656..d5ea1d780aac 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -48,6 +48,7 @@ import ( "github.com/ethereum/go-ethereum/internal/ethapi/override" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/trie" ) 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) + } + }) + } +} From 297c4e7fd1f22c6df83f27128243f8484953cfdb Mon Sep 17 00:00:00 2001 From: katsumata <12413150+winor30@users.noreply.github.com> Date: Mon, 9 Jun 2025 02:01:35 +0900 Subject: [PATCH 2/3] eth/tracers: refactor transaction creation and improve bad block setup This commit refactors the transaction creation logic in `createTestTransactions` to use a range loop for better readability. It also updates the bad block setup to use the latest parent block's properties consistently. Additionally, it removes unused functions and comments to clean up the code. Signed-off-by: katsumata <12413150+winor30@users.noreply.github.com> --- eth/tracers/api_test.go | 37 +++++++------------------------------ 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index d5ea1d780aac..5fca3addc4fb 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -1346,7 +1346,7 @@ func createTestTransactions(t *testing.T, count int) []*types.Transaction { ) var transactions []*types.Transaction - for i := 0; i < count; i++ { + for i := range count { tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ Nonce: uint64(i), To: &testAddr, @@ -1361,27 +1361,6 @@ func createTestTransactions(t *testing.T, count int) []*types.Transaction { 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() @@ -1392,23 +1371,23 @@ func setupBadBlock(t *testing.T, backend *testBackend, transactions []*types.Tra } // Get the latest block from the test chain as the parent - latestBlock := backend.chain.CurrentBlock() - parentHash := latestBlock.Hash() - parentNumber := latestBlock.Number.Uint64() + parentBlock := backend.chain.CurrentBlock() + parentHash := parentBlock.Hash() + parentNumber := parentBlock.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, + Time: parentBlock.Time + 1, Extra: []byte("bad block for testing"), - GasLimit: latestBlock.GasLimit, + GasLimit: parentBlock.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 + BaseFee: parentBlock.BaseFee, // Copy base fee to avoid nil pointer } // Create the bad block @@ -1438,7 +1417,6 @@ func TestStandardTraceBadBlockToFile(t *testing.T) { 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{ @@ -1562,7 +1540,6 @@ func TestStandardTraceBadBlockToFile(t *testing.T) { 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 { From 8b3c88c088505b4d28d7f57935a2d75d3fddaf1d Mon Sep 17 00:00:00 2001 From: katsumata <12413150+winor30@users.noreply.github.com> Date: Mon, 9 Jun 2025 21:29:27 +0900 Subject: [PATCH 3/3] eth/tracers: add cleanup for temporary files in bad block trace tests This commit adds a cleanup function to remove temporary files created during the `TestStandardTraceBadBlockToFile` test. The cleanup is performed using `t.Cleanup` to ensure that files are deleted after the test execution, improving resource management and preventing potential file clutter. Signed-off-by: katsumata <12413150+winor30@users.noreply.github.com> --- eth/tracers/api_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 5fca3addc4fb..ec5d7bda3b87 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -1516,6 +1516,11 @@ func TestStandardTraceBadBlockToFile(t *testing.T) { t.Parallel() files, err := api.StandardTraceBadBlockToFile(context.Background(), tc.badBlockHash, tc.config) + t.Cleanup(func() { + for _, fileName := range files { + os.Remove(fileName) + } + }) // Check error expectations if tc.expectError { @@ -1552,9 +1557,6 @@ func TestStandardTraceBadBlockToFile(t *testing.T) { t.Fatalf("Trace file %d content mismatch.\nExpected:\n%s\nGot:\n%s", i, tc.expectedTraces[i], traceContent) } } - - // Clean up - os.Remove(fileName) } }) }