ethclient: fix retrieval of pending block (#31504)

Since the block hash is not returned for pending blocks, ethclient cannot unmarshal into RPC block. This makes hash optional on rpc block and compute the hash locally for pending blocks to correctly key the tx sender cache.


a82303f4e3/internal/ethapi/api.go (L500-L504)

---------

Co-authored-by: lightclient <lightclient@protonmail.com>
This commit is contained in:
Shude Li 2025-05-02 21:19:54 +08:00 committed by GitHub
parent 701df4baad
commit af9673b143
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 37 additions and 6 deletions

View file

@ -131,7 +131,7 @@ func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumb
}
type rpcBlock struct {
Hash common.Hash `json:"hash"`
Hash *common.Hash `json:"hash"`
Transactions []rpcTransaction `json:"transactions"`
UncleHashes []common.Hash `json:"uncles"`
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
@ -158,6 +158,12 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
if err := json.Unmarshal(raw, &body); err != nil {
return nil, err
}
// Pending blocks don't return a block hash, compute it 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")
@ -199,7 +205,7 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
txs := make([]*types.Transaction, len(body.Transactions))
for i, tx := range body.Transactions {
if tx.From != nil {
setSenderFromServer(tx.tx, *tx.From, body.Hash)
setSenderFromServer(tx.tx, *tx.From, *body.Hash)
}
txs[i] = tx.tx
}

View file

@ -307,6 +307,12 @@ func testTransactionInBlock(t *testing.T, client *rpc.Client) {
if tx.Hash() != testTx2.Hash() {
t.Fatalf("unexpected transaction: %v", tx)
}
// Test pending block
_, err = ec.BlockByNumber(context.Background(), big.NewInt(int64(rpc.PendingBlockNumber)))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func testChainID(t *testing.T, client *rpc.Client) {
@ -619,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) {
@ -640,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)
}
@ -662,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 {