Skip to content

Tx filtering at the prechecker#4300

Closed
MishkaRogachev wants to merge 7 commits intomasterfrom
tx-filtering-at-the-prechecker
Closed

Tx filtering at the prechecker#4300
MishkaRogachev wants to merge 7 commits intomasterfrom
tx-filtering-at-the-prechecker

Conversation

@MishkaRogachev
Copy link
Contributor

@MishkaRogachev MishkaRogachev commented Jan 30, 2026

Fixes NIT-4344

Transaction address filtering on pre-checker, mirroring sequencer-side behaviour

@codecov
Copy link

codecov bot commented Jan 30, 2026

Codecov Report

❌ Patch coverage is 9.30233% with 78 lines in your changes missing coverage. Please review.
✅ Project coverage is 32.88%. Comparing base (c885660) to head (0f1fe86).
⚠️ Report is 177 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4300      +/-   ##
==========================================
- Coverage   34.84%   32.88%   -1.96%     
==========================================
  Files         488      488              
  Lines       57870    57946      +76     
==========================================
- Hits        20163    19056    -1107     
- Misses      34134    35560    +1426     
+ Partials     3573     3330     -243     

@github-actions
Copy link
Contributor

github-actions bot commented Jan 30, 2026

❌ 13 Tests Failed:

Tests completed Failed Passed Skipped
4195 13 4182 0
View the top 3 failed tests by shortest run time
TestPreCheckerMaxAllowedGas
Stack Traces | 0.080s run time
=== RUN   TestPreCheckerMaxAllowedGas
=== PAUSE TestPreCheckerMaxAllowedGas
=== CONT  TestPreCheckerMaxAllowedGas
    tx_address_filter_test.go:639: goroutine 69 [running]:
        runtime/debug.Stack()
        	/opt/hostedtoolcache/go/1.25.6/x64/src/runtime/debug/stack.go:26 +0x5e
        github.com/offchainlabs/nitro/util/testhelpers.RequireImpl({0x40e5e10, 0xc000582fc0}, {0x40a2780, 0xc000940090}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/util/testhelpers/testhelpers.go:29 +0x55
        github.com/offchainlabs/nitro/system_tests.Require(0xc000582fc0, {0x40a2780, 0xc000940090}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/system_tests/common_test.go:2078 +0x5d
        github.com/offchainlabs/nitro/system_tests.TestPreCheckerMaxAllowedGas(0xc000582fc0)
        	/home/runner/work/nitro/nitro/system_tests/tx_address_filter_test.go:639 +0x496
        testing.tRunner(0xc000582fc0, 0x3d1dc88)
        	/opt/hostedtoolcache/go/1.25.6/x64/src/testing/testing.go:1934 +0xea
        created by testing.(*T).Run in goroutine 1
        	/opt/hostedtoolcache/go/1.25.6/x64/src/testing/testing.go:1997 +0x465
        
    tx_address_filter_test.go:639: �[31;1m [] intrinsic gas too low �[0;0m
--- FAIL: TestPreCheckerMaxAllowedGas (0.08s)
TestPreCheckerMaxAllowedGas
Stack Traces | 0.180s run time
=== RUN   TestPreCheckerMaxAllowedGas
=== PAUSE TestPreCheckerMaxAllowedGas
=== CONT  TestPreCheckerMaxAllowedGas
    tx_address_filter_test.go:639: goroutine 51 [running]:
        runtime/debug.Stack()
        	/opt/hostedtoolcache/go/1.25.6/x64/src/runtime/debug/stack.go:26 +0x5e
        github.com/offchainlabs/nitro/util/testhelpers.RequireImpl({0x43eab30, 0xc0004828c0}, {0x43a6a80, 0xc0008e2030}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/util/testhelpers/testhelpers.go:29 +0x9f
        github.com/offchainlabs/nitro/system_tests.Require(0xc0004828c0, {0x43a6a80, 0xc0008e2030}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/system_tests/common_test.go:2078 +0x5d
        github.com/offchainlabs/nitro/system_tests.TestPreCheckerMaxAllowedGas(0xc0004828c0)
        	/home/runner/work/nitro/nitro/system_tests/tx_address_filter_test.go:639 +0x4e9
        testing.tRunner(0xc0004828c0, 0x40206b8)
        	/opt/hostedtoolcache/go/1.25.6/x64/src/testing/testing.go:1934 +0xea
        created by testing.(*T).Run in goroutine 1
        	/opt/hostedtoolcache/go/1.25.6/x64/src/testing/testing.go:1997 +0x465
        
    tx_address_filter_test.go:639: �[31;1m [] intrinsic gas too low �[0;0m
--- FAIL: TestPreCheckerMaxAllowedGas (0.18s)
TestPreCheckerMaxAllowedGas
Stack Traces | 0.270s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
        runtime/debug.Stack()
        	/opt/hostedtoolcache/go/1.25.6/x64/src/runtime/debug/stack.go:26 +0x5e
        github.com/offchainlabs/nitro/util/testhelpers.RequireImpl({0x43eb810, 0xc0ba5021c0}, {0x43a7760, 0xc09ecc06c0}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/util/testhelpers/testhelpers.go:29 +0x9f
        github.com/offchainlabs/nitro/system_tests.Require(0xc0ba5021c0, {0x43a7760, 0xc09ecc06c0}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/system_tests/common_test.go:2078 +0x5d
        github.com/offchainlabs/nitro/system_tests.TestPreCheckerMaxAllowedGas(0xc0ba5021c0)
        	/home/runner/work/nitro/nitro/system_tests/tx_address_filter_test.go:639 +0x4e9
        testing.tRunner(0xc0ba5021c0, 0x4021378)
        	/opt/hostedtoolcache/go/1.25.6/x64/src/testing/testing.go:1934 +0xea
        created by testing.(*T).Run in goroutine 1
        	/opt/hostedtoolcache/go/1.25.6/x64/src/testing/testing.go:1997 +0x465
        
    tx_address_filter_test.go:639: �[31;1m [] intrinsic gas too low �[0;0m
INFO [02-10|16:46:37.068] Waiting background transaction indexer to exit
INFO [02-10|16:46:37.068] Persisting dirty state                   head=2  root=aa1659..be6cdc layers=2
INFO [02-10|16:46:37.068] Persisted dirty state to disk            size=8.65KiB  elapsed="268.753µs"
INFO [02-10|16:46:37.068] Blockchain stopped
WARN [02-10|16:46:37.076] XClaimJustID returned empty response when indicating heartbeat msgID=1770741996347-0
--- FAIL: TestPreCheckerMaxAllowedGas (0.27s)

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

@MishkaRogachev MishkaRogachev force-pushed the tx-filtering-at-the-prechecker branch from 9166e24 to ceb7022 Compare February 2, 2026 20:15
@MishkaRogachev MishkaRogachev marked this pull request as ready for review February 4, 2026 09:32
@MishkaRogachev MishkaRogachev changed the title Proposal for pre-checker filtering Tx filtering at the prechecker Feb 4, 2026
Copy link
Member

@Tristan-Wilson Tristan-Wilson left a comment

Choose a reason for hiding this comment

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

The change largely looks good, great tests, just some "policy" comments about the max gas and fail open.

Make TxPreCheckerMaxAllowedGas configurable instead of a hardcoded
constant. Transactions exceeding the configured max gas are now rejected
outright (fail closed) rather than partially executed (fail open), since
we cannot guarantee full address coverage for transactions we can't
fully simulate.

The speculative execution is moved after the balance check so that
underfunded transactions are rejected cheaply before spinning up EVM
simulation.

Wire TxPreChecker.SetAddressChecker in production alongside the existing
ExecEngine.SetAddressChecker call now that PR #4235 has merged.

- Add MaxAllowedGas field to TxPreCheckerConfig (default 50M, hot-reloadable)
- Register max-allowed-gas flag and EventFilter flags in TxPreCheckerConfigAddOptions
- Reject txs with gas > MaxAllowedGas before speculative execution
- Move ApplyTransactionFilter block after balance check
- Wire TxPreChecker.SetAddressChecker in ExecutionNode.Start
- Replace deleted txfilter.NewStaticAsyncChecker with newHashedChecker in test
- Add TestPreCheckerMaxAllowedGas test
@Tristan-Wilson
Copy link
Member

Fixed my comments, over to @diegoximenes to review.

Copy link
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.

LGTM, with some nitpicks and doubts

f.Int64(prefix+".required-state-age", DefaultTxPreCheckerConfig.RequiredStateAge, "how long ago should the storage conditions from eth_SendRawTransactionConditional be true, 0 = don't check old state")
f.Uint(prefix+".required-state-max-blocks", DefaultTxPreCheckerConfig.RequiredStateMaxBlocks, "maximum number of blocks to look back while looking for the <required-state-age> seconds old state, 0 = don't limit the search")
f.Bool(prefix+".apply-transaction-filter", DefaultTxPreCheckerConfig.ApplyTransactionFilter, "whether to apply the transaction filter while pre-checking")
f.Uint64(prefix+".max-allowed-gas", DefaultTxPreCheckerConfig.MaxAllowedGas, "max gas allowed for speculative execution during transaction filtering; txs exceeding this are rejected")
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: Config name could mentioned that this is only to useful for filtering.

}

if tx.Gas() > config.MaxAllowedGas {
log.Warn("rejecting transaction that exceeds prechecker max gas for filtering",
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick:
Why warn?
How about creating a metric for that, and lowering log level?

Comment on lines +183 to +191
deadlineCtx, cancel := context.WithTimeout(context.Background(), time.Second*5)
go func() {
<-deadlineCtx.Done()
if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) {
// Stop evm execution. Note cancellation is not necessarily immediate.
evm.Cancel()
}
}()
defer cancel()
Copy link
Contributor

Choose a reason for hiding this comment

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

Why this cancel logic is needed?

Comment on lines +202 to +204
_, err = core.ApplyMessage(evm, message, gasPool)
// Ignore execution errors, since we care only about touched addresses
_ = err
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
_, err = core.ApplyMessage(evm, message, gasPool)
// Ignore execution errors, since we care only about touched addresses
_ = err
// Ignore execution errors, since we care only about touched addresses
_, _ = core.ApplyMessage(evm, message, gasPool)

@diegoximenes diegoximenes assigned tsahee and unassigned diegoximenes Feb 10, 2026
Copy link
Member

@Tristan-Wilson Tristan-Wilson left a comment

Choose a reason for hiding this comment

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

We likely need a different approach to handle retriable redemptions.

@Tristan-Wilson
Copy link
Member

Closing this in favor of ProduceBlockAdvanced dryrun approach #4382

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.

4 participants