Skip to content

TxPreChecker: report filtered txs to filtering-report service#4653

Open
MishkaRogachev wants to merge 6 commits intomasterfrom
prechecker-calls-cmdfiltering-report
Open

TxPreChecker: report filtered txs to filtering-report service#4653
MishkaRogachev wants to merge 6 commits intomasterfrom
prechecker-calls-cmdfiltering-report

Conversation

@MishkaRogachev
Copy link
Copy Markdown
Contributor

@MishkaRogachev MishkaRogachev commented Apr 17, 2026

Fixes NIT-4645
Pulls in OffchainLabs/go-ethereum#657

TxPreChecker now reports filtered transactions to the filtering-report service via FilteringReportRPCClient when its address-filter dry-run rejects a tx. Records flow back through gasestimator.Run, avoiding a second statedb query. eth_estimateGas does not pass through TxPreChecker, so it won't trigger reports.

@MishkaRogachev MishkaRogachev changed the base branch from master to add-report-filtered-txn-api April 17, 2026 15:22
@MishkaRogachev MishkaRogachev changed the base branch from add-report-filtered-txn-api to master April 17, 2026 15:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds reporting of address-filtered transactions from TxPreChecker to the filtering-report service (per NIT-4645), including a system test to validate the end-to-end RPC call.

Changes:

  • Extend TxPreChecker to emit FilteredTxReport via FilteringReportRPCClient when dry-run address filtering rejects a transaction.
  • Update tx filtering plumbing to return filtered address records (for inclusion in reports).
  • Add a system test with a local JSON-RPC server to capture and assert reported filtered transactions.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
system_tests/prechecker_filter_test.go Adds an end-to-end test server/collector and a new test asserting reports are emitted for filtered txs.
execution/gethexec/tx_pre_checker.go Wires a filtering-report client into TxPreChecker and builds/sends FilteredTxReport on filter rejection.
execution/gethexec/tx_filterer.go Changes CheckFiltered to return filtered address records along with the filtering error.
execution/gethexec/node.go Passes the filtering-report client into the TxPreChecker constructor.
changelog/mrogachev-nit-4645.md Adds a changelog entry describing the new reporting behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +355 to +360
report := addressfilter.FilteredTxReport{
ID: uuid.Must(uuid.NewV7()).String(),
TxHash: tx.Hash(),
TxRLP: txRLP,
FilteredAddresses: filteredAddresses,
BlockNumber: header.Number.Uint64(),
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

addressfilter.FilteredTxReport.TxRLP is hexutil.Bytes, but tx.MarshalBinary() returns a []byte. Assigning txRLP directly to TxRLP won't compile; convert it to hexutil.Bytes (or change txRLP variable type) before assigning.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

false-alert

Comment on lines +355 to +356
report := addressfilter.FilteredTxReport{
ID: uuid.Must(uuid.NewV7()).String(),
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

uuid.Must(uuid.NewV7()) can panic if UUID generation fails. Since this runs on the tx submission path, a panic would take down the process; handle the error and either skip reporting (log at debug/error) or fall back to a different ID generation strategy.

Suggested change
report := addressfilter.FilteredTxReport{
ID: uuid.Must(uuid.NewV7()).String(),
reportID, err := uuid.NewV7()
if err != nil {
return fmt.Errorf("failed to generate filtered tx report id: %w", err)
}
report := addressfilter.FilteredTxReport{
ID: reportID.String(),

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

false-alert

Comment thread execution/gethexec/tx_pre_checker.go Outdated
Comment on lines +367 to +369
promise := c.filteringReportRPCClient.ReportFilteredTransactions([]addressfilter.FilteredTxReport{report})
go func(txHash common.Hash) {
if _, err := promise.Await(context.Background()); err != nil {
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

The goroutine waiting on promise.Await(context.Background()) has no timeout/cancellation tied to the caller, so if the RPC call stalls for an extended time this can accumulate background goroutines under load. Consider awaiting with a bounded context (e.g., a short timeout) or logging errors via the promise thread without spawning an extra goroutine.

Suggested change
promise := c.filteringReportRPCClient.ReportFilteredTransactions([]addressfilter.FilteredTxReport{report})
go func(txHash common.Hash) {
if _, err := promise.Await(context.Background()); err != nil {
const reportAwaitTimeout = 5 * time.Second
promise := c.filteringReportRPCClient.ReportFilteredTransactions([]addressfilter.FilteredTxReport{report})
go func(txHash common.Hash) {
reportCtx, cancel := context.WithTimeout(context.Background(), reportAwaitTimeout)
defer cancel()
if _, err := promise.Await(reportCtx); err != nil {

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

Comment thread execution/gethexec/tx_pre_checker.go Outdated
Comment on lines +95 to +97
func (c *TxPreChecker) SetFilteringReportRPCClient(client *FilteringReportRPCClient) {
c.filteringReportRPCClient = client
}
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

SetFilteringReportRPCClient mutates TxPreChecker.filteringReportRPCClient without synchronization. Since TxPreChecker methods can be called concurrently, calling this setter after the prechecker is in use would introduce a data race. If runtime mutation is required, guard it (mutex/atomic) or document/enforce that it must only be called during initialization before any tx handling starts.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

@MishkaRogachev MishkaRogachev Apr 17, 2026

Choose a reason for hiding this comment

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

called just once durning node startup

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 17, 2026

Codecov Report

❌ Patch coverage is 16.66667% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 33.67%. Comparing base (96ae52a) to head (d91668e).

Additional details and impacted files
@@             Coverage Diff             @@
##           master    #4653       +/-   ##
===========================================
- Coverage   52.85%   33.67%   -19.19%     
===========================================
  Files         501      501               
  Lines       60225    60251       +26     
===========================================
- Hits        31834    20290    -11544     
- Misses      23277    36400    +13123     
+ Partials     5114     3561     -1553     

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 17, 2026

❌ 15 Tests Failed:

Tests completed Failed Passed Skipped
9845 15 9830 0
View the top 3 failed tests by shortest run time
TestInboxReaderBlobFailureWithDelayedMessage
Stack Traces | 10.530s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
INFO [04-21|10:26:09.051]  - Istanbul:                    #0       
INFO [04-21|10:26:09.051]  - Muir Glacier:                #0       
INFO [04-21|10:26:09.051]  - Berlin:                      #0       
INFO [04-21|10:26:09.051]  - London:                      #0       
INFO [04-21|10:26:09.051] 
INFO [04-21|10:26:09.051] Merge configured:
INFO [04-21|10:26:09.051]  - Total terminal difficulty:  <nil>
INFO [04-21|10:26:09.051] 
INFO [04-21|10:26:09.051] Post-Merge hard forks (timestamp based):
INFO [04-21|10:26:09.051] 
INFO [04-21|10:26:09.051] All fork specifications can be found at https://ethereum.github.io/execution-specs/src/ethereum/forks/
INFO [04-21|10:26:09.051] 
INFO [04-21|10:26:09.051] ---------------------------------------------------------------------------------------------------------------------------------------------------------
INFO [04-21|10:26:09.051] 
INFO [04-21|10:26:09.052] Loaded most recent local block           number=0   hash=580f71..41aa6a age=57y1mo2w
INFO [04-21|10:26:09.052] Initialized transaction indexer          range="last 126230400 blocks"
INFO [04-21|10:26:09.052] Deploying seq inbox bufferable
INFO [04-21|10:26:09.052] Starting work on payload                 id=0x035cbe3f54b2519c
WARN [04-21|10:26:09.053] Sequencer ReadFromTxQueueTimeout is higher than MaxBlockSpeed ReadFromTxQueueTimeout=1s MaxBlockSpeed=10ms
--- FAIL: TestInboxReaderBlobFailureWithDelayedMessage (10.53s)
TestAnyTrustRekey
Stack Traces | 11.320s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
INFO [04-21|10:26:09.888] Starting peer-to-peer node               instance=test-stack-name/linux-amd64/go1.25.9
WARN [04-21|10:26:09.888] P2P server will be useless, neither dialing nor listening
INFO [04-21|10:26:09.888] Started P2P networking                   self=enode://9757ab55fb8df78df39204c6314577a4199a5131b1c6ece19f0a6d3865d7b91ba59f40f69bd726d6ae0d706fcb6587a3d4f4488e8337103b4f680b3848c49e62@127.0.0.1:0
INFO [04-21|10:26:09.888] New Key                                  name=Faucet              Address=0xaF24Ca6c2831f4d4F629418b50C227DF0885613A
INFO [04-21|10:26:09.888] InboxTracker                             SequencerBatchCount=0
INFO [04-21|10:26:09.888] Updated payload                          id=0x034b2f97f9ae12d8 number=15  hash=cb8864..8a0587 txs=1   withdrawals=0 gas=2,252,567  fees=2.252567e-06   root=612efc..baab6e elapsed=6.788ms
INFO [04-21|10:26:09.888] New Key                                  name=RollupOwner         Address=0x57Ff0F473737a1c161bfF9efDF016F7991585088
INFO [04-21|10:26:09.888] Stopping work on payload                 id=0x034b2f97f9ae12d8 reason=delivery
INFO [04-21|10:26:09.888] New Key                                  name=Sequencer           Address=0xb386a74Dcab67b66F8AC07B4f08365d37495Dd23
INFO [04-21|10:26:09.889] New Key                                  name=Validator           Address=0x83FFCFaCE2Fb0E1286686815503608A16EF41e47
INFO [04-21|10:26:09.889] New Key                                  name=User                Address=0x7E23C8862920797d81916d62c274dd9217113e28
INFO [04-21|10:26:09.890] Allocated trie memory caches             clean=154.00MiB dirty=256.00MiB
INFO [04-21|10:26:09.890] State schema set to default              scheme=path
INFO [04-21|10:26:09.900] Submitted transaction                    hash=0x5b0dd944873b87eaa18a1b3e549f7abadaee17c66f34a9809e72e1527edd4922 from=0x26E554a8acF9003b83495c7f45F06edCB803d4e3 nonce=2   recipient=0x0000000000000000000000000000000000000070 value=0
INFO [04-21|10:26:09.902] New local node record                    seq=1,776,767,169,901 id=0161f7f9b2dc96a8   ip=127.0.0.1 udp=0 tcp=0
INFO [04-21|10:26:09.902] Started P2P networking                   self=enode://d13fb8a0ac3765773f6c07c9847b6df7c38e07100b609f9529b3612836e444b101038667f23531baaf645c6cd4d967cb074d81dd5a53f5ad2fa8627d3472918c@127.0.0.1:0
WARN [04-21|10:26:09.904] Sanitizing invalid node buffer size      provided=1024.00MiB updated=256.00MiB
INFO [04-21|10:26:09.904] Load database journal from disk
--- FAIL: TestAnyTrustRekey (11.32s)
WARN [04-21|10:26:09.904] Head block is not reachable
TestSequencerInboxReader
Stack Traces | 12.550s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
DEBUG[04-21|10:21:16.789] Journaled pathdb diff layer              root=2bf40e..983869 parent=0e7dbe..567efc id=186                block=185
DEBUG[04-21|10:21:16.789] Journaled pathdb diff layer              root=68bd4a..a84607 parent=2bf40e..983869 id=187                block=186
DEBUG[04-21|10:21:16.789] Journaled pathdb diff layer              root=1d8fa0..2b85e6 parent=68bd4a..a84607 id=188                block=187
DEBUG[04-21|10:21:16.789] Journaled pathdb diff layer              root=4c2575..ade5ba parent=1d8fa0..2b85e6 id=189                block=188
DEBUG[04-21|10:21:16.789] Journaled pathdb diff layer              root=6d6492..32bca8 parent=4c2575..ade5ba id=190                block=189
DEBUG[04-21|10:21:16.789] Journaled pathdb diff layer              root=997e84..c90825 parent=6d6492..32bca8 id=191                block=190
DEBUG[04-21|10:21:16.789] Journaled pathdb diff layer              root=cef5a3..842ae4 parent=997e84..c90825 id=192                block=191
DEBUG[04-21|10:21:16.789] Journaled pathdb diff layer              root=728498..b93d82 parent=cef5a3..842ae4 id=193                block=192
DEBUG[04-21|10:21:16.790] Journaled pathdb diff layer              root=63a821..a6eb6e parent=728498..b93d82 id=194                block=193
DEBUG[04-21|10:21:16.790] Journaled pathdb diff layer              root=6aaec1..f5f20d parent=63a821..a6eb6e id=195                block=194
DEBUG[04-21|10:21:16.790] Journaled pathdb diff layer              root=18ef10..43702c parent=6aaec1..f5f20d id=196                block=195
DEBUG[04-21|10:21:16.790] Journaled pathdb diff layer              root=6631da..2afd87 parent=18ef10..43702c id=197                block=196
DEBUG[04-21|10:21:16.790] Journaled pathdb diff layer              root=a0904d..f72385 parent=6631da..2afd87 id=198                block=197
DEBUG[04-21|10:21:16.790] Journaled pathdb diff layer              root=205cb7..a0e198 parent=a0904d..f72385 id=199                block=198
DEBUG[04-21|10:21:16.790] Journaled pathdb diff layer              root=b4fc52..9cb187 parent=205cb7..a0e198 id=200                block=199
DEBUG[04-21|10:21:16.790] Journaled pathdb diff layer              root=42f322..f72d46 parent=b4fc52..9cb187 id=201                block=200
INFO [04-21|10:21:16.790] Persisted dirty state to disk            size=591.67KiB elapsed=5.859ms
INFO [04-21|10:21:16.790] Blockchain stopped
TRACE[04-21|10:21:16.790] P2P networking is spinning down
--- FAIL: TestSequencerInboxReader (12.55s)

📣 Thoughts on this report? Let Codecov know! | Powered by Codecov

Comment thread execution/gethexec/tx_pre_checker.go Outdated
Comment thread execution/gethexec/tx_pre_checker.go Outdated
Comment thread execution/gethexec/tx_pre_checker.go Outdated
Copy link
Copy Markdown
Contributor

@diegoximenes diegoximenes left a comment

Choose a reason for hiding this comment

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

Let's also add the tests discussed out of GitHub.

Comment thread execution/gethexec/tx_pre_checker.go Outdated
promise := c.filteringReportRPCClient.ReportFilteredTransactions([]addressfilter.FilteredTxReport{report})
txHash := tx.Hash()
c.filteringReportRPCClient.LaunchThread(func(ctx context.Context) {
awaitCtx, cancel := context.WithTimeout(ctx, filteredTxReportDeliveryTimeout)
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.

RPC client, inside of the filteringReportRPCClient, already have a timeout procedure that takes into consideration a CLI config, no need for this one.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed in f7a5194

Comment thread execution/gethexec/tx_pre_checker.go Outdated
Backend: c.backend,
RunScheduledTxes: retryables.RunScheduledTxes,
ReportFilteredTx: func(_ context.Context, records []filter.FilteredAddressRecord) {
if reportErr := c.reportFilteredTx(tx, header, records); reportErr != nil {
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.

nitpick: c.reportFilteredTx could not return an error, and this log would be handled in c.reportFilteredTx instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed in f7a5194

Copy link
Copy Markdown
Contributor

@diegoximenes diegoximenes left a comment

Choose a reason for hiding this comment

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

I didn't do a full review round this time, but as discussed here and in a meeting, since design review can take a while, let's implement the discussed test scenarios in this PR

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