Skip to content

Fix nil batchFetcher dereference in FillInBatchGasFields#4540

Open
joshuacolvin0 wants to merge 15 commits intomasterfrom
fix-nil-batch-fetcher
Open

Fix nil batchFetcher dereference in FillInBatchGasFields#4540
joshuacolvin0 wants to merge 15 commits intomasterfrom
fix-nil-batch-fetcher

Conversation

@joshuacolvin0
Copy link
Copy Markdown
Member

@joshuacolvin0 joshuacolvin0 commented Mar 23, 2026

FillInBatchGasFields(nil) panicked for BatchPostingReport messages because FromFallibleBatchFetcher(nil) produced a non-nil closure that bypassed the downstream nil guard and panicked on invocation.

Changes

  • FillInBatchGasFields: explicit nil check — returns ErrNilBatchFetcher for BatchPostingReport, nil for all other kinds; only wraps non-nil fetchers via FromFallibleBatchFetcher
  • FillInBatchGasFieldsWithParentBlock: restores original nil→skip behavior (return nil) so LookupMessagesInRange callers that don't need batch gas fields can safely pass nil without failing on incidental BatchPostingReport messages in the same block
  • arbnode/inbox_tracker.go, arbnode/transaction_streamer.go: pass real fetchers instead of nil at call sites that process BatchPostingReport messages
  • cmd/nitro/init/init.go: defensive stub fetcher for the deployment block (no BatchPostingReport expected there); updated comment
  • Sentinel errors: ErrNilBatchFetcher, ErrBatchHashMismatch, ErrParseBatchPostingReport with %w wrapping for programmatic checking
  • Logging: warning/error for previously silent failure paths (replay binary, GetMessage without inboxReader)
  • Unit tests: 22 tests covering nil fetcher, hash mismatch, truncated L2msg, fetcher errors with/without legacy cost, idempotency, empty batch data, and all message kinds

Behavioral contract

FillInBatchGasFields(nil)
  → BatchPostingReport  ⇒ ErrNilBatchFetcher   // was: panic
  → other kinds         ⇒ nil                  // unchanged

FillInBatchGasFieldsWithParentBlock(nil, ...)
  → any kind            ⇒ nil (skip)           // restored; was temporarily an error

No consensus changes — replay binary behavior is identical.

… is nil

Guard against nil batchFetcher in FillInBatchGasFields before passing it
to FromFallibleBatchFetcher, which wraps a nil function in a non-nil
closure that bypasses the downstream nil check and panics when called.
Also skip FillInBatchGasFields in ParseIncomingL1Message when
batchFetcher is nil.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@joshuacolvin0 joshuacolvin0 marked this pull request as ready for review March 23, 2026 03:45
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 23, 2026

Codecov Report

❌ Patch coverage is 22.22222% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 34.08%. Comparing base (5b41808) to head (e734095).
⚠️ Report is 232 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4540      +/-   ##
==========================================
- Coverage   34.52%   34.08%   -0.45%     
==========================================
  Files         498      497       -1     
  Lines       59092    59169      +77     
==========================================
- Hits        20400    20165     -235     
- Misses      35067    35439     +372     
+ Partials     3625     3565      -60     

@joshuacolvin0 joshuacolvin0 assigned bragaigor and unassigned KolbyML Mar 23, 2026
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 23, 2026

❌ 11 Tests Failed:

Tests completed Failed Passed Skipped
4789 11 4778 0
View the top 3 failed tests by shortest run time
TestAliasingFlaky
Stack Traces | -0.000s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
=== PAUSE TestAliasingFlaky
=== CONT  TestAliasingFlaky
    common_test.go:777: BuildL1 deployConfig: DeployBold=true, DeployReferenceDAContracts=false
INFO [04-08|17:32:34.958] Submitted contract creation              hash=0x399184f9977f0f6ea1e1f807698efdd88b4fcbb85106214486ecbfdf28b60683 from=0x57Ff0F473737a1c161bfF9efDF016F7991585088 nonce=0  contract=0xA46C59ce2FCaF445F96f66F0411e06A94D34BF45 value=0
WARN [04-08|17:32:34.958] Served eth_getTransactionReceipt         reqid=7 duration="34.491µs" err="transaction indexing is in progress"  errdata="\"transaction indexing is in progress\""
INFO [04-08|17:32:34.958] Starting work on payload                 id=0x03acfed193017d10
INFO [04-08|17:32:34.958] Updated payload                          id=0x03acfed193017d10                      number=1 hash=dca97a..543d9e txs=1  withdrawals=0 gas=854,353 fees=8.54353e-07 root=e6fce3..c1a87f elapsed="419.689µs"
INFO [04-08|17:32:34.959] Stopping work on payload                 id=0x03acfed193017d10                      reason=delivery
INFO [04-08|17:32:34.959] Imported new potential chain segment     number=1 hash=dca97a..543d9e blocks=1  txs=1  mgas=0.854 elapsed="582.083µs" mgasps=1467.751 triediffs=3.08KiB triedirty=0.00B
INFO [04-08|17:32:34.959] Chain head was updated                   number=1 hash=dca97a..543d9e root=e6fce3..c1a87f elapsed="140.21µs"
INFO [04-08|17:32:34.959] Indexed transactions                     blocks=2  txs=1  tail=0 elapsed="77.677µs"
INFO [04-08|17:32:34.960] Starting peer-to-peer node               instance=test-stack-name/linux-amd64/go1.25.8
WARN [04-08|17:32:34.960] P2P server will be useless, neither dialing nor listening
INFO [04-08|17:32:34.961] Deploying rollup creator
INFO [04-08|17:32:34.961] Deploying bridge creator contracts
INFO [04-08|17:32:34.961] Deploying bridge template
INFO [04-08|17:32:34.961] New local node record                    seq=1,775,669,554,961 id=95523a8f3c05e6d6                        ip=127.0.0.1 udp=0 tcp=0
INFO [04-08|17:32:34.961] Started P2P networking                   self=enode://bd0354d2b2e30bc4310c602908a607ab55fc337639d0b1f122892c8ae9bafa3e150bb7327ed40f8208a402d7f7c8a0a59463dda206c9fd57341e32b2fa32540c@127.0.0.1:0
INFO [04-08|17:32:34.961] Started log indexer
WARN [04-08|17:32:34.961] Getting file info                        dir= error="stat : no such file or directory"
TestBatchPosterL1SurplusMatchesBatchGasFlaky
Stack Traces | 0.550s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
panic: runtime error: invalid memory address or nil pointer dereference [recovered, repanicked]
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x20f3632]

goroutine 82 [running]:
testing.tRunner.func1.2({0x3866ac0, 0x62fd9b0})
	/opt/hostedtoolcache/go/1.25.8/x64/src/testing/testing.go:1872 +0x237
testing.tRunner.func1()
	/opt/hostedtoolcache/go/1.25.8/x64/src/testing/testing.go:1875 +0x35b
panic({0x3866ac0?, 0x62fd9b0?})
	/opt/hostedtoolcache/go/1.25.8/x64/src/runtime/panic.go:783 +0x132
github.com/offchainlabs/nitro/arbnode.(*InboxTracker).GetBatchCount(0x172d7900?)
	/home/runner/work/nitro/nitro/arbnode/inbox_tracker.go:210 +0x12
github.com/offchainlabs/nitro/arbnode.(*InboxTracker).FindInboxBatchContainingMessage(0x0, 0x7)
	/home/runner/work/nitro/nitro/arbnode/inbox_tracker.go:225 +0x2f
github.com/offchainlabs/nitro/system_tests.TestBatchPosterL1SurplusMatchesBatchGasFlaky(0xc000424a80)
	/home/runner/work/nitro/nitro/system_tests/batch_poster_test.go:839 +0x725
testing.tRunner(0xc000424a80, 0x4242718)
	/opt/hostedtoolcache/go/1.25.8/x64/src/testing/testing.go:1934 +0xea
created by testing.(*T).Run in goroutine 1
	/opt/hostedtoolcache/go/1.25.8/x64/src/testing/testing.go:1997 +0x465
TestRedisProduceComplex/one_producer,_all_consumers_are_active
Stack Traces | 1.230s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
�[36mDEBUG�[0m[04-08|17:28:18.608] consumer: xdel                           �[36mcid�[0m=644f93f7-9d32-485b-8d19-bfdd1d114f57 �[36mmessageId�[0m=1775669297463-0
�[36mDEBUG�[0m[04-08|17:28:18.608] consumer: xack                           �[36mcid�[0m=688046d8-a5ec-4795-8d56-d5602f7fac6d �[36mmessageId�[0m=1775669297463-3
�[36mDEBUG�[0m[04-08|17:28:18.608] consumer: xdel                           �[36mcid�[0m=85363775-dc45-4c08-bbf2-54ef5e6d8afc �[36mmessageId�[0m=1775669297463-2
�[36mDEBUG�[0m[04-08|17:28:18.609] consumer: xdel                           �[36mcid�[0m=c2176ad0-5d4e-44bb-b81d-75b131cc11c2 �[36mmessageId�[0m=1775669297480-0
�[36mDEBUG�[0m[04-08|17:28:18.609] consumer: xack                           �[36mcid�[0m=c139ae56-2c97-49d3-964f-208d25350ef5 �[36mmessageId�[0m=1775669297463-4
�[36mDEBUG�[0m[04-08|17:28:18.609] consumer: xdel                           �[36mcid�[0m=688046d8-a5ec-4795-8d56-d5602f7fac6d �[36mmessageId�[0m=1775669297463-3
�[36mDEBUG�[0m[04-08|17:28:18.609] Redis stream consuming                   �[36mconsumer_id�[0m=e164e715-eb46-42a9-86c0-2856fd45b262 �[36mmessage_id�[0m=1775669297480-3
�[36mDEBUG�[0m[04-08|17:28:18.610] consumer: setting result                 �[36mcid�[0m=e164e715-eb46-42a9-86c0-2856fd45b262 �[36mmsgIdInStream�[0m=1775669297480-3  �[36mresultKeyInRedis�[0m=result-key:stream:9667fc30-cbe5-44cd-8646-e9cb8ea2b687.1775669297480-3
�[36mDEBUG�[0m[04-08|17:28:18.610] Redis stream consuming                   �[36mconsumer_id�[0m=17780f18-db31-4ca8-8e0d-ea60ba3f2f22 �[36mmessage_id�[0m=1775669297480-2
�[36mDEBUG�[0m[04-08|17:28:18.610] consumer: setting result                 �[36mcid�[0m=17780f18-db31-4ca8-8e0d-ea60ba3f2f22 �[36mmsgIdInStream�[0m=1775669297480-2  �[36mresultKeyInRedis�[0m=result-key:stream:9667fc30-cbe5-44cd-8646-e9cb8ea2b687.1775669297480-2
�[36mDEBUG�[0m[04-08|17:28:18.610] consumer: xack                           �[36mcid�[0m=0ae97787-3348-44c4-b610-d6e33ff8c9c2 �[36mmessageId�[0m=1775669297480-1
�[36mDEBUG�[0m[04-08|17:28:18.610] consumer: xdel                           �[36mcid�[0m=c139ae56-2c97-49d3-964f-208d25350ef5 �[36mmessageId�[0m=1775669297463-4
�[36mDEBUG�[0m[04-08|17:28:18.610] consumer: xack                           �[36mcid�[0m=e164e715-eb46-42a9-86c0-2856fd45b262 �[36mmessageId�[0m=1775669297480-3
�[36mDEBUG�[0m[04-08|17:28:18.610] consumer: xack                           �[36mcid�[0m=17780f18-db31-4ca8-8e0d-ea60ba3f2f22 �[36mmessageId�[0m=1775669297480-2
�[36mDEBUG�[0m[04-08|17:28:18.610] consumer: xdel                           �[36mcid�[0m=0ae97787-3348-44c4-b610-d6e33ff8c9c2 �[36mmessageId�[0m=1775669297480-1
�[36mDEBUG�[0m[04-08|17:28:18.611] consumer: xdel                           �[36mcid�[0m=17780f18-db31-4ca8-8e0d-ea60ba3f2f22 �[36mmessageId�[0m=1775669297480-2
�[36mDEBUG�[0m[04-08|17:28:18.611] consumer: xdel                           �[36mcid�[0m=e164e715-eb46-42a9-86c0-2856fd45b262 �[36mmessageId�[0m=1775669297480-3
�[36mDEBUG�[0m[04-08|17:28:18.663] checkResponses                           �[36mresponded�[0m=92 �[36merrored�[0m=0 �[36mchecked�[0m=98
�[36mDEBUG�[0m[04-08|17:28:18.672] redis producer: check responses starting
--- FAIL: TestRedisProduceComplex/one_producer,_all_consumers_are_active (1.23s)

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

bragaigor
bragaigor previously approved these changes Mar 23, 2026
Copy link
Copy Markdown
Contributor

@bragaigor bragaigor left a comment

Choose a reason for hiding this comment

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

LGTM

@bragaigor bragaigor assigned joshuacolvin0 and unassigned bragaigor Mar 23, 2026
Comment thread arbos/arbostypes/incomingmessage.go Outdated
if err != nil {
return nil, err
if batchFetcher != nil {
err = msg.FillInBatchGasFields(batchFetcher)
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.

nil check should be in FillInBatchGasFields, so that if the fields need to be filled the function will error out and not silently create wrong messages.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

done

Both FillInBatchGasFields and FillInBatchGasFieldsWithParentBlock now
return an error when batchFetcher is nil and the message kind is
BatchPostingReport, instead of silently leaving gas fields unpopulated.
Move the nil guard to the caller in delayed.go and pass the fetcher
directly in inbox_tracker.go.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
joshuacolvin0 and others added 6 commits March 23, 2026 14:02
Let the nil check live in FillInBatchGasFieldsWithParentBlock so that
FillInBatchGasFields delegates without duplicating the logic. This way a
nil fetcher only errors when the message is a BatchPostingReport and the
fields actually need to be filled, rather than being checked upfront.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Remove caller-side nil guards for batchFetcher

Now that FillInBatchGasFieldsWithParentBlock handles nil batchFetcher
internally, remove the redundant nil guards in delayed.go and
transaction_streamer.go so that all call sites go through the
centralized check.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Provide real fetcher in reorg path and add tests for fetcher error paths

Wire up a real batchFetcher in the reorg-resequence LookupMessagesInRange
call so BatchPostingReport messages get their gas fields filled.

Add tests for fetcher error fallback: when LegacyBatchGasCost is already
set the function succeeds (pre-arbos50 compat), and when neither field is
set the fetcher error is propagated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Add tests for pre-set BatchDataStats and truncated L2msg edge cases

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test that FillInBatchGasFields populates BatchDataStats and
LegacyBatchGasCost with a working fetcher, and that it passes
msg.Header.BlockNumber as parentChainBlockNumber through the
FromFallibleBatchFetcher wrapper.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
joshuacolvin0 and others added 3 commits March 27, 2026 12:51
…ntBlock

Include parentChainBlockNumber in the nil-fetcher error and add batchNum
and err to the batch data fetch warning for easier debugging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bragaigor
bragaigor previously approved these changes Mar 30, 2026
Copy link
Copy Markdown
Contributor

@bragaigor bragaigor left a comment

Choose a reason for hiding this comment

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

LGTM, only nitpick comments against err == nil test checks

Comment thread arbos/arbostypes/incomingmessage_test.go Outdated
Comment thread arbos/arbostypes/incomingmessage_test.go
Comment thread arbos/arbostypes/incomingmessage_test.go
Comment thread arbos/arbostypes/incomingmessage_test.go
@bragaigor bragaigor assigned joshuacolvin0 and unassigned bragaigor Mar 30, 2026
- Add ErrNilBatchFetcher, ErrBatchHashMismatch, ErrParseBatchPostingReport
  sentinel errors with %w wrapping for programmatic error checking
- Remove redundant nil guard in delayed.go since
  FillInBatchGasFieldsWithParentBlock handles non-BatchPostingReport
- Add edge-case tests: nil fetcher data, empty batch, idempotent calls,
  WithParentBlock nil data, and all non-BatchPostingReport message kinds
- Add warning logs for swallowed FillInBatchGasFields errors in replay
  binary and GetMessage; fix misleading reorg log message
- Use log.Error instead of log.Warn in replay binary (log level filtering)
- Provide stub batchFetcher in init.go to satisfy non-nil contract

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bragaigor
bragaigor previously approved these changes Mar 31, 2026
Copy link
Copy Markdown
Contributor

@bragaigor bragaigor left a comment

Choose a reason for hiding this comment

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

LGTM

- Provide non-nil batchFetcher in system tests that call LookupMessagesInRange
- Add delayedMessagesRead to replay log.Error for debuggability
- Trim verbose comments in init.go and remove redundant comment block in
  transaction_streamer.go
- Minor test comment fixes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bragaigor
Copy link
Copy Markdown
Contributor

@copilot please review

…, revert test stubs to nil

Agent-Logs-Url: https://github.com/OffchainLabs/nitro/sessions/184c0914-dd59-4497-a54d-b33a83f9d832

Co-authored-by: bragaigor <5835477+bragaigor@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI commented Apr 8, 2026

@copilot please review

I found and fixed a CI failure introduced by the previous commit. The root cause was that FillInBatchGasFieldsWithParentBlock(nil) was changed to return ErrNilBatchFetcher for BatchPostingReport messages, which broke test callers (getLookupL2Tx in retryable_test.go and delayed_message_filter_test.go) that pass nil to LookupMessagesInRange. Those callers don't need batch gas fields—they just look up specific message types—but their blocks can contain BatchPostingReport messages that triggered the new error.

Fix in commit e734095:

  • FillInBatchGasFieldsWithParentBlock: restored original nil→silently-skip behavior (return nil), so lookup-only callers can safely pass nil
  • FillInBatchGasFields: retains the explicit nil check that returns ErrNilBatchFetcher for BatchPostingReport (fixing the original panic)
  • Reverted test stubs in retryable_test.go and delayed_message_filter_test.go back to passing nil
  • Updated TestNilFetcherBehavior to reflect that FillInBatchGasFieldsWithParentBlock(nil) returns nil for all message kinds

Copilot AI changed the title Fix nil batchFetcher dereference Fix nil batchFetcher dereference in FillInBatchGasFields Apr 8, 2026
Copilot AI requested a review from bragaigor April 8, 2026 17:09
Copy link
Copy Markdown
Contributor

@bragaigor bragaigor left a comment

Choose a reason for hiding this comment

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

LGTM

@bragaigor bragaigor assigned joshuacolvin0 and unassigned bragaigor Apr 8, 2026
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.

5 participants