Skip to content

Commit 7bd99c9

Browse files
authored
feat: add tests for min-base-fee (ethereum-optimism#17441)
* min-base-fee solidity diffs * remove codegen bin * update semver and remove some more diffs in systemconfig codegen * revert bindings/systemconfig * e2e test * spike acceptance-test * wip acceptance test * add support for jovian mbf * op-acceptance-test: min base fee (#14) * add kurtosis files * move dsl into test * passing * ensure block is progressing * remove checkfordecrease func * simulate txs * nits + simplify * base + jovian sysgo passes * sysext passes * nits + reduce bindings diff * just wait for 1 block * use zero addr, and swap test order * add else clause to e2e test * use high/med/zero mbfs * e2e test configure mbf * check activation block base fee is less than mbf * wip setting diff mbfs * claude fix * build activation+1 block * simulate some tx on diff med/high mbfs * wip claude dont use extclient * use 2gwei, 5gwei too high causes maxFeePerGas error * sysgo and sysext pass for jovian * dont default ptr(0) and first nit on action test * remove user tx in actions * acceptance-test no user tx + reduce diffs in actions * kurtosis: run on jovian devnet (#15) * wip * pin teku version * fix TestSystemConfigMarshaling * fix attribute test + add comment in payload_util
1 parent c813b04 commit 7bd99c9

File tree

17 files changed

+630
-57
lines changed

17 files changed

+630
-57
lines changed

kurtosis-devnet/jovian.yaml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
optimism_package:
2+
faucet:
3+
enabled: true
4+
image: {{ localDockerImage "op-faucet" }}
5+
chains:
6+
op-kurtosis:
7+
participants:
8+
node0:
9+
el:
10+
type: op-geth
11+
image: "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:v1.101602.1-rc.1"
12+
log_level: ""
13+
extra_env_vars: {}
14+
extra_labels: {}
15+
extra_params: []
16+
tolerations: []
17+
volume_size: 0
18+
min_cpu: 0
19+
max_cpu: 0
20+
min_mem: 0
21+
max_mem: 0
22+
cl: &x-node-cl
23+
type: op-node
24+
image: {{ localDockerImage "op-node" }}
25+
log_level: ""
26+
extra_env_vars: {}
27+
extra_labels: {}
28+
extra_params: []
29+
tolerations: []
30+
volume_size: 0
31+
min_cpu: 0
32+
max_cpu: 0
33+
min_mem: 0
34+
max_mem: 0
35+
network_params:
36+
network: "kurtosis"
37+
network_id: "2151908"
38+
seconds_per_slot: 2
39+
fjord_time_offset: 0
40+
granite_time_offset: 0
41+
holocene_time_offset: 0
42+
isthmus_time_offset: 0
43+
jovian_time_offset: 60
44+
fund_dev_accounts: true
45+
batcher_params:
46+
image: {{ localDockerImage "op-batcher" }}
47+
extra_params: []
48+
proposer_params:
49+
image: {{ localDockerImage "op-proposer" }}
50+
extra_params: []
51+
game_type: 1
52+
proposal_interval: 10m
53+
challengers:
54+
challenger:
55+
enabled: true
56+
image: {{ localDockerImage "op-challenger" }}
57+
participants: "*"
58+
cannon_prestates_url: {{ localPrestate.URL }}
59+
cannon_trace_types: ["cannon", "permissioned"]
60+
op_contract_deployer_params:
61+
image: {{ localDockerImage "op-deployer" }}
62+
l1_artifacts_locator: {{ localContractArtifacts "l1" }}
63+
l2_artifacts_locator: {{ localContractArtifacts "l2" }}
64+
overrides:
65+
faultGameAbsolutePrestate: {{ localPrestate.Hashes.prestate_mt64 }}
66+
global_log_level: "info"
67+
global_node_selectors: {}
68+
global_tolerations: []
69+
persistent: false
70+
ethereum_package:
71+
participants:
72+
- el_type: geth
73+
cl_type: teku
74+
cl_image: consensys/teku:25.7.1
75+
network_params:
76+
preset: minimal
77+
genesis_delay: 5
78+
additional_preloaded_contracts: |
79+
{
80+
"0x4e59b44847b379578588920cA78FbF26c0B4956C": {
81+
"balance": "0ETH",
82+
"code": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3",
83+
"storage": {},
84+
"nonce": "1"
85+
}
86+
}

kurtosis-devnet/justfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ pectra-devnet: (devnet "pectra.yaml")
9999
# Isthmus devnet
100100
isthmus-devnet: (devnet "isthmus.yaml")
101101

102+
# Jovian devnet
103+
jovian-devnet: (devnet "jovian.yaml")
104+
102105
# Flashblocks devnet
103106
flash-devnet: (devnet "flash.yaml")
104107

op-acceptance-tests/acceptance-tests.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ gates:
7878
tests:
7979
- package: github.com/ethereum-optimism/optimism/op-acceptance-tests/tests/flashblocks
8080
timeout: 5m
81-
81+
8282
- id: flashblocks-with-isthmus
8383
inherits:
8484
- isthmus
@@ -92,3 +92,11 @@ gates:
9292
tests:
9393
- package: github.com/ethereum-optimism/optimism/op-acceptance-tests/tests/sync_tester/sync_tester_ext_el
9494
timeout: 30m
95+
96+
- id: jovian
97+
inherits:
98+
- base
99+
description: "Jovian network tests."
100+
tests:
101+
- package: github.com/ethereum-optimism/optimism/op-acceptance-tests/tests/jovian
102+
timeout: 10m

op-acceptance-tests/justfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ holocene:
1414
isthmus:
1515
@just acceptance-test "" isthmus
1616

17+
jovian:
18+
@just acceptance-test jovian jovian
19+
1720
interop:
1821
@just acceptance-test "" interop
1922

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package jovian
2+
3+
import (
4+
"testing"
5+
6+
"github.com/ethereum-optimism/optimism/op-devstack/presets"
7+
)
8+
9+
func TestMain(m *testing.M) {
10+
presets.DoMain(m, presets.WithMinimal(), presets.WithJovianAtGenesis())
11+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package jovian
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
7+
"github.com/ethereum-optimism/optimism/op-devstack/devtest"
8+
"github.com/ethereum-optimism/optimism/op-devstack/dsl"
9+
"github.com/ethereum-optimism/optimism/op-devstack/presets"
10+
"github.com/ethereum-optimism/optimism/op-node/rollup"
11+
"github.com/ethereum-optimism/optimism/op-service/eth"
12+
13+
"encoding/binary"
14+
"time"
15+
16+
"github.com/ethereum-optimism/optimism/op-chain-ops/devkeys"
17+
"github.com/ethereum-optimism/optimism/op-service/txintent/bindings"
18+
"github.com/ethereum-optimism/optimism/op-service/txintent/contractio"
19+
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
20+
"github.com/ethereum/go-ethereum/core/types"
21+
"github.com/ethereum/go-ethereum/rlp"
22+
)
23+
24+
type minBaseFeeEnv struct {
25+
l1Client *dsl.L1ELNode
26+
l2Network *dsl.L2Network
27+
l2EL *dsl.L2ELNode
28+
systemConfig minBaseFeeSystemConfig
29+
}
30+
31+
type minBaseFeeSystemConfig struct {
32+
SetMinBaseFee func(minBaseFee uint64) bindings.TypedCall[any] `sol:"setMinBaseFee"`
33+
MinBaseFee func() bindings.TypedCall[uint64] `sol:"minBaseFee"`
34+
}
35+
36+
func newMinBaseFee(t devtest.T, l2Network *dsl.L2Network, l1EL *dsl.L1ELNode, l2EL *dsl.L2ELNode) *minBaseFeeEnv {
37+
systemConfig := bindings.NewBindings[minBaseFeeSystemConfig](
38+
bindings.WithClient(l1EL.EthClient()),
39+
bindings.WithTo(l2Network.Escape().Deployment().SystemConfigProxyAddr()),
40+
bindings.WithTest(t))
41+
42+
return &minBaseFeeEnv{
43+
l1Client: l1EL,
44+
l2Network: l2Network,
45+
l2EL: l2EL,
46+
systemConfig: systemConfig,
47+
}
48+
}
49+
50+
func (mbf *minBaseFeeEnv) checkCompatibility(t devtest.T) {
51+
_, err := contractio.Read(mbf.systemConfig.MinBaseFee(), t.Ctx())
52+
if err != nil {
53+
t.Fail()
54+
}
55+
}
56+
57+
func (mbf *minBaseFeeEnv) getSystemConfigOwner(t devtest.T) *dsl.EOA {
58+
priv := mbf.l2Network.Escape().Keys().Secret(devkeys.SystemConfigOwner.Key(mbf.l2Network.ChainID().ToBig()))
59+
return dsl.NewKey(t, priv).User(mbf.l1Client)
60+
}
61+
62+
func (mbf *minBaseFeeEnv) setMinBaseFeeViaSytemConfigOnL1(t devtest.T, minBaseFee uint64) {
63+
owner := mbf.getSystemConfigOwner(t)
64+
65+
_, err := contractio.Write(mbf.systemConfig.SetMinBaseFee(minBaseFee), t.Ctx(), owner.Plan())
66+
t.Require().NoError(err, "SetMinBaseFee transaction failed")
67+
68+
t.Logf("Set min base fee on L1: minBaseFee=%d", minBaseFee)
69+
}
70+
71+
func (mbf *minBaseFeeEnv) verifyMinBaseFee(t devtest.T, minBase *big.Int) {
72+
// Wait for the next block
73+
_ = mbf.l2EL.WaitForBlock()
74+
el := mbf.l2EL.Escape().EthClient()
75+
info, err := el.InfoByLabel(t.Ctx(), "latest")
76+
t.Require().NoError(err)
77+
78+
// Verify base fee is clamped
79+
t.Require().True(info.BaseFee().Cmp(minBase) >= 0, "expected base fee to be >= minBaseFee")
80+
t.Logf("base fee %s, minBase %s", info.BaseFee(), minBase)
81+
}
82+
83+
// waitForMinBaseFeeConfigChangeOnL2 waits until the L2 latest payload extra-data encodes the expected min base fee.
84+
func (mbf *minBaseFeeEnv) waitForMinBaseFeeConfigChangeOnL2(t devtest.T, expected uint64) {
85+
client := mbf.l2EL.Escape().L2EthClient()
86+
expectedExtraData := eth.BytesMax32(eip1559.EncodeJovianExtraData(250, 6, expected))
87+
88+
// Check extradata in block header (for all clients)
89+
var actualBlockExtraData []byte
90+
t.Require().Eventually(func() bool {
91+
info, err := client.InfoByLabel(t.Ctx(), "latest")
92+
if err != nil {
93+
return false
94+
}
95+
96+
// Get header RLP and decode to access Extra field
97+
headerRLP, err := info.HeaderRLP()
98+
if err != nil {
99+
return false
100+
}
101+
102+
var header types.Header
103+
if err := rlp.DecodeBytes(headerRLP, &header); err != nil {
104+
return false
105+
}
106+
107+
if len(header.Extra) != 17 {
108+
return false
109+
}
110+
111+
got := binary.BigEndian.Uint64(header.Extra[9:])
112+
actualBlockExtraData = header.Extra
113+
return got == expected
114+
}, 2*time.Minute, 5*time.Second, "L2 min base fee in block header did not sync within timeout")
115+
116+
t.Require().Equal(expectedExtraData, eth.BytesMax32(actualBlockExtraData), "block header extradata doesnt match")
117+
}
118+
119+
// TestMinBaseFee verifies configurable minimum base fee using devstack presets.
120+
func TestMinBaseFee(gt *testing.T) {
121+
t := devtest.SerialT(gt)
122+
sys := presets.NewMinimal(t)
123+
require := t.Require()
124+
125+
err := dsl.RequiresL2Fork(t.Ctx(), sys, 0, rollup.Jovian)
126+
require.NoError(err, "Jovian fork must be active for this test")
127+
128+
minBaseFee := newMinBaseFee(t, sys.L2Chain, sys.L1EL, sys.L2EL)
129+
minBaseFee.checkCompatibility(t)
130+
131+
systemOwner := minBaseFee.getSystemConfigOwner(t)
132+
sys.FunderL1.FundAtLeast(systemOwner, eth.OneTenthEther)
133+
134+
testCases := []struct {
135+
name string
136+
minBaseFee uint64
137+
}{
138+
// High minimum base fee
139+
{"MinBaseFeeHigh", 2_000_000_000},
140+
// Medium minimum base fee
141+
{"MinBaseFeeMedium", 1_000_000_000},
142+
// Zero minimum base fee (not enforced)
143+
{"MinBaseFeeZero", 0},
144+
}
145+
146+
for _, tc := range testCases {
147+
t.Run(tc.name, func(t devtest.T) {
148+
minBaseFee.setMinBaseFeeViaSytemConfigOnL1(t, tc.minBaseFee)
149+
minBaseFee.waitForMinBaseFeeConfigChangeOnL2(t, tc.minBaseFee)
150+
151+
minBaseFee.verifyMinBaseFee(t, big.NewInt(int64(tc.minBaseFee)))
152+
153+
t.Log("Test completed successfully:",
154+
"testCase", tc.name,
155+
"minBaseFee", tc.minBaseFee)
156+
})
157+
}
158+
}

op-devstack/presets/jovian.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package presets
2+
3+
import (
4+
"github.com/ethereum-optimism/optimism/op-chain-ops/devkeys"
5+
"github.com/ethereum-optimism/optimism/op-devstack/devtest"
6+
"github.com/ethereum-optimism/optimism/op-devstack/stack"
7+
"github.com/ethereum-optimism/optimism/op-devstack/sysgo"
8+
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/intentbuilder"
9+
"github.com/ethereum-optimism/optimism/op-node/rollup"
10+
)
11+
12+
// WithJovianAtGenesis configures all L2s to activate the Jovian fork at genesis in sysgo mode.
13+
func WithJovianAtGenesis() stack.CommonOption {
14+
return stack.MakeCommon(sysgo.WithDeployerOptions(
15+
func(p devtest.P, _ devkeys.Keys, builder intentbuilder.Builder) {
16+
for _, l2Cfg := range builder.L2s() {
17+
l2Cfg.WithForkAtGenesis(rollup.Jovian)
18+
}
19+
},
20+
))
21+
}

0 commit comments

Comments
 (0)