Skip to content

execution/types/ethutils, node/privateapi: recover the receipt sender from the chain config - #24103

Open
lupin012 wants to merge 5 commits into
mainfrom
lupin012/fix_subscribe_receipt_nil_sender
Open

lupin012 wants to merge 5 commits into
mainfrom
lupin012/fix_subscribe_receipt_nil_sender

Conversation

@lupin012

@lupin012 lupin012 commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Part of #23986.

receiptNotificationToProto built its signer with types.MakeSigner(nil, ...), which returns a zero Signer that rejects every transaction type. Recovery worked only while the transaction still cached its sender; otherwise SubscribeReceiptsReply.From stayed nil, and MarshalSubscribeReceipt passed that nil to ConvertH160toAddress, which dereferences it. dbg.LogPanic recovers the panic but sends SIGINT, so a missing sender shuts the node down.

Fix: build the signer once from the chain config with types.LatestSigner, as geth does in FilterAPI.TransactionReceipts, and guard From on the RPC side the way To and ContractAddress already are.

Both tests fail on main: one segfaults in ConvertH160toAddress, the other signs a dynamic-fee transaction so its sender is not cached.

The rest of #23986 (log fields, effectiveGasPrice, one notification per block) changes the payload format and needs a proto field; it follows in a stacked PR.

… from the chain config

The receipts subscription built its signer with types.MakeSigner(nil, blockNum, 0),
which returns a zero Signer: no chain ID and every fork flag false, so it rejects
every transaction type. Sender recovery only worked while the transaction still
cached its sender; otherwise SubscribeReceiptsReply.From stayed nil.

MarshalSubscribeReceipt then passed that nil to ConvertH160toAddress, which
dereferences it. The subscription goroutine recovers the panic with dbg.LogPanic,
but that sends SIGINT, so a missing sender shuts the node down.

Build the signer once from the chain config, as geth does in
FilterAPI.TransactionReceipts, and guard From on the RPC side the way To and
ContractAddress already are.
Comment thread node/privateapi/receiptsfilter.go Outdated
The fallback rebuilt the zero signer this branch removes. Pass the chain
config from the three test call sites instead.
@lupin012
lupin012 marked this pull request as ready for review September 18, 2026 09:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused fix addresses both failure paths and includes appropriate regression coverage.

Pull request overview

This PR prevents receipt subscriptions from crashing when sender data is not cached by recovering senders with the configured chain signer and safely handling missing senders.

Changes:

  • Build and reuse a receipt signer from the chain configuration.
  • Guard missing From values during RPC receipt marshaling.
  • Add regression coverage and update test backend setup.
File summaries
File Description
node/privateapi/receiptsfilter.go Recovers receipt senders with the configured signer.
node/privateapi/receiptsfilter_test.go Tests uncached sender recovery.
node/privateapi/ethbackend.go Passes chain configuration to the receipt filter.
execution/types/ethutils/receipt.go Avoids dereferencing a missing sender.
execution/types/ethutils/receipt_test.go Tests marshaling without sender data.
rpc/jsonrpc/eth_subscribe_test.go Supplies chain configuration in subscription tests.
rpc/jsonrpc/debug_api_test.go Supplies chain configuration in debug tests.
cmd/rpcdaemon/rpcdaemontest/test_util.go Supplies chain configuration to test servers.
Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@AskAlexSharov

Copy link
Copy Markdown
Collaborator

The signer fix is right. One gap in the guard.

execution/types/ethutils/receipt.go:42 — an unrecoverable sender is now reported as 0x0000…0000, not as absent.

From is a value common.Address with json:"from" and no omitempty, so skipping the assignment leaves the zero address, which serialises as a real-looking "from":"0x0000000000000000000000000000000000000000". That is not what To and ContractAddress do: nonZeroAddress returns nil for both a nil and a zero input, so they come out as null.

It is still reachable after the signer fix. Sender errors on a transaction with no valid signature — the unsigned system/protocol shape (v=r=s=0, EIP-4788 and friends) that NewRPCTransaction has an explicit branch for. Such a transaction's sender is 0xffff…fffe, so 0x0 is a wrong answer a subscriber cannot tell from a real one. Make From a *common.Address and feed it through nonZeroAddress like the other two.

Also worth a line in the doc comment: types.LatestSigner dereferences config.ChainID, so NewEthBackendServer now panics at construction on a nil chainConfig where it used to accept one — four call sites in this diff were passing nil.

RPCReceipt.From was a value common.Address, so a receipt whose sender the
backend could not recover serialised as "from":"0x0000...0000", which a
subscriber cannot tell from a real zero address. Make it a *common.Address
and run the subscribe path through nonZeroAddress, as To and ContractAddress
already do.

MarshalReceipt keeps setting the pointer whenever the receipt is signed, so
eth_getTransactionReceipt and eth_getBlockReceipts still emit an address:
execution-apis lists "from" as required with no null variant, and geth also
falls back to the zero address there.

Document that NewEthBackendServer now needs a non-nil chainConfig.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants