Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions eth/catalyst/simulated_beacon.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package catalyst

import (
"context"
"crypto/rand"
"crypto/sha256"
"errors"
Expand Down Expand Up @@ -256,9 +257,23 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
return err
}
c.lastBlockTime = payload.Timestamp
if err := c.syncLogProcessing(); err != nil {
log.Warn("Failed to process logs", "err", err)
}
return nil
}

func (c *SimulatedBeacon) syncLogProcessing() error {
blockHash := c.eth.BlockChain().CurrentBlock().Hash()

blockNumber := c.eth.BlockChain().CurrentBlock().Number.Uint64()

ctx := context.Background()

_, err := c.eth.APIBackend.GetLogs(ctx, blockHash, blockNumber)
return err
}

// loop runs the block production loop for non-zero period configuration
func (c *SimulatedBeacon) loop() {
timer := time.NewTimer(0)
Expand Down Expand Up @@ -307,6 +322,11 @@ func (c *SimulatedBeacon) Commit() common.Hash {
if err := c.sealBlock(withdrawals, uint64(time.Now().Unix())); err != nil {
log.Warn("Error performing sealing work", "err", err)
}

if err := c.syncLogProcessing(); err != nil {
log.Warn("Failed to process logs", "err", err)
}

return c.eth.BlockChain().CurrentBlock().Hash()
}

Expand Down
131 changes: 131 additions & 0 deletions ethclient/simulated/log_events_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package simulated

import (
"context"
"math/big"
"testing"
"time"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)

// TestLogEventsImmediateAvailability verifies that the fix for issue #31518
// ensures log events are immediately available after transaction commitment.
func TestLogEventsImmediateAvailability(t *testing.T) {
// Generate a random private key instead of using a hardcoded one
key, err := crypto.GenerateKey()
if err != nil {
t.Fatalf("Failed to generate random key: %v", err)
}
addr := crypto.PubkeyToAddress(key.PublicKey)

// Create a simulated backend with initial allocation
balance := new(big.Int).Mul(big.NewInt(1000), big.NewInt(params.Ether))
sim := NewBackend(types.GenesisAlloc{addr: {Balance: balance}})
defer sim.Close()

// Create a client to interact with the simulated backend
client := sim.Client()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// Deploy a simple contract that emits logs
contractCode := []byte{
// PUSH1 1, PUSH1 0, PUSH1 0, LOG0 - Simple bytecode that emits a log
0x60, 0x01, 0x60, 0x00, 0x60, 0x00, 0xa0,
// PUSH1 0, PUSH1 0, RETURN - Return empty
0x60, 0x00, 0x60, 0x00, 0xf3,
}

// Get the nonce for the sender
nonce, err := client.PendingNonceAt(ctx, addr)
if err != nil {
t.Fatalf("Failed to get nonce: %v", err)
}

// Get the current gas price
gasPrice, err := client.SuggestGasPrice(ctx)
if err != nil {
t.Fatalf("Failed to get gas price: %v", err)
}

// Create the contract creation transaction
tx := types.NewContractCreation(
nonce,
big.NewInt(0),
500000, // Gas limit
gasPrice,
contractCode,
)

// Sign the transaction
chainID, err := client.ChainID(ctx)
if err != nil {
t.Fatalf("Failed to get chain ID: %v", err)
}
signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainID), key)
if err != nil {
t.Fatalf("Failed to sign transaction: %v", err)
}

// Set up a filter to catch logs
query := ethereum.FilterQuery{
FromBlock: big.NewInt(0),
Topics: [][]common.Hash{},
}

// Send the transaction
t.Log("Sending contract creation transaction")
err = client.SendTransaction(ctx, signedTx)
if err != nil {
t.Fatalf("Failed to send transaction: %v", err)
}

// Commit the transaction - this should emit logs
t.Log("Committing block")
sim.Commit()

// Check for the transaction receipt
receipt, err := client.TransactionReceipt(ctx, signedTx.Hash())
if err != nil {
t.Fatalf("Failed to get transaction receipt: %v", err)
}
if receipt.Status != types.ReceiptStatusSuccessful {
t.Fatal("Transaction failed")
}

// Update the filter query to include the contract address
query.Addresses = []common.Address{receipt.ContractAddress}

// Check for logs immediately without any sleep or wait
t.Log("Checking for logs immediately after commit")
logs, err := client.FilterLogs(ctx, query)
if err != nil {
t.Fatalf("Error filtering logs: %v", err)
}

// Verify logs are found immediately
if len(logs) == 0 {
t.Fatal("No logs found immediately after commit")
}

t.Logf("Success: Found %d logs immediately after commit", len(logs))

// Additional verification - check log details
for i, log := range logs {
t.Logf("Log #%d - BlockNumber: %d, TxHash: %s, Index: %d",
i, log.BlockNumber, log.TxHash.Hex(), log.Index)
if log.BlockNumber != receipt.BlockNumber.Uint64() {
t.Errorf("Log block number mismatch: expected %d, got %d",
receipt.BlockNumber.Uint64(), log.BlockNumber)
}
if log.TxHash != signedTx.Hash() {
t.Errorf("Log transaction hash mismatch: expected %s, got %s",
signedTx.Hash().Hex(), log.TxHash.Hex())
}
}
}