Skip to content

Commit 9adad59

Browse files
committed
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>
1 parent 35dd84c commit 9adad59

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed

eth/tracers/api_test.go

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import (
4848
"github.com/ethereum/go-ethereum/internal/ethapi/override"
4949
"github.com/ethereum/go-ethereum/params"
5050
"github.com/ethereum/go-ethereum/rpc"
51+
"github.com/ethereum/go-ethereum/trie"
5152
)
5253

5354
var (
@@ -1334,3 +1335,250 @@ func TestStandardTraceBlockToFile(t *testing.T) {
13341335
}
13351336
}
13361337
}
1338+
1339+
// createTestTransactions creates test transactions for bad block testing
1340+
func createTestTransactions(t *testing.T, count int) []*types.Transaction {
1341+
t.Helper()
1342+
1343+
var (
1344+
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
1345+
testAddr = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43")
1346+
)
1347+
1348+
var transactions []*types.Transaction
1349+
for i := 0; i < count; i++ {
1350+
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
1351+
Nonce: uint64(i),
1352+
To: &testAddr,
1353+
Value: big.NewInt(int64(100 * (i + 1))), // Different values for each tx
1354+
Gas: 21000,
1355+
GasPrice: big.NewInt(1000000000),
1356+
Data: []byte{},
1357+
}), types.HomesteadSigner{}, key)
1358+
transactions = append(transactions, tx)
1359+
}
1360+
1361+
return transactions
1362+
}
1363+
1364+
// createSingleTransaction creates a single transaction for testing
1365+
func createSingleTransaction(t *testing.T, nonce uint64, value *big.Int, gasLimit uint64, data []byte) *types.Transaction {
1366+
t.Helper()
1367+
1368+
var (
1369+
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
1370+
testAddr = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43")
1371+
)
1372+
1373+
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
1374+
Nonce: nonce,
1375+
To: &testAddr,
1376+
Value: value,
1377+
Gas: gasLimit,
1378+
GasPrice: big.NewInt(1000000000),
1379+
Data: data,
1380+
}), types.HomesteadSigner{}, key)
1381+
1382+
return tx
1383+
}
1384+
1385+
// setupBadBlock creates a bad block and stores it in the database for testing
1386+
func setupBadBlock(t *testing.T, backend *testBackend, transactions []*types.Transaction) (*types.Block, []common.Hash) {
1387+
t.Helper()
1388+
1389+
var txHashes []common.Hash
1390+
for _, tx := range transactions {
1391+
txHashes = append(txHashes, tx.Hash())
1392+
}
1393+
1394+
// Get the latest block from the test chain as the parent
1395+
latestBlock := backend.chain.CurrentBlock()
1396+
parentHash := latestBlock.Hash()
1397+
parentNumber := latestBlock.Number.Uint64()
1398+
1399+
// Create a bad block header that references the existing parent
1400+
badHeader := &types.Header{
1401+
Number: big.NewInt(int64(parentNumber + 1)),
1402+
Time: latestBlock.Time + 1,
1403+
Extra: []byte("bad block for testing"),
1404+
GasLimit: latestBlock.GasLimit,
1405+
GasUsed: uint64(len(transactions) * 21000),
1406+
Difficulty: big.NewInt(1000),
1407+
UncleHash: types.EmptyUncleHash,
1408+
TxHash: types.DeriveSha(types.Transactions(transactions), trie.NewStackTrie(nil)),
1409+
ReceiptHash: types.EmptyReceiptsHash,
1410+
ParentHash: parentHash,
1411+
BaseFee: latestBlock.BaseFee, // Copy base fee to avoid nil pointer
1412+
}
1413+
1414+
// Create the bad block
1415+
body := &types.Body{
1416+
Transactions: transactions,
1417+
Uncles: []*types.Header{},
1418+
}
1419+
badBlock := types.NewBlock(badHeader, body, nil, trie.NewStackTrie(nil))
1420+
1421+
// Store the bad block in the database
1422+
rawdb.WriteBadBlock(backend.chaindb, badBlock)
1423+
1424+
// Verify the bad block was stored
1425+
storedBlock := rawdb.ReadBadBlock(backend.chaindb, badBlock.Hash())
1426+
if storedBlock == nil {
1427+
t.Fatalf("Failed to store bad block in database")
1428+
}
1429+
1430+
return badBlock, txHashes
1431+
}
1432+
1433+
func TestStandardTraceBadBlockToFile(t *testing.T) {
1434+
// Set up accounts for testing
1435+
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
1436+
address := crypto.PubkeyToAddress(key.PublicKey)
1437+
funds := big.NewInt(1000000000000000)
1438+
testAddr := common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43")
1439+
testCode := []byte{byte(vm.PUSH1), 0x42, byte(vm.POP), byte(vm.STOP)}
1440+
1441+
// Create test backend with proper accounts
1442+
genesis := &core.Genesis{
1443+
Config: params.TestChainConfig,
1444+
Alloc: types.GenesisAlloc{
1445+
address: {Balance: funds},
1446+
testAddr: {
1447+
Code: testCode,
1448+
Balance: big.NewInt(0),
1449+
},
1450+
},
1451+
}
1452+
backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
1453+
// Empty block generator
1454+
})
1455+
defer backend.chain.Stop()
1456+
1457+
api := NewAPI(backend)
1458+
1459+
// Create test transactions
1460+
testTxs := createTestTransactions(t, 2)
1461+
emptyTxs := []*types.Transaction{}
1462+
1463+
// Create bad blocks for testing
1464+
badBlockWithTxs, txHashes := setupBadBlock(t, backend, testTxs)
1465+
badBlockWithoutTxs, _ := setupBadBlock(t, backend, emptyTxs)
1466+
nonExistentHash := common.HexToHash("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
1467+
1468+
var testSuite = []struct {
1469+
name string
1470+
badBlockHash common.Hash
1471+
config *StdTraceConfig
1472+
expectError bool
1473+
expectedError string
1474+
expectedTraces []string
1475+
}{
1476+
{
1477+
name: "BasicBadBlockTrace",
1478+
badBlockHash: badBlockWithTxs.Hash(),
1479+
config: nil,
1480+
expectError: false,
1481+
expectedTraces: []string{
1482+
`{"pc":0,"op":96,"gas":"0x0","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1","error":"out of gas"}
1483+
{"output":"","gasUsed":"0x0","error":"out of gas"}
1484+
`,
1485+
`{"pc":0,"op":96,"gas":"0x0","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1","error":"out of gas"}
1486+
{"output":"","gasUsed":"0x0","error":"out of gas"}
1487+
`,
1488+
},
1489+
},
1490+
{
1491+
name: "BadBlockNotFound",
1492+
badBlockHash: nonExistentHash,
1493+
config: nil,
1494+
expectError: true,
1495+
expectedError: "bad block 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef not found",
1496+
},
1497+
{
1498+
name: "BadBlockWithSpecificTransaction",
1499+
badBlockHash: badBlockWithTxs.Hash(),
1500+
config: &StdTraceConfig{
1501+
TxHash: txHashes[0], // Trace only first transaction
1502+
},
1503+
expectError: false,
1504+
expectedTraces: []string{
1505+
`{"pc":0,"op":96,"gas":"0x0","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1","error":"out of gas"}
1506+
{"output":"","gasUsed":"0x0","error":"out of gas"}
1507+
`,
1508+
},
1509+
},
1510+
{
1511+
name: "BadBlockWithoutTransactions",
1512+
badBlockHash: badBlockWithoutTxs.Hash(),
1513+
config: nil,
1514+
expectError: false,
1515+
},
1516+
{
1517+
name: "BadBlockWithConfiguration",
1518+
badBlockHash: badBlockWithTxs.Hash(),
1519+
config: &StdTraceConfig{
1520+
Config: logger.Config{
1521+
DisableStack: true,
1522+
},
1523+
},
1524+
expectError: false,
1525+
expectedTraces: []string{
1526+
`{"pc":0,"op":96,"gas":"0x0","gasCost":"0x3","memSize":0,"stack":null,"depth":1,"refund":0,"opName":"PUSH1","error":"out of gas"}
1527+
{"output":"","gasUsed":"0x0","error":"out of gas"}
1528+
`,
1529+
`{"pc":0,"op":96,"gas":"0x0","gasCost":"0x3","memSize":0,"stack":null,"depth":1,"refund":0,"opName":"PUSH1","error":"out of gas"}
1530+
{"output":"","gasUsed":"0x0","error":"out of gas"}
1531+
`,
1532+
},
1533+
},
1534+
}
1535+
1536+
for _, tc := range testSuite {
1537+
t.Run(tc.name, func(t *testing.T) {
1538+
t.Parallel()
1539+
1540+
files, err := api.StandardTraceBadBlockToFile(context.Background(), tc.badBlockHash, tc.config)
1541+
1542+
// Check error expectations
1543+
if tc.expectError {
1544+
if err == nil {
1545+
t.Fatalf("Expected error, got nil")
1546+
}
1547+
if err.Error() != tc.expectedError {
1548+
t.Fatalf("Expected error '%s', got '%s'", tc.expectedError, err.Error())
1549+
}
1550+
if files != nil {
1551+
t.Fatalf("Expected nil files for error case, got %v", files)
1552+
}
1553+
return
1554+
}
1555+
1556+
// Check success case
1557+
if err != nil {
1558+
t.Fatalf("Unexpected error: %v", err)
1559+
}
1560+
1561+
if len(files) != len(tc.expectedTraces) {
1562+
t.Fatalf("Expected %d trace files, got %d", len(tc.expectedTraces), len(files))
1563+
}
1564+
1565+
// Verify trace content if files are expected
1566+
for i, fileName := range files {
1567+
data, err := os.ReadFile(fileName)
1568+
if err != nil {
1569+
t.Fatalf("Failed to read trace file %s: %v", fileName, err)
1570+
}
1571+
1572+
if i < len(tc.expectedTraces) {
1573+
traceContent := string(data)
1574+
if traceContent != tc.expectedTraces[i] {
1575+
t.Fatalf("Trace file %d content mismatch.\nExpected:\n%s\nGot:\n%s", i, tc.expectedTraces[i], traceContent)
1576+
}
1577+
}
1578+
1579+
// Clean up
1580+
os.Remove(fileName)
1581+
}
1582+
})
1583+
}
1584+
}

0 commit comments

Comments
 (0)