-
Notifications
You must be signed in to change notification settings - Fork 1
Update gas target immediately after block execution #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
af596f7
178724f
19c3fb9
3b7e488
6851d5a
d8e67f3
b4450bb
f34665a
0373d0a
94e1fb9
5c4a6e1
16badd0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,39 +15,47 @@ import ( | |
| "github.com/ava-labs/libevm/core/types" | ||
| "github.com/ava-labs/libevm/params" | ||
|
|
||
| "github.com/ava-labs/strevm/blocks" | ||
| "github.com/ava-labs/strevm/gastime" | ||
| "github.com/ava-labs/strevm/intmath" | ||
| saeparams "github.com/ava-labs/strevm/params" | ||
| ) | ||
|
|
||
| // Points define user-injected hook points. | ||
| type Points interface { | ||
| GasTarget(parent *types.Block) gas.Gas | ||
| SubSecondBlockTime(*types.Block) gas.Gas | ||
| // GasTarget returns the amount of gas per second that the chain should | ||
| // target to consume after executing the provided block. | ||
| GasTarget(*types.Header) gas.Gas | ||
| // SubSecondBlockTime returns the sub-second portion of the block time based | ||
| // on the provided gas rate. | ||
| // | ||
| // For example, if the block timestamp is 10.75 seconds and the gas rate is | ||
| // 100 gas/second, then this method should return 75 gas. | ||
| SubSecondBlockTime(gasRate gas.Gas, h *types.Header) gas.Gas | ||
| // BeforeBlock is called immediately prior to executing the block. | ||
| BeforeBlock(params.Rules, *state.StateDB, *types.Block) error | ||
| // AfterBlock is called immediately after executing the block. | ||
| AfterBlock(*state.StateDB, *types.Block, types.Receipts) | ||
| } | ||
|
|
||
| // BeforeBlock is intended to be called before processing a block, with the gas | ||
| // target sourced from [Points]. | ||
| func BeforeBlock(pts Points, rules params.Rules, sdb *state.StateDB, b *blocks.Block, clock *gastime.Time) error { | ||
| // BeforeBlock is intended to be called before processing a block. | ||
| func BeforeBlock(pts Points, rules params.Rules, sdb *state.StateDB, b *types.Block, clock *gastime.Time) error { | ||
| r := clock.Rate() | ||
| clock.FastForwardTo( | ||
| b.BuildTime(), | ||
| pts.SubSecondBlockTime(b.EthBlock()), | ||
| b.Time(), | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to differentiate between the different times, much like how we do with state roots. Granted in this case it couldn't be execution (gas nor wall) time, but more generally I want to be more precise in this code base. Happy to change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| pts.SubSecondBlockTime(r, b.Header()), | ||
| ) | ||
| target := pts.GasTarget(b.ParentBlock().EthBlock()) | ||
| if err := clock.SetTarget(target); err != nil { | ||
| return fmt.Errorf("%T.SetTarget() before block: %w", clock, err) | ||
| } | ||
| return pts.BeforeBlock(rules, sdb, b.EthBlock()) | ||
| return pts.BeforeBlock(rules, sdb, b) | ||
| } | ||
|
|
||
| // AfterBlock is intended to be called after processing a block, with the gas | ||
| // sourced from [types.Block.GasUsed] or equivalent. | ||
| func AfterBlock(pts Points, sdb *state.StateDB, b *types.Block, clock *gastime.Time, used gas.Gas, rs types.Receipts) { | ||
| // AfterBlock is intended to be called after processing a block. | ||
| func AfterBlock(pts Points, sdb *state.StateDB, b *types.Block, clock *gastime.Time, used gas.Gas, rs types.Receipts) error { | ||
| clock.Tick(used) | ||
| target := pts.GasTarget(b.Header()) | ||
| if err := clock.SetTarget(target); err != nil { | ||
| return fmt.Errorf("%T.SetTarget() after block: %w", clock, err) | ||
| } | ||
| pts.AfterBlock(sdb, b, rs) | ||
| return nil | ||
| } | ||
|
|
||
| // MinimumGasConsumption MUST be used as the implementation for the respective | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. | ||
| // See the file LICENSE for licensing terms. | ||
|
|
||
| package hook_test | ||
StephenButtolph marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/ava-labs/avalanchego/vms/components/gas" | ||
| "github.com/ava-labs/libevm/core/types" | ||
| "github.com/ava-labs/libevm/params" | ||
| "github.com/ava-labs/strevm/gastime" | ||
| "github.com/ava-labs/strevm/hook/hookstest" | ||
| "github.com/ava-labs/strevm/saetest" | ||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
|
|
||
| . "github.com/ava-labs/strevm/hook" | ||
| ) | ||
|
|
||
| // TestTargetUpdateTiming verifies that the gas target is modified in AfterBlock | ||
| // rather than BeforeBlock. | ||
| func TestTargetUpdateTiming(t *testing.T) { | ||
| const ( | ||
| initialTime = 42 | ||
| initialTarget gas.Gas = 1_600_000 | ||
| initialExcess = 1_234_567_890 | ||
| ) | ||
| tm := gastime.New(initialTime, initialTarget, initialExcess) | ||
|
|
||
| const ( | ||
| newTime uint64 = initialTime + 1 | ||
| newTarget = initialTarget + 100_000 | ||
| ) | ||
| hook := &hookstest.Stub{ | ||
| Target: newTarget, | ||
| } | ||
| header := &types.Header{ | ||
| Time: newTime, | ||
| } | ||
| block := types.NewBlock(header, nil, nil, nil, saetest.TrieHasher()) | ||
|
|
||
| initialPrice := tm.Price() | ||
| require.NoError(t, BeforeBlock(hook, params.TestRules, nil, block, tm), "BeforeBlock()") | ||
| assert.Equal(t, newTime, tm.Unix(), "Unix time advanced by BeforeBlock()") | ||
| assert.Equal(t, initialTarget, tm.Target(), "Target not changed by BeforeBlock()") | ||
|
|
||
| enforcedPrice := tm.Price() | ||
| assert.LessOrEqual(t, enforcedPrice, initialPrice, "Price should not increase in BeforeBlock()") | ||
|
||
| if t.Failed() { | ||
| t.FailNow() | ||
| } | ||
|
|
||
| const ( | ||
| secondsOfGasUsed = 3 | ||
| initialRate = initialTarget * gastime.TargetToRate | ||
StephenButtolph marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| used gas.Gas = initialRate * secondsOfGasUsed | ||
| expectedEndTime = newTime + secondsOfGasUsed | ||
| ) | ||
| require.NoError(t, AfterBlock(hook, nil, block, tm, used, nil), "AfterBlock()") | ||
| assert.Equal(t, expectedEndTime, tm.Unix(), "Unix time advanced by AfterBlock()") | ||
| assert.Equal(t, newTarget, tm.Target(), "Target updated by AfterBlock()") | ||
| assert.GreaterOrEqual(t, tm.Price(), enforcedPrice, "Price should not decrease in AfterBlock()") | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also (nit), something about the wording of "to consume after executing the provided block" feels off and it confused me for a split second. Perhaps "target to consume in subsequent blocks."? The current phrasing suggests immediacy, not long-term horizon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reworded it, lmk what you think