Skip to content

Conversation

@MishkaRogachev
Copy link
Contributor

@MishkaRogachev MishkaRogachev commented Dec 2, 2025

Fixes NIT-4152
pulls in OffchainLabs/go-ethereum#599

Changes:

  • Add WithUnmeteredGasAccounting to the precompile context to ignore gas charges within the ShrinkBacklog call.
  • Parametrise Retryable-Redeem test scenarios to prove behaviour for different ArbOS versions

@github-actions
Copy link
Contributor

github-actions bot commented Dec 2, 2025

❌ 10 Tests Failed:

Tests completed Failed Passed Skipped
3935 10 3925 0
View the top 3 failed tests by shortest run time
TestDataStreaming_PositiveScenario/Many_senders,_long_messages
Stack Traces | 0.110s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
        	/home/runner/work/nitro/nitro/util/testhelpers/testhelpers.go:29 +0x55
        github.com/offchainlabs/nitro/daprovider/data_streaming.testBasic.func1()
        	/home/runner/work/nitro/nitro/daprovider/data_streaming/protocol_test.go:230 +0x14f
        created by github.com/offchainlabs/nitro/daprovider/data_streaming.testBasic in goroutine 174
        	/home/runner/work/nitro/nitro/daprovider/data_streaming/protocol_test.go:223 +0x85
        
    protocol_test.go:230: �[31;1m [] too much time has elapsed since request was signed �[0;0m
INFO [01-20|14:18:36.037] rpc response                             method=datastreaming_start logId=9  err="too much time has elapsed since request was signed" result={} attempt=0 args="[\"0x696f8ebb\", \"0x32\", \"0xd9\", \"0x29f4\", \"0xa\", \"0x1a584ee5ad2236c90425df8dce5bc3e9a9225e01e69996b726e6ee3c3860aed3603fd9ff50134b6a71c6346d8664ea040603a07b58079f24180ebbb992dc43a101\"]" errorData=null
    protocol_test.go:230: goroutine 276 [running]:
        runtime/debug.Stack()
        	/opt/hostedtoolcache/go/1.25.5/x64/src/runtime/debug/stack.go:26 +0x5e
        github.com/offchainlabs/nitro/util/testhelpers.RequireImpl({0x1569270, 0xc0006b6380}, {0x154fd20, 0xc0004f26f0}, {0x0, 0x0, 0x0})
        	/home/runner/work/nitro/nitro/util/testhelpers/testhelpers.go:29 +0x55
        github.com/offchainlabs/nitro/daprovider/data_streaming.testBasic.func1()
        	/home/runner/work/nitro/nitro/daprovider/data_streaming/protocol_test.go:230 +0x14f
        created by github.com/offchainlabs/nitro/daprovider/data_streaming.testBasic in goroutine 174
        	/home/runner/work/nitro/nitro/daprovider/data_streaming/protocol_test.go:223 +0x85
        
    protocol_test.go:230: �[31;1m [] too much time has elapsed since request was signed �[0;0m
--- FAIL: TestDataStreaming_PositiveScenario/Many_senders,_long_messages (0.11s)
TestDataStreaming_PositiveScenario
Stack Traces | 0.140s run time
=== RUN   TestDataStreaming_PositiveScenario
--- FAIL: TestDataStreaming_PositiveScenario (0.14s)
TestRedisProduceComplex/two_producers,_some_consumers_killed,_others_should_take_over_their_work,_some_invalid_entries,_unequal_number_of_requests_from_producers
Stack Traces | 2.130s run time
... [CONTENT TRUNCATED: Keeping last 20 lines]
�[36mDEBUG�[0m[01-20|14:19:46.957] checkResponses                           �[36mresponded�[0m=0   �[36merrored�[0m=0 �[36mchecked�[0m=0
�[36mDEBUG�[0m[01-20|14:19:46.958] request timed out waiting for response   �[36mmsgId�[0m=1768918784956-18 �[36mallowedOldestId�[0m=1768918784957-0
�[36mDEBUG�[0m[01-20|14:19:46.958] request timed out waiting for response   �[36mmsgId�[0m=1768918784956-20 �[36mallowedOldestId�[0m=1768918784957-0
�[36mDEBUG�[0m[01-20|14:19:46.958] request timed out waiting for response   �[36mmsgId�[0m=1768918784956-16 �[36mallowedOldestId�[0m=1768918784957-0
�[36mDEBUG�[0m[01-20|14:19:46.958] request timed out waiting for response   �[36mmsgId�[0m=1768918784956-14 �[36mallowedOldestId�[0m=1768918784957-0
�[36mDEBUG�[0m[01-20|14:19:46.958] request timed out waiting for response   �[36mmsgId�[0m=1768918784956-19 �[36mallowedOldestId�[0m=1768918784957-0
�[36mDEBUG�[0m[01-20|14:19:46.959] request timed out waiting for response   �[36mmsgId�[0m=1768918784956-15 �[36mallowedOldestId�[0m=1768918784957-0
�[36mDEBUG�[0m[01-20|14:19:46.959] request timed out waiting for response   �[36mmsgId�[0m=1768918784956-17 �[36mallowedOldestId�[0m=1768918784957-0
�[36mDEBUG�[0m[01-20|14:19:46.959] checkResponses                           �[36mresponded�[0m=0   �[36merrored�[0m=7 �[36mchecked�[0m=7
    pubsub_test.go:385: Unexpected error while awaiting responses, producer: 1, response: 29, err: error getting response, request has been waiting for too long
    pubsub_test.go:385: Unexpected error while awaiting responses, producer: 1, response: 30, err: error getting response, request has been waiting for too long
    pubsub_test.go:385: Unexpected error while awaiting responses, producer: 1, response: 31, err: error getting response, request has been waiting for too long
    pubsub_test.go:385: Unexpected error while awaiting responses, producer: 1, response: 32, err: error getting response, request has been waiting for too long
    pubsub_test.go:385: Unexpected error while awaiting responses, producer: 1, response: 33, err: error getting response, request has been waiting for too long
    pubsub_test.go:385: Unexpected error while awaiting responses, producer: 1, response: 34, err: error getting response, request has been waiting for too long
    pubsub_test.go:385: Unexpected error while awaiting responses, producer: 1, response: 35, err: error getting response, request has been waiting for too long
�[31mERROR�[0m[01-20|14:19:46.959] Error from XpendingExt in getting PEL for auto claim �[31merr�[0m="context canceled" �[31mpendingLen�[0m=0
�[31mERROR�[0m[01-20|14:19:46.960] Error from XpendingExt in getting PEL for auto claim �[31merr�[0m="context canceled" �[31mpendingLen�[0m=0
�[36mDEBUG�[0m[01-20|14:19:47.059] Error destroying a stream group          �[36merror�[0m="dial tcp 127.0.0.1:45377: connect: connection refused"
--- FAIL: TestRedisProduceComplex/two_producers,_some_consumers_killed,_others_should_take_over_their_work,_some_invalid_entries,_unequal_number_of_requests_from_producers (2.13s)

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

@MishkaRogachev MishkaRogachev force-pushed the revisit-retryable-redeem-gas-budgeting-fro-arbos-60 branch 3 times, most recently from 4fa5689 to 85e2c9c Compare December 3, 2025 11:43
@MishkaRogachev MishkaRogachev changed the title WIP: considering free storage access from precompile context Stop charging gas for retryable redeem gas pool update on ArbOS 60+ Dec 3, 2025
@MishkaRogachev MishkaRogachev force-pushed the revisit-retryable-redeem-gas-budgeting-fro-arbos-60 branch 3 times, most recently from 32ca70c to 2bc088b Compare December 5, 2025 11:56
@MishkaRogachev MishkaRogachev marked this pull request as ready for review December 5, 2025 12:29
Copy link
Contributor

@gligneul gligneul left a comment

Choose a reason for hiding this comment

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

Looks good, just minor comments.

@gligneul gligneul assigned MishkaRogachev and unassigned gligneul Dec 5, 2025
gligneul
gligneul previously approved these changes Dec 8, 2025
@MishkaRogachev MishkaRogachev force-pushed the revisit-retryable-redeem-gas-budgeting-fro-arbos-60 branch 2 times, most recently from 749e9c7 to 29a5c43 Compare December 9, 2025 10:16
}

func TestRetryableRedeem(t *testing.T) {
func overrideStateArbOSVersion(evm *vm.EVM, arbosVersion uint64) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

instead of that, use newMockEVMForTestingWithVersion

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried it, but it does not work because of the way the arbos version is obtained here. Probably it could be fixed by presetting the version here. I suggest to open a separate ticket for it

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't follw. If newMockEVMForTestingWithVersion needs to be fixed, let's fix it. Let's talk about it later.

// L2PricingState().ShrinkBacklog(). BacklogUpdateCost() returns
// that amount based on the storage read/write mix used by ShrinkBacklog().
backlogUpdateCost := uint64(0)
if c.State.L2PricingState().ArbosVersion >= params.ArbosVersion_60 {
Copy link
Contributor

Choose a reason for hiding this comment

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

in all of those this shouldn't be ArbosVersion60, but arbosVersion_RedeemChargingFix or something similar.
So we could easily change the version in which this applies (can be a number only after it's issued)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I fixed it to ArbosVersion_MultiGasConstraintsVersion to exclude the possibility of an intermediate state under which there is no implementation.

backlogUpdateCost := uint64(0)
if c.State.L2PricingState().ArbosVersion >= params.ArbosVersion_60 {
// Charge static price for any pricer starting from ArbOS 60
backlogUpdateCost = l2pricing.ArbOS60StaticBacklogUpdateCost
Copy link
Contributor

Choose a reason for hiding this comment

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

why not just add that logic into L2PricingState().BacklogUpdateCost() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

return retryTxHash, c.State.L2PricingState().ShrinkBacklog(gasToDonate, multigas.ComputationGas(gasToDonate))
// Starting from ArbOS 60, we don't charge gas for the ShrinkBacklog call here
if c.State.L2PricingState().ArbosVersion >= params.ArbosVersion_60 {
err = c.WithUnmeteredGasAccounting(func() error {
Copy link
Contributor

Choose a reason for hiding this comment

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

I get the idea and kind of like it.. however, since the context only exists for a specific call and will be removed once you return from this function (it is created in "func (p *Precompile) Call"), I think this adds more complexity then safety, and it'll probably be simpler to just have a context.StopCollectingGas() function or something similar. This will also allow you to reuse the call to ShrinkBacklog between the options and help us in case we do anything else in the end of the function make sure we don't spend (and need to allocate) more gas.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, but there is one nuance: after the method completes, the context is still in use, and I see a difference of 3 gas units if I leave it free. I corrected it to a compromise option with defer c.SetUnmeteredGasAccounting(false)

@MishkaRogachev MishkaRogachev force-pushed the revisit-retryable-redeem-gas-budgeting-fro-arbos-60 branch from 6f44f27 to e458d6d Compare December 24, 2025 15:47
Copy link
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. tiny fix

}

func TestRetryableSubmissionAndRedeemFees(t *testing.T) {
t.Helper()
Copy link
Contributor

Choose a reason for hiding this comment

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

this is the main test func, should not be a helper

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

// This ensures that the gas pool has enough gas to run the retryable attempt.
// TODO(NIT-4120): clarify the gas dimension for gasToDonate
// Starting from ArbosVersion_MultiGasConstraintsVersion, don't charge gas for the ShrinkBacklog call.
stopChargingGas := c.State.L2PricingState().ArbosVersion >= params.ArbosVersion_MultiGasConstraintsVersion
Copy link
Contributor

Choose a reason for hiding this comment

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

where do you charge the backlog update cost?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is my current understanding:

ArbOS version Active pricer(s) possible BacklogUpdateCost() behaviour
< 50 Legacy StorageReadCost + StorageWriteCost
SingleGasConstraintsVersion (50) Legacy / SingleGasConstraints StorageReadCost (for model detection) + legacy fallback cost
. Buggy if constraints > 0
MultiConstraintFix (51) Legacy / SingleGasConstraints If constraints exist:
StorageReadCost (model check)
StorageReadCost (iterate)
N × (StorageReadCost + StorageWriteCost)
Else legacy cost
>= MultiGasConstraintsVersion (60) Legacy / SingleGas / MultiGas Always MultiConstraintStaticBacklogUpdateCost, independent of model

So having the single gas constraints configured on ArbOS 50 should be the only broken state (fixed by MultiConstraintFix)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So backlogUpdateCost := c.State.L2PricingState().BacklogUpdateCost() should be always the correct value

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With the latest changes:

  • MultiConstraintStaticBacklogUpdateCost is the actual charged price for the redeem backlog update.
  • Fix redeem test: the extra storage.StorageWriteCost - storage.StorageWriteZeroCost is now expected only for < ArbOS 60.

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

@tsahee tsahee enabled auto-merge January 21, 2026 01:38
@tsahee tsahee added this pull request to the merge queue Jan 21, 2026
Merged via the queue into master with commit 36c6bff Jan 21, 2026
25 checks passed
@tsahee tsahee deleted the revisit-retryable-redeem-gas-budgeting-fro-arbos-60 branch January 21, 2026 02:36
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.

3 participants