|
1 | 1 | package evmrpc_test
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "strconv" |
4 | 5 | "testing"
|
5 | 6 | "time"
|
6 | 7 |
|
7 | 8 | "github.com/ethereum/go-ethereum/common"
|
| 9 | + ethtypes "github.com/ethereum/go-ethereum/core/types" |
| 10 | + testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper" |
| 11 | + "github.com/sei-protocol/sei-chain/x/evm/keeper" |
| 12 | + evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" |
8 | 13 | "github.com/stretchr/testify/require"
|
9 | 14 | )
|
10 | 15 |
|
@@ -433,3 +438,179 @@ func TestGetLogsBlockHashIsNotZero(t *testing.T) {
|
433 | 438 | "log %d should be from block 2", i)
|
434 | 439 | }
|
435 | 440 | }
|
| 441 | + |
| 442 | +func TestGetLogsTransactionIndexConsistency(t *testing.T) { |
| 443 | + t.Parallel() |
| 444 | + |
| 445 | + // Test that eth_getLogs returns logs with transaction indices that match eth_getBlockByNumber |
| 446 | + // This is a regression test for the transaction index mismatch issue |
| 447 | + |
| 448 | + // Try multiple blocks to find one with both logs and EVM transactions |
| 449 | + testBlocks := []string{"0x2", "0x8", "0x64", "0x67"} // Block 2, 8, 100, 103 |
| 450 | + |
| 451 | + var logs []interface{} |
| 452 | + var transactions []interface{} |
| 453 | + var blockHex string |
| 454 | + |
| 455 | + for _, blockNum := range testBlocks { |
| 456 | + // Get logs for this block |
| 457 | + filterCriteria := map[string]interface{}{ |
| 458 | + "fromBlock": blockNum, |
| 459 | + "toBlock": blockNum, |
| 460 | + } |
| 461 | + resObj := sendRequestGood(t, "getLogs", filterCriteria) |
| 462 | + blockLogs := resObj["result"].([]interface{}) |
| 463 | + |
| 464 | + if len(blockLogs) == 0 { |
| 465 | + continue // No logs in this block, try next |
| 466 | + } |
| 467 | + |
| 468 | + // Get the block to see what transaction indices eth_getBlockByNumber returns |
| 469 | + blockRes := sendRequestGood(t, "getBlockByNumber", blockNum, true) |
| 470 | + block := blockRes["result"].(map[string]interface{}) |
| 471 | + blockTxs := block["transactions"].([]interface{}) |
| 472 | + |
| 473 | + if len(blockTxs) > 0 { |
| 474 | + // Found a block with both logs and EVM transactions |
| 475 | + logs = blockLogs |
| 476 | + transactions = blockTxs |
| 477 | + blockHex = blockNum |
| 478 | + t.Logf("Using block %s with %d logs and %d EVM transactions", blockHex, len(logs), len(transactions)) |
| 479 | + break |
| 480 | + } |
| 481 | + } |
| 482 | + |
| 483 | + // Skip test if we can't find a suitable block (this might happen in minimal test environments) |
| 484 | + if len(logs) == 0 || len(transactions) == 0 { |
| 485 | + t.Skip("No block found with both logs and EVM transactions - skipping transaction index consistency test") |
| 486 | + return |
| 487 | + } |
| 488 | + |
| 489 | + // Create a map of transaction hash to EVM transaction index from the block |
| 490 | + hashToEvmIndex := make(map[string]int) |
| 491 | + for i, txInterface := range transactions { |
| 492 | + tx := txInterface.(map[string]interface{}) |
| 493 | + txHash := tx["hash"].(string) |
| 494 | + hashToEvmIndex[txHash] = i |
| 495 | + } |
| 496 | + |
| 497 | + // Verify that each log's transactionIndex is a valid EVM index (0 to len(transactions)-1) |
| 498 | + for i, logInterface := range logs { |
| 499 | + log := logInterface.(map[string]interface{}) |
| 500 | + txHash := log["transactionHash"].(string) |
| 501 | + logTxIndex := log["transactionIndex"].(string) |
| 502 | + |
| 503 | + // Convert hex string to int for comparison |
| 504 | + logTxIndexInt, err := strconv.ParseInt(logTxIndex[2:], 16, 64) |
| 505 | + require.NoError(t, err, "should be able to parse transaction index from log %d", i) |
| 506 | + |
| 507 | + // Key assertion: transactionIndex should be a valid EVM transaction index |
| 508 | + require.GreaterOrEqual(t, logTxIndexInt, int64(0), |
| 509 | + "log %d: transactionIndex %d should be >= 0", i, logTxIndexInt) |
| 510 | + require.Less(t, logTxIndexInt, int64(len(transactions)), |
| 511 | + "log %d: transactionIndex %d should be < %d (number of EVM transactions)", |
| 512 | + i, logTxIndexInt, len(transactions)) |
| 513 | + |
| 514 | + // If the transaction exists in the block, verify indices match |
| 515 | + if expectedEvmIndex, exists := hashToEvmIndex[txHash]; exists { |
| 516 | + require.Equal(t, int64(expectedEvmIndex), logTxIndexInt, |
| 517 | + "log %d: transactionIndex from eth_getLogs (%d) should match EVM transaction index from eth_getBlockByNumber (%d) for tx %s", |
| 518 | + i, logTxIndexInt, expectedEvmIndex, txHash) |
| 519 | + } |
| 520 | + } |
| 521 | + |
| 522 | + // Additional check: ensure transaction indices are reasonable for the block structure |
| 523 | + // Block 8 should have mixed transaction types, so EVM transaction indices should be sequential |
| 524 | + txIndicesFound := make(map[int64]bool) |
| 525 | + for _, logInterface := range logs { |
| 526 | + log := logInterface.(map[string]interface{}) |
| 527 | + logTxIndex := log["transactionIndex"].(string) |
| 528 | + logTxIndexInt, _ := strconv.ParseInt(logTxIndex[2:], 16, 64) |
| 529 | + txIndicesFound[logTxIndexInt] = true |
| 530 | + } |
| 531 | + |
| 532 | + // We should not see indices that are >= number of EVM transactions |
| 533 | + for txIndex := range txIndicesFound { |
| 534 | + require.Less(t, txIndex, int64(len(transactions)), |
| 535 | + "no log should have transactionIndex %d when there are only %d EVM transactions", |
| 536 | + txIndex, len(transactions)) |
| 537 | + } |
| 538 | +} |
| 539 | + |
| 540 | +func TestCollectLogsEvmTransactionIndex(t *testing.T) { |
| 541 | + t.Parallel() |
| 542 | + |
| 543 | + // This is a unit test for the core logic that collectLogs implements |
| 544 | + // It tests that transaction indices are set correctly for EVM transactions |
| 545 | + |
| 546 | + // Set up the test environment - use the correct return values from MockEVMKeeper |
| 547 | + k, ctx := testkeeper.MockEVMKeeper() |
| 548 | + |
| 549 | + // Create a mock block with mixed transaction types (similar to block 2 in our test data) |
| 550 | + // We'll simulate the transaction hashes that getTxHashesFromBlock would return |
| 551 | + evmTxHashes := []common.Hash{ |
| 552 | + common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111"), // EVM tx index 0 |
| 553 | + common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222"), // EVM tx index 1 |
| 554 | + common.HexToHash("0x3333333333333333333333333333333333333333333333333333333333333333"), // EVM tx index 2 |
| 555 | + } |
| 556 | + |
| 557 | + // Create mock receipts with logs |
| 558 | + for i, txHash := range evmTxHashes { |
| 559 | + receipt := &evmtypes.Receipt{ |
| 560 | + TxHashHex: txHash.Hex(), |
| 561 | + TransactionIndex: uint32(i + 10), // Use high absolute indices to simulate mixed tx types |
| 562 | + BlockNumber: 2, |
| 563 | + Logs: []*evmtypes.Log{ |
| 564 | + { |
| 565 | + Address: "0x1111111111111111111111111111111111111112", |
| 566 | + Topics: []string{"0x0000000000000000000000000000000000000000000000000000000000000123"}, |
| 567 | + Data: []byte("test data"), |
| 568 | + Index: 0, |
| 569 | + }, |
| 570 | + }, |
| 571 | + LogsBloom: make([]byte, 256), // Empty bloom for simplicity |
| 572 | + } |
| 573 | + |
| 574 | + // Fill bloom filter to match our test filters |
| 575 | + receipt.LogsBloom[0] = 0xFF // Simple bloom that will match any filter |
| 576 | + |
| 577 | + k.MockReceipt(ctx, txHash, receipt) |
| 578 | + } |
| 579 | + |
| 580 | + // Test the core logic that collectLogs implements |
| 581 | + // This simulates what collectLogs does for each EVM transaction |
| 582 | + var collectedLogs []*ethtypes.Log |
| 583 | + evmTxIndex := 0 |
| 584 | + totalLogs := uint(0) |
| 585 | + |
| 586 | + for _, txHash := range evmTxHashes { |
| 587 | + receipt, err := k.GetReceipt(ctx, txHash) |
| 588 | + require.NoError(t, err, "should be able to get receipt for tx %s", txHash.Hex()) |
| 589 | + |
| 590 | + // This simulates keeper.GetLogsForTx |
| 591 | + logs := keeper.GetLogsForTx(receipt, totalLogs) |
| 592 | + |
| 593 | + // This is the key part we're testing: setting the correct EVM transaction index |
| 594 | + for _, log := range logs { |
| 595 | + log.TxIndex = uint(evmTxIndex) // This should override receipt.TransactionIndex |
| 596 | + collectedLogs = append(collectedLogs, log) |
| 597 | + } |
| 598 | + |
| 599 | + totalLogs += uint(len(receipt.Logs)) |
| 600 | + evmTxIndex++ |
| 601 | + } |
| 602 | + |
| 603 | + // Verify that the transaction indices are set correctly |
| 604 | + require.Equal(t, len(evmTxHashes), len(collectedLogs), "should have one log per transaction") |
| 605 | + |
| 606 | + for i, log := range collectedLogs { |
| 607 | + // This is the main assertion: TxIndex should be the EVM transaction index (0, 1, 2) |
| 608 | + // NOT the absolute transaction index (10, 11, 12) |
| 609 | + require.Equal(t, uint(i), log.TxIndex, |
| 610 | + "log %d should have EVM transaction index %d, but got %d", i, i, log.TxIndex) |
| 611 | + |
| 612 | + // Verify it's NOT using the absolute transaction index |
| 613 | + require.NotEqual(t, uint(i+10), log.TxIndex, |
| 614 | + "log %d should not use absolute transaction index %d", i, i+10) |
| 615 | + } |
| 616 | +} |
0 commit comments