From fc341f484d85e67631dba5989eb32fdb72ef3c55 Mon Sep 17 00:00:00 2001 From: lightclient Date: Thu, 1 May 2025 14:11:22 -0600 Subject: [PATCH] ethclient: use pending block hash to key tx sender cache --- ethclient/ethclient.go | 9 ++++++++- ethclient/ethclient_test.go | 27 +++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 32e1f4d7b8..0e295710c8 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -158,6 +158,13 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface if err := json.Unmarshal(raw, &body); err != nil { return nil, err } + // Pending blocks do not return a block hash, set it to empty hash explicitly + // for sender caching. + if body.Hash == nil { + tmp := head.Hash() + body.Hash = &tmp + } + // Quick-verify transaction and uncle lists. This mostly helps with debugging the server. if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 { return nil, errors.New("server returned non-empty uncle list but block header indicates no uncles") @@ -198,7 +205,7 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface // Fill the sender cache of transactions in the block. txs := make([]*types.Transaction, len(body.Transactions)) for i, tx := range body.Transactions { - if tx.From != nil && body.Hash != nil { + if tx.From != nil { setSenderFromServer(tx.tx, *tx.From, *body.Hash) } txs[i] = tx.tx diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index b3da2494b6..8e70177944 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -625,6 +625,21 @@ func testAtFunctions(t *testing.T, client *rpc.Client) { if gas != 21000 { t.Fatalf("unexpected gas limit: %v", gas) } + + // Verify that sender address of pending transaction is saved in cache. + pendingBlock, err := ec.BlockByNumber(context.Background(), big.NewInt(int64(rpc.PendingBlockNumber))) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + // No additional RPC should be required, ensure the server is not asked by + // canceling the context. + sender, err := ec.TransactionSender(newCanceledContext(), pendingBlock.Transactions()[0], pendingBlock.Hash(), 0) + if err != nil { + t.Fatal("unable to recover sender:", err) + } + if sender != testAddr { + t.Fatal("wrong sender:", sender) + } } func testTransactionSender(t *testing.T, client *rpc.Client) { @@ -646,10 +661,7 @@ func testTransactionSender(t *testing.T, client *rpc.Client) { // The sender address is cached in tx1, so no additional RPC should be required in // TransactionSender. Ensure the server is not asked by canceling the context here. - canceledCtx, cancel := context.WithCancel(context.Background()) - cancel() - <-canceledCtx.Done() // Ensure the close of the Done channel - sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0) + sender1, err := ec.TransactionSender(newCanceledContext(), tx1, block2.Hash(), 0) if err != nil { t.Fatal(err) } @@ -668,6 +680,13 @@ func testTransactionSender(t *testing.T, client *rpc.Client) { } } +func newCanceledContext() context.Context { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + <-ctx.Done() // Ensure the close of the Done channel + return ctx +} + func sendTransaction(ec *ethclient.Client) error { chainID, err := ec.ChainID(context.Background()) if err != nil {