-
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
Changes from all commits
af596f7
178724f
19c3fb9
3b7e488
6851d5a
d8e67f3
b4450bb
f34665a
0373d0a
94e1fb9
5c4a6e1
16badd0
ad66e07
2339b16
cb79047
1f15693
b5873a6
ee5f973
cf23da5
5793181
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 |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // 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/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
|
|
||
| "github.com/ava-labs/strevm/gastime" | ||
| "github.com/ava-labs/strevm/hook/hookstest" | ||
| "github.com/ava-labs/strevm/saetest" | ||
|
|
||
| . "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) | ||
| initialRate := tm.Rate() | ||
|
|
||
| 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()") | ||
| // While the price technically could remain the same, being more strict | ||
| // ensures the test is meaningful. | ||
| enforcedPrice := tm.Price() | ||
| assert.Less(t, enforcedPrice, initialPrice, "Price should not increase in BeforeBlock()") | ||
| if t.Failed() { | ||
| t.FailNow() | ||
| } | ||
|
|
||
| const ( | ||
| secondsOfGasUsed = 3 | ||
| expectedEndTime = newTime + secondsOfGasUsed | ||
| ) | ||
| used := initialRate * secondsOfGasUsed | ||
| require.NoError(t, AfterBlock(hook, nil, block, tm, used, nil), "AfterBlock()") | ||
| assert.Equal(t, expectedEndTime, tm.Unix(), "Unix time advanced by AfterBlock() due to gas consumption") | ||
| assert.Equal(t, newTarget, tm.Target(), "Target updated by AfterBlock()") | ||
| // While the price technically could remain the same, being more strict | ||
| // ensures the test is meaningful. | ||
| assert.Greater(t, tm.Price(), enforcedPrice, "Price should not decrease in AfterBlock()") | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -363,9 +363,9 @@ func TestGasAccounting(t *testing.T) { | |
| // Steps are _not_ independent, so the execution time of one is the starting | ||
| // time of the next. | ||
| steps := []struct { | ||
| target gas.Gas | ||
| blockTime uint64 | ||
| numTxs int | ||
| targetAfter gas.Gas | ||
| wantExecutedBy *proxytime.Time[gas.Gas] | ||
| // Because of the 2:1 ratio between Rate and Target, gas consumption | ||
| // increases excess by half of the amount consumed, while | ||
|
|
@@ -374,74 +374,85 @@ func TestGasAccounting(t *testing.T) { | |
| wantPriceAfter gas.Price | ||
| }{ | ||
| { | ||
| target: 5 * gasPerTx, | ||
| // Initially set the gasTarget for the next block. | ||
| blockTime: 0, | ||
| numTxs: 0, | ||
| targetAfter: 5 * gasPerTx, | ||
| wantExecutedBy: at(0, 0, 10*gasPerTx), | ||
| wantExcessAfter: 0, | ||
| wantPriceAfter: 1, | ||
| }, | ||
|
Comment on lines
376
to
+384
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. The alternative to doing this would be to modify
Happy to do either if this is too janky
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. (thinking out loud) Practically, for |
||
| { | ||
| blockTime: 2, | ||
| numTxs: 3, | ||
| targetAfter: 5 * gasPerTx, | ||
| wantExecutedBy: at(2, 3, 10*gasPerTx), | ||
| wantExcessAfter: 3 * gasPerTx / 2, | ||
| wantPriceAfter: 1, // Excess isn't high enough so price is effectively e^0 | ||
| }, | ||
| { | ||
| target: 5 * gasPerTx, | ||
| blockTime: 3, // fast-forward | ||
| numTxs: 12, | ||
| targetAfter: 5 * gasPerTx, | ||
| wantExecutedBy: at(4, 2, 10*gasPerTx), | ||
| wantExcessAfter: 12 * gasPerTx / 2, | ||
| wantPriceAfter: 1, | ||
| }, | ||
| { | ||
| target: 5 * gasPerTx, | ||
| blockTime: 4, // no fast-forward so starts at last execution time | ||
| numTxs: 20, | ||
| targetAfter: 5 * gasPerTx, | ||
| wantExecutedBy: at(6, 2, 10*gasPerTx), | ||
| wantExcessAfter: (12 + 20) * gasPerTx / 2, | ||
| wantPriceAfter: 1, | ||
| }, | ||
| { | ||
| target: 5 * gasPerTx, | ||
| blockTime: 7, // fast-forward equivalent of 8 txs | ||
| numTxs: 16, | ||
| wantExecutedBy: at(8, 6, 10*gasPerTx), | ||
| wantExcessAfter: (12 + 20 - 8 + 16) * gasPerTx / 2, | ||
| blockTime: 7, // fast-forward equivalent of 8 txs | ||
| numTxs: 16, | ||
| targetAfter: 10 * gasPerTx, // double gas/block --> halve ticking rate | ||
| // Doubling the target scales both the ending time and excess to compensate. | ||
| wantExecutedBy: at(8, 2*6, 2*10*gasPerTx), | ||
| wantExcessAfter: 2 * (12 + 20 - 8 + 16) * gasPerTx / 2, | ||
| wantPriceAfter: 1, | ||
| }, | ||
| { | ||
| target: 10 * gasPerTx, // double gas/block --> halve ticking rate | ||
| blockTime: 8, // no fast-forward | ||
| numTxs: 4, | ||
| wantExecutedBy: at(8, (6*2)+4, 20*gasPerTx), // starting point scales | ||
| wantExcessAfter: (2*(12+20-8+16) + 4) * gasPerTx / 2, | ||
| blockTime: 8, // no fast-forward | ||
| numTxs: 4, | ||
| targetAfter: 5 * gasPerTx, // back to original | ||
| // Halving the target inverts the scaling seen in the last block. | ||
| wantExecutedBy: at(8, 6+(4/2), 10*gasPerTx), | ||
| wantExcessAfter: ((12 + 20 - 8 + 16) + 4/2) * gasPerTx / 2, | ||
| wantPriceAfter: 1, | ||
| }, | ||
| { | ||
| target: 5 * gasPerTx, // back to original | ||
| blockTime: 8, | ||
| numTxs: 5, | ||
| targetAfter: 5 * gasPerTx, | ||
| wantExecutedBy: at(8, 6+(4/2)+5, 10*gasPerTx), | ||
| wantExcessAfter: ((12 + 20 - 8 + 16) + 4/2 + 5) * gasPerTx / 2, | ||
| wantPriceAfter: 1, | ||
| }, | ||
| { | ||
| target: 5 * gasPerTx, | ||
| blockTime: 20, // more than double the last executed-by time, reduces excess to 0 | ||
| numTxs: 1, | ||
| targetAfter: 5 * gasPerTx, | ||
| wantExecutedBy: at(20, 1, 10*gasPerTx), | ||
| wantExcessAfter: gasPerTx / 2, | ||
| wantPriceAfter: 1, | ||
| }, | ||
| { | ||
| target: 5 * gasPerTx, | ||
| blockTime: 21, // fast-forward so excess is 0 | ||
| numTxs: 30 * gastime.TargetToExcessScaling, // deliberate, see below | ||
| targetAfter: 5 * gasPerTx, | ||
| wantExecutedBy: at(21, 30*gastime.TargetToExcessScaling, 10*gasPerTx), | ||
| wantExcessAfter: 3 * ((5 * gasPerTx /*T*/) * gastime.TargetToExcessScaling /* == K */), | ||
| // Excess is now 3·K so the price is e^3 | ||
| wantPriceAfter: gas.Price(math.Floor(math.Pow(math.E, 3 /* <----- NB */))), | ||
| }, | ||
| { | ||
| target: 5 * gasPerTx, | ||
| blockTime: 22, // no fast-forward | ||
| numTxs: 10 * gastime.TargetToExcessScaling, | ||
| targetAfter: 5 * gasPerTx, | ||
| wantExecutedBy: at(21, 40*gastime.TargetToExcessScaling, 10*gasPerTx), | ||
| wantExcessAfter: 4 * ((5 * gasPerTx /*T*/) * gastime.TargetToExcessScaling /* == K */), | ||
| wantPriceAfter: gas.Price(math.Floor(math.Pow(math.E, 4 /* <----- NB */))), | ||
|
|
@@ -451,7 +462,7 @@ func TestGasAccounting(t *testing.T) { | |
| e, chain, wallet := sut.Executor, sut.chain, sut.wallet | ||
|
|
||
| for i, step := range steps { | ||
| hooks.Target = step.target | ||
| hooks.Target = step.targetAfter | ||
|
|
||
| txs := make(types.Transactions, step.numTxs) | ||
| for i := range txs { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.