Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 43 additions & 32 deletions op-acceptance-tests/tests/fusaka/fusaka_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fusaka
import (
"context"
"crypto/rand"
"errors"
"math/big"
"sync"
"testing"
Expand All @@ -18,10 +19,10 @@ import (
"github.com/ethereum-optimism/optimism/op-service/txintent/bindings"
"github.com/ethereum-optimism/optimism/op-service/txintent/contractio"
"github.com/ethereum-optimism/optimism/op-service/txplan"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
)

func TestSafeHeadAdvancesAfterOsaka(gt *testing.T) {
Expand Down Expand Up @@ -50,30 +51,10 @@ func TestBlobBaseFeeIsCorrectAfterBPOFork(gt *testing.T) {
sys.L1EL.WaitForTime(*sys.L1Network.Escape().ChainConfig().BPO1Time)
t.Log("BPO1 activated")

sys.L1EL.WaitForBlock()
l1BlockTime := sys.L1EL.EstimateBlockTime()
l1ChainConfig := sys.L1Network.Escape().ChainConfig()

spamBlobs(t, sys) // Raise the blob base fee to make blob parameter changes visible.

// Wait for the blob base fee to rise above 1 so the blob parameter changes will be visible.
for range time.Tick(l1BlockTime) {
info, _, err := sys.L1EL.EthClient().InfoAndTxsByLabel(t.Ctx(), eth.Unsafe)
t.Require().NoError(err)
if calcBlobBaseFee(l1ChainConfig, info).Cmp(big.NewInt(1)) > 0 {
break
}
t.Logf("Waiting for blob base fee to rise above 1")
}

l2UnsafeRef := sys.L2CL.SyncStatus().UnsafeL2

// Get the L1 blob base fee.
l1OriginInfo, err := sys.L1EL.EthClient().InfoByHash(t.Ctx(), l2UnsafeRef.L1Origin.Hash)
t.Require().NoError(err)
l1BlobBaseFee := calcBlobBaseFee(l1ChainConfig, l1OriginInfo)

l2Info, l2Txs, err := sys.L2EL.Escape().EthClient().InfoAndTxsByHash(t.Ctx(), l2UnsafeRef.Hash)
l2UnsafeHash, l1BlobBaseFee := waitForNonTrivialBPO1Block(t, sys)
l2Info, l2Txs, err := sys.L2EL.Escape().EthClient().InfoAndTxsByHash(t.Ctx(), l2UnsafeHash)
t.Require().NoError(err)

// Check the L1 blob base fee in the system deposit tx.
Expand All @@ -91,6 +72,45 @@ func TestBlobBaseFeeIsCorrectAfterBPOFork(gt *testing.T) {
t.Require().Equal(l1BlobBaseFee, l2BlobBaseFee)
}

// waitForNonTrivialBPO1Block will return an L1 blob base fee that can only be calculated using the
// correct BPO1 parameters (i.e., the Osaka parameters result in a different value). It also
// returns an L2 block hash from the same epoch.
func waitForNonTrivialBPO1Block(t devtest.T, sys *presets.Minimal) (common.Hash, *big.Int) {
l1ChainConfig := sys.L1Network.Escape().ChainConfig()
l1BlockTime := sys.L1EL.EstimateBlockTime()
for {
l2UnsafeRef := sys.L2CL.SyncStatus().UnsafeL2

l1Info, _, err := sys.L1EL.EthClient().InfoAndTxsByHash(t.Ctx(), l2UnsafeRef.L1Origin.Hash)
if errors.Is(err, ethereum.NotFound) { // Possible reorg, try again.
continue
}
t.Require().NoError(err)

// Calculate expected blob base fee with old Osaka parameters.
osakaBlobBaseFee := eip4844.CalcBlobFee(l1ChainConfig, &types.Header{
Time: *l1ChainConfig.OsakaTime,
ExcessBlobGas: l1Info.ExcessBlobGas(),
})

// Calculate expected blob base fee with new BPO1 parameters.
bpo1BlobBaseFee := eip4844.CalcBlobFee(l1ChainConfig, &types.Header{
Time: l1Info.Time(),
ExcessBlobGas: l1Info.ExcessBlobGas(),
})

if bpo1BlobBaseFee.Cmp(osakaBlobBaseFee) != 0 {
return l2UnsafeRef.Hash, bpo1BlobBaseFee
}

select {
case <-t.Ctx().Done():
t.Require().Fail("context canceled before finding a block with a divergent base fee")
case <-time.After(l1BlockTime):
}
}
}

func spamBlobs(t devtest.T, sys *presets.Minimal) {
l1BlockTime := sys.L1EL.EstimateBlockTime()
l1ChainConfig := sys.L1Network.Escape().ChainConfig()
Expand Down Expand Up @@ -134,12 +154,3 @@ func spamBlobs(t devtest.T, sys *presets.Minimal) {
schedule.Run(t.WithCtx(ctx), spammer)
}()
}

func calcBlobBaseFee(cfg *params.ChainConfig, info eth.BlockInfo) *big.Int {
return eip4844.CalcBlobFee(cfg, &types.Header{
// It's unfortunate that we can't build a proper header from a BlockInfo.
// We do our best to work around deficiencies in the BlockInfo implementation here.
Time: info.Time(),
ExcessBlobGas: info.ExcessBlobGas(),
})
}
4 changes: 3 additions & 1 deletion op-acceptance-tests/tests/fusaka/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ func TestMain(m *testing.M) {
sysgo.WithDeployerOptions(func(_ devtest.P, _ devkeys.Keys, builder intentbuilder.Builder) {
_, l1Config := builder.WithL1(sysgo.DefaultL1ID)
l1Config.WithOsakaOffset(0)
l1Config.WithBPO1Offset(0)
// Make the BPO fork happen after Osaka so we can easily use geth's eip4844.CalcBlobFee
// to calculate the blob base fee using the Osaka parameters.
l1Config.WithBPO1Offset(1)
l1Config.WithL1BlobSchedule(&params.BlobScheduleConfig{
Cancun: params.DefaultCancunBlobConfig,
Osaka: params.DefaultOsakaBlobConfig,
Expand Down
17 changes: 13 additions & 4 deletions op-devstack/dsl/l1_el.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,19 @@ func (el *L1ELNode) EthClient() apis.EthClient {
// EstimateBlockTime estimates the L1 block based on the last 1000 blocks
// (or since genesis, if insufficient blocks).
func (el *L1ELNode) EstimateBlockTime() time.Duration {
latest, err := el.inner.EthClient().BlockRefByLabel(el.t.Ctx(), eth.Unsafe)
el.require.NoError(err)
if latest.Number == 0 {
return time.Second * 12
var latest eth.BlockRef
for {
var err error
latest, err = el.inner.EthClient().BlockRefByLabel(el.t.Ctx(), eth.Unsafe)
el.require.NoError(err)
if latest.Number > 0 {
break
}
select {
case <-time.After(time.Millisecond * 500):
case <-el.ctx.Done():
el.require.Fail("context was canceled before L1 block time could be estimated")
}
}
lowerNum := uint64(0)
if latest.Number > 1000 {
Expand Down