Skip to content

Add flag to disable ArbOwner outside on-chain execution#4591

Merged
joshuacolvin0 merged 11 commits intomasterfrom
braga/disable-arbowner-flag
Apr 7, 2026
Merged

Add flag to disable ArbOwner outside on-chain execution#4591
joshuacolvin0 merged 11 commits intomasterfrom
braga/disable-arbowner-flag

Conversation

@bragaigor
Copy link
Copy Markdown
Contributor

@bragaigor bragaigor commented Apr 1, 2026

  • Add --execution.disable-offchain-arbowner node config flag (default false)
  • When enabled, OwnerPrecompile.Call panics if the run context is not IsExecutedOnChain() (blocks ethcall and gas estimation modes)
  • The flag is an atomic.Bool field on the OwnerPrecompile struct, configured during ExecutionNode.Initialize() via gethhook.GetOwnerPrecompile()

The *OwnerPrecompile reference is held in an unexported var in the gethhook package, exposed via GetOwnerPrecompile(). This is necessary because precompile instances are created during gethhook.init() (a void function that runs at package load time), while node config only becomes available later in ExecutionNode.Initialize(). Since init() cannot return values, the pointer must be held somewhere to bridge that gap. An unexported var with a getter is the smallest surface area — set once, immutable after init, and the actual config (disableOffchain) lives on the OwnerPrecompile struct where it belongs.

closes NIT-4744

- When `DisableOffchainArbOwner` is set to `true` in chain config,
  `OwnerPrecompile.Call` panics
- Defaults to `false` — no behavior change unless explicitly opted in

Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 1, 2026

Codecov Report

❌ Patch coverage is 22.22222% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 34.16%. Comparing base (68da2ed) to head (a77614a).
⚠️ Report is 17 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4591      +/-   ##
==========================================
+ Coverage   34.14%   34.16%   +0.02%     
==========================================
  Files         495      495              
  Lines       59024    59041      +17     
==========================================
+ Hits        20154    20172      +18     
- Misses      35312    35319       +7     
+ Partials     3558     3550       -8     

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

❌ 14 Tests Failed:

Tests completed Failed Passed Skipped
4740 14 4726 0
View the top 3 failed tests by shortest run time
TestAliasingFlaky
Stack Traces | -0.000s run time
=== RUN   TestAliasingFlaky
=== PAUSE TestAliasingFlaky
=== CONT  TestAliasingFlaky
    common_test.go:777: BuildL1 deployConfig: DeployBold=true, DeployReferenceDAContracts=false
INFO [04-06|23:17:46.944] New local node record                    seq=1,775,517,466,944 id=43dc4bfc0647c92a                        ip=127.0.0.1 udp=0 tcp=0
INFO [04-06|23:17:46.944] Started P2P networking                   self=enode://6699609631bef094c961a6d6eade0bceb67f4a687513e4e1e38d18060210fdf6c6015f0761f592c3557c1cad5e78260004bbce787bd0b7d3d179201e276162a1@127.0.0.1:0
WARN [04-06|23:17:46.944] Getting file info                        dir= error="stat : no such file or directory"
TestMultigasStylus_StorageWrite/out_of_gas
Stack Traces | 0.170s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
INFO [04-06|23:24:10.201] Starting work on payload                 id=0x033fd6b5de7302bd
INFO [04-06|23:24:10.202] Updated payload                          id=0x033fd6b5de7302bd number=98  hash=01dde6..39c296 txs=0   withdrawals=0 gas=0          fees=0              root=e66e32..9b4cd8 elapsed=1.149ms
INFO [04-06|23:24:10.203] Stopping work on payload                 id=0x033fd6b5de7302bd reason=delivery
INFO [04-06|23:24:10.203] Imported new potential chain segment     number=98  hash=01dde6..39c296 blocks=1  txs=0   mgas=0.000  elapsed="952.893µs"  mgasps=0.000    triediffs=476.20KiB triedirty=0.00B
INFO [04-06|23:24:10.204] Submitted transaction                    hash=0x61d9c7b1790a53459e53c291c1fdfec1579c074a4fb17ff58f995934c8f7a7f7 from=0x7E23C8862920797d81916d62c274dd9217113e28 nonce=85  recipient=0x81910ff0A29ca55F883249db2B38469EBe6bd26D value=0
INFO [04-06|23:24:10.204] Chain head was updated                   number=98  hash=01dde6..39c296 root=e66e32..9b4cd8 elapsed="73.908µs"
INFO [04-06|23:24:10.205] Starting work on payload                 id=0x035f19abeb39d20c
INFO [04-06|23:24:10.206] Submitted transaction                    hash=0xe864caf5d116e799c65b7d227b8b2293412d37208e09317e1e25c5e9b735e23e from=0xb386a74Dcab67b66F8AC07B4f08365d37495Dd23 nonce=62  recipient=0x56007371b15c684940278b30610aB3d2B7af9D1D value=0
INFO [04-06|23:24:10.206] Submitted contract creation              hash=0x2de2774e30ebefd5c3fce7127bbbabee765b7e4911f73bd38f09fe468d554ed5 from=0x57Ff0F473737a1c161bfF9efDF016F7991585088 nonce=4   contract=0x68c55BbCAbb6EC7D623c420B6169CF277Fe040b5 value=0
INFO [04-06|23:24:10.206] DataPoster sent transaction              nonce=62  hash=e864ca..35e23e feeCap=506,380,510    tipCap=50,000,000    blobFeeCap=&lt;nil&gt; gas=157,199
INFO [04-06|23:24:10.206] BatchPoster: batch sent                  sequenceNumber=63 from=119 to=122 prevDelayed=41 currentDelayed=43 totalSegments=5  numBlobs=0
INFO [04-06|23:24:10.207] Starting work on payload                 id=0x03ca7650b43de18d
INFO [04-06|23:24:10.207] Starting work on payload                 id=0x03d7376a16e5b215
INFO [04-06|23:24:10.207] Submitted contract creation              hash=0xf18d5e975c5944e8bd8aa64e40e8b8ac9f60d1a1acc7fc47f70c05313b7d9431 from=0x57Ff0F473737a1c161bfF9efDF016F7991585088 nonce=3   contract=0xFE24a1d448a05c1d45C71dec3d218072dFBfA88b value=0
INFO [04-06|23:24:10.209] Starting work on payload                 id=0x03b81c49dd6ba8bb
    multigas_stylus_program_test.go:380: 
        	Error Trace:	/home/runner/work/nitro/nitro/system_tests/multigas_stylus_program_test.go:380
        	Error:      	An error is expected but got nil.
        	Test:       	TestMultigasStylus_StorageWrite/out_of_gas
--- FAIL: TestMultigasStylus_StorageWrite/out_of_gas (0.17s)
TestChallengeToOSP
Stack Traces | 0.180s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
    challenge_test.go:38: goroutine 50 [running]:
        runtime/debug.Stack()
        	/opt/hostedtoolcache/go/1.25.8/x64/src/runtime/debug/stack.go:26 +0x5e
        github.com/offchainlabs/nitro/util/testhelpers.RequireImpl({0x2de6e70, 0xc000102540}, {0x2dc3160, 0xc000626f90}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/util/testhelpers/testhelpers.go:28 +0x9f
        github.com/offchainlabs/nitro/staker/legacy.Require(0xc000102540, {0x2dc3160, 0xc000626f90}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/staker/legacy/common_test.go:14 +0x5d
        github.com/offchainlabs/nitro/staker/legacy.DeployOneStepProofEntry(0xc000102540, 0xc0005706c0, {0x2ddab30, 0xc00024d698})
        	/home/runner/work/nitro/nitro/staker/legacy/challenge_test.go:38 +0x18d
        github.com/offchainlabs/nitro/staker/legacy.runChallengeTest(0xc000102540, 0xc0000dcd00, {0x2ddd830, 0xc000013008}, 0x0, 0x0, 0x0)
        	/home/runner/work/nitro/nitro/staker/legacy/challenge_test.go:135 +0x425
        github.com/offchainlabs/nitro/staker/legacy.TestChallengeToOSP(0xc000102540)
        	/home/runner/work/nitro/nitro/staker/legacy/challenge_test.go:263 +0xaf
        testing.tRunner(0xc000102540, 0x2bdf6e8)
        	/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
        
    challenge_test.go:38: �[31;1m [] replacement transaction underpriced �[0;0m
--- FAIL: TestChallengeToOSP (0.18s)

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

Add `--execution.disable-offchain-arbowner` flag to disable ArbOwner
precompile calls outside on-chain execution

Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Comment on lines +20 to +25
var arbOwnerPrecompile *precompiles.OwnerPrecompile

func GetOwnerPrecompile() *precompiles.OwnerPrecompile {
return arbOwnerPrecompile
}

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.

The *OwnerPrecompile reference in gethhook is an unexported package-level variable, which we'd normally avoid. Here's why it's necessary:

Precompile instances are created during gethhook.init() — a void function that runs automatically at package load time. Node config (DisableOffchainArbOwner) only becomes available much later in ExecutionNode.Initialize(). Since init() can't return values and has no caller to hand the instance to, the pointer must be held somewhere between creation and configuration. The alternatives we evaluated:

  • storing it in chain config (wrong: this is a node-level concern, not consensus state)
  • a freestanding global atomic bool (worse: config detached from the object it belongs to)
  • or looking it up from the VM precompile maps (fragile: requires unwrapping across multiple layers and picking an arbitrary ArbOS version map)

all the above were all strictly worse. The unexported var with an exported getter is the smallest surface: set exactly once in init(), immutable after that, and the actual config flag (disableOffchain) lives on the OwnerPrecompile struct where it belongs.

In more detail:

  1. gethhook/geth-hook.go init() — runs at package load time (triggered in gethexec/blockchain.go importing gethhook). This is where precompiles.Precompiles() is called, which creates the *OwnerPrecompile instance. That instance gets wrapped in ArbosPrecompileWrapper and stored in the VM's global precompile maps (vm.PrecompiledContractsBeforeArbOS30, etc.). After init() returns, nobody holds a direct reference to the *OwnerPrecompile — it's buried inside the wrapper inside the VM maps.
  2. gethexec.CreateExecutionNode() — runs much later, when the node is actually being set up. This is where configFetcher (which has the DisableOffchainArbOwner flag) first becomes available.
  3. ExecutionNode.Initialize() — called after CreateExecutionNode. This is where we want to call ownerPC.SetDisableOffchain(config.DisableOffchainArbOwner). But we need the *OwnerPrecompile pointer to do that.

The gap: the instance is created in step 1, the config arrives in step 3, and there's no object that naturally carries the pointer from 1 to 3. Hence the global

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.

Nice solve!

Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
@bragaigor bragaigor marked this pull request as ready for review April 1, 2026 14:47
@bragaigor bragaigor requested a review from MishkaRogachev April 1, 2026 14:49
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
@bragaigor bragaigor assigned joshuacolvin0 and unassigned bragaigor Apr 2, 2026
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
@joshuacolvin0 joshuacolvin0 removed their assignment Apr 2, 2026
@bragaigor bragaigor requested a review from diegoximenes April 2, 2026 21:39
@bragaigor bragaigor assigned diegoximenes and unassigned bragaigor Apr 2, 2026
diegoximenes
diegoximenes previously approved these changes Apr 6, 2026
tx, err := arbOwner.AddChainOwner(&auth, common.HexToAddress("0xdeadbeef"))
Require(t, err)
_, err = builder.L2.EnsureTxSucceeded(tx)
Require(t, err)
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.

We want a final cleanup here that sets the static to false (default), so that whatever order tests are running other tests will see the same value

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.

that's probably line 1376 where I included gethhook.GetOwnerPrecompile().SetDisableEthCall(false) inside defer, or you meant something else?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

tracking this

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.

missed that. Thanks!

tx, err := arbOwner.AddChainOwner(&auth, common.HexToAddress("0xdeadbeef"))
Require(t, err)
_, err = builder.L2.EnsureTxSucceeded(tx)
Require(t, err)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

tracking this

tsahee
tsahee previously approved these changes Apr 6, 2026
Copy link
Copy Markdown
Contributor

@tsahee tsahee left a comment

Choose a reason for hiding this comment

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

LGTM

tx, err := arbOwner.AddChainOwner(&auth, common.HexToAddress("0xdeadbeef"))
Require(t, err)
_, err = builder.L2.EnsureTxSucceeded(tx)
Require(t, err)
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.

missed that. Thanks!

@tsahee
Copy link
Copy Markdown
Contributor

tsahee commented Apr 6, 2026

@clabot check

joshuacolvin0
joshuacolvin0 previously approved these changes Apr 6, 2026
Copy link
Copy Markdown
Member

@joshuacolvin0 joshuacolvin0 left a comment

Choose a reason for hiding this comment

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

LGTM

@joshuacolvin0 joshuacolvin0 enabled auto-merge April 6, 2026 21:24
Signed-off-by: Igor Braga <5835477+bragaigor@users.noreply.github.com>
@bragaigor bragaigor dismissed stale reviews from joshuacolvin0, tsahee, and diegoximenes via a77614a April 6, 2026 23:01
@bragaigor
Copy link
Copy Markdown
Contributor Author

had to resolve a minor conflict on execution/gethexec/node.go now we're good

Copy link
Copy Markdown
Member

@joshuacolvin0 joshuacolvin0 left a comment

Choose a reason for hiding this comment

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

LGTM

@joshuacolvin0 joshuacolvin0 added this pull request to the merge queue Apr 7, 2026
Merged via the queue into master with commit dd416d9 Apr 7, 2026
25 checks passed
@joshuacolvin0 joshuacolvin0 deleted the braga/disable-arbowner-flag branch April 7, 2026 02:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants