Skip to content

Commit 81a3209

Browse files
committed
op-acceptance-tests: harden the BPO blob base fee calculation test
Before, we merely waited for the blob base fee to rise above 1 before assuming that the blob parameters would have a visible impact on the fee. Due to rounding, it is possible that different parameters still result in the same base fee even above the minimum (either 1 or the reserve price set by EIP-7918). Calculating the precise intersections between different blob base fee formulae is complicated, so we opt for the simpler but maximally robust approach: calculate both the BPO1 base fee and the Osaka base fee, and only proceed when they differ. This resolves a nit on a previous PR that was merged due to time pressure: #17529 (comment)
1 parent c0829ab commit 81a3209

File tree

2 files changed

+60
-35
lines changed

2 files changed

+60
-35
lines changed

op-acceptance-tests/tests/fusaka/fusaka_test.go

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package fusaka
33
import (
44
"context"
55
"crypto/rand"
6+
"errors"
67
"math/big"
78
"sync"
89
"testing"
@@ -18,10 +19,10 @@ import (
1819
"github.com/ethereum-optimism/optimism/op-service/txintent/bindings"
1920
"github.com/ethereum-optimism/optimism/op-service/txintent/contractio"
2021
"github.com/ethereum-optimism/optimism/op-service/txplan"
22+
"github.com/ethereum/go-ethereum"
2123
"github.com/ethereum/go-ethereum/common"
2224
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
2325
"github.com/ethereum/go-ethereum/core/types"
24-
"github.com/ethereum/go-ethereum/params"
2526
)
2627

2728
func TestSafeHeadAdvancesAfterOsaka(gt *testing.T) {
@@ -50,31 +51,12 @@ func TestBlobBaseFeeIsCorrectAfterBPOFork(gt *testing.T) {
5051
sys.L1EL.WaitForTime(*sys.L1Network.Escape().ChainConfig().BPO1Time)
5152
t.Log("BPO1 activated")
5253

53-
sys.L1EL.WaitForBlock()
54-
l1BlockTime := sys.L1EL.EstimateBlockTime()
55-
l1ChainConfig := sys.L1Network.Escape().ChainConfig()
56-
5754
spamBlobs(t, sys) // Raise the blob base fee to make blob parameter changes visible.
5855

59-
// Wait for the blob base fee to rise above 1 so the blob parameter changes will be visible.
60-
for range time.Tick(l1BlockTime) {
61-
info, _, err := sys.L1EL.EthClient().InfoAndTxsByLabel(t.Ctx(), eth.Unsafe)
62-
t.Require().NoError(err)
63-
if calcBlobBaseFee(l1ChainConfig, info).Cmp(big.NewInt(1)) > 0 {
64-
break
65-
}
66-
t.Logf("Waiting for blob base fee to rise above 1")
67-
}
68-
69-
l2UnsafeRef := sys.L2CL.SyncStatus().UnsafeL2
70-
71-
// Get the L1 blob base fee.
72-
l1OriginInfo, err := sys.L1EL.EthClient().InfoByHash(t.Ctx(), l2UnsafeRef.L1Origin.Hash)
73-
t.Require().NoError(err)
74-
l1BlobBaseFee := calcBlobBaseFee(l1ChainConfig, l1OriginInfo)
56+
l2UnsafeHash, l1BlobBaseFee := waitForBlockWithDivergentBlobBaseFee(t, sys)
7557

7658
// Get the L1 blob base fee from the system deposit tx.
77-
info, txs, err := sys.L2EL.Escape().EthClient().InfoAndTxsByHash(t.Ctx(), l2UnsafeRef.Hash)
59+
info, txs, err := sys.L2EL.Escape().EthClient().InfoAndTxsByHash(t.Ctx(), l2UnsafeHash)
7860
t.Require().NoError(err)
7961
blockInfo, err := derive.L1BlockInfoFromBytes(sys.L2Chain.Escape().RollupConfig(), info.Time(), txs[0].Data())
8062
t.Require().NoError(err)
@@ -90,6 +72,49 @@ func TestBlobBaseFeeIsCorrectAfterBPOFork(gt *testing.T) {
9072
t.Require().Equal(l1BlobBaseFee, l2BlobBaseFee)
9173
}
9274

75+
// waitForBlockWithDivergentBlobBaseFee will return an L1 blob base fee that can only be calculated
76+
// using the correct BPO1 parameters (i.e., the Osaka parameters result in a different value). It
77+
// also returns an L2 block hash from the same epoch.
78+
func waitForBlockWithDivergentBlobBaseFee(t devtest.T, sys *presets.Minimal) (common.Hash, *big.Int) {
79+
l1ChainConfig := sys.L1Network.Escape().ChainConfig()
80+
l1BlockTime := sys.L1EL.EstimateBlockTime()
81+
for {
82+
select {
83+
case <-t.Ctx().Done():
84+
t.Require().Fail("context canceled before finding a block with a divergent base fee")
85+
case <-time.After(l1BlockTime):
86+
}
87+
88+
l2UnsafeRef := sys.L2CL.SyncStatus().UnsafeL2
89+
90+
l1Info, _, err := sys.L1EL.EthClient().InfoAndTxsByHash(t.Ctx(), l2UnsafeRef.L1Origin.Hash)
91+
if errors.Is(err, ethereum.NotFound) { // Possible reorg, try again.
92+
continue
93+
}
94+
sys.T.Require().NoError(err)
95+
96+
// Note that we don't need to worry about the reserve blob gas price set by EIP-7918
97+
// because it depends on the blob parameters (if it didn't, we would also need to wait for
98+
// the L1 blob base fee to be different from the reserve price).
99+
100+
// Calculate expected blob base fee with old Osaka parameters.
101+
osakaBlobBaseFee := eip4844.CalcBlobFee(l1ChainConfig, &types.Header{
102+
Time: *l1ChainConfig.OsakaTime + 1,
103+
ExcessBlobGas: l1Info.ExcessBlobGas(),
104+
})
105+
106+
// Calculate expected blob base fee with new BPO1 parameters.
107+
bpo1BlobBaseFee := eip4844.CalcBlobFee(l1ChainConfig, &types.Header{
108+
Time: l1Info.Time(),
109+
ExcessBlobGas: l1Info.ExcessBlobGas(),
110+
})
111+
112+
if bpo1BlobBaseFee.Cmp(osakaBlobBaseFee) != 0 {
113+
return l2UnsafeRef.Hash, bpo1BlobBaseFee
114+
}
115+
}
116+
}
117+
93118
func spamBlobs(t devtest.T, sys *presets.Minimal) {
94119
l1BlockTime := sys.L1EL.EstimateBlockTime()
95120
l1ChainConfig := sys.L1Network.Escape().ChainConfig()
@@ -133,12 +158,3 @@ func spamBlobs(t devtest.T, sys *presets.Minimal) {
133158
schedule.Run(t.WithCtx(ctx), spammer)
134159
}()
135160
}
136-
137-
func calcBlobBaseFee(cfg *params.ChainConfig, info eth.BlockInfo) *big.Int {
138-
return eip4844.CalcBlobFee(cfg, &types.Header{
139-
// It's unfortunate that we can't build a proper header from a BlockInfo.
140-
// We do our best to work around deficiencies in the BlockInfo implementation here.
141-
Time: info.Time(),
142-
ExcessBlobGas: info.ExcessBlobGas(),
143-
})
144-
}

op-devstack/dsl/l1_el.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,19 @@ func (el *L1ELNode) EthClient() apis.EthClient {
4242
// EstimateBlockTime estimates the L1 block based on the last 1000 blocks
4343
// (or since genesis, if insufficient blocks).
4444
func (el *L1ELNode) EstimateBlockTime() time.Duration {
45-
latest, err := el.inner.EthClient().BlockRefByLabel(el.t.Ctx(), eth.Unsafe)
46-
el.require.NoError(err)
47-
if latest.Number == 0 {
48-
return time.Second * 12
45+
var latest eth.BlockRef
46+
for {
47+
var err error
48+
latest, err = el.inner.EthClient().BlockRefByLabel(el.t.Ctx(), eth.Unsafe)
49+
el.require.NoError(err)
50+
if latest.Number > 0 {
51+
break
52+
}
53+
select {
54+
case <-time.After(time.Millisecond * 500):
55+
case <-el.ctx.Done():
56+
el.require.Fail("context was canceled before L1 block time could be estimated")
57+
}
4958
}
5059
lowerNum := uint64(0)
5160
if latest.Number > 1000 {

0 commit comments

Comments
 (0)