Skip to content
Closed
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
2 changes: 2 additions & 0 deletions op-chain-ops/genesis/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ type UpgradeScheduleDeployConfig struct {
L1CancunTimeOffset *hexutil.Uint64 `json:"l1CancunTimeOffset,omitempty"`
// When Prague activates. Relative to L1 genesis.
L1PragueTimeOffset *hexutil.Uint64 `json:"l1PragueTimeOffset,omitempty"`
// When Fusaka activates. Relative to L1 genesis.
L1OsakaTimeOffset *hexutil.Uint64 `json:"l1FusakaTimeOffset,omitempty"`
}

var _ ConfigChecker = (*UpgradeScheduleDeployConfig)(nil)
Expand Down
7 changes: 7 additions & 0 deletions op-chain-ops/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ func NewL1Genesis(config *DeployConfig) (*core.Genesis, error) {
DevL1DeployConfig: config.DevL1DeployConfig,
L1ChainID: eth.ChainIDFromUInt64(config.L1ChainID),
L1PragueTimeOffset: (*uint64)(config.L1PragueTimeOffset),
L1OsakaTimeOffset: (*uint64)(config.L1OsakaTimeOffset),
})
}

Expand All @@ -148,6 +149,8 @@ type DevL1DeployConfigMinimal struct {
L1ChainID eth.ChainID
// When Prague activates. Relative to L1 genesis.
L1PragueTimeOffset *uint64
// When Fusaka activates. Relative to L1 genesis.
L1OsakaTimeOffset *uint64
}

// NewL1GenesisMinimal creates a L1 dev genesis template.
Expand Down Expand Up @@ -203,6 +206,10 @@ func NewL1GenesisMinimal(config *DevL1DeployConfigMinimal) (*core.Genesis, error
pragueTime := uint64(timestamp) + uint64(*config.L1PragueTimeOffset)
chainConfig.PragueTime = &pragueTime
}
if config.L1OsakaTimeOffset != nil {
osakaTime := uint64(timestamp) + uint64(*config.L1OsakaTimeOffset)
chainConfig.OsakaTime = &osakaTime
}
// Note: excess-blob-gas, blob-gas-used, withdrawals-hash, requests-hash are set to reasonable defaults for L1 by the ToBlock() function
return &core.Genesis{
Config: &chainConfig,
Expand Down
11 changes: 11 additions & 0 deletions op-e2e/actions/helpers/l1_miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,17 @@ func (s *L1Miner) ActEmptyBlock(t Testing) *types.Block {
return s.ActL1EndBlock(t)
}

func (s *L1Miner) ActBuildToOsaka(t Testing) *types.Block {
t.Helper()
require.NotNil(t, s.l1Cfg.Config.OsakaTime, "cannot activate OsakaTime when it is not scheduled")
h := s.L1Chain().CurrentHeader()
for h.Time < *s.l1Cfg.Config.OsakaTime {
h = s.ActEmptyBlock(t).Header()
}
require.True(t, s.l1Cfg.Config.IsOsaka(h.Number, h.Time), "Osaka not active at block", h.Number)
return s.L1Chain().GetBlockByHash(h.Hash())
}

func (s *L1Miner) Close() error {
return s.L1Replica.Close()
}
4 changes: 2 additions & 2 deletions op-e2e/actions/helpers/l2_sequencer.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@ func (s *L2Sequencer) ActL2ForceAdvanceL1Origin(t Testing) {
s.mockL1OriginSelector.originOverride = nextOrigin
}

// ActBuildToL1Head builds empty blocks until (incl.) the L1 head becomes the L2 origin
// ActBuildToL1Head builds empty blocks until (incl.) the L1 head becomes the L1 origin of the L2 head
func (s *L2Sequencer) ActBuildToL1Head(t Testing) {
for s.engine.UnsafeL2Head().L1Origin.Number < s.syncStatus.L1Head().Number {
for s.L2Unsafe().L1Origin.Number < s.syncStatus.L1Head().Number {
s.ActL2PipelineFull(t)
s.ActL2EmptyBlock(t)
}
Expand Down
106 changes: 106 additions & 0 deletions op-e2e/actions/proofs/l1_osaka_fork_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package proofs_test

import (
"math/big"
"testing"

batcherFlags "github.com/ethereum-optimism/optimism/op-batcher/flags"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers"
"github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers"
legacybindings "github.com/ethereum-optimism/optimism/op-e2e/bindings"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/predeploys"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/stretchr/testify/require"
)

func Test_ProgramAction_OsakaForkAfterGenesis(gt *testing.T) {
runL1FusakaTest := func(gt *testing.T, testCfg *helpers.TestCfg[any]) {
t := actionsHelpers.NewDefaultTesting(gt)

// Create test environment with Fusaka activation
env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(),
helpers.NewBatcherCfg(
func(c *actionsHelpers.BatcherCfg) {
c.DataAvailabilityType = batcherFlags.BlobsType
},
),
func(dp *genesis.DeployConfig) {
dp.L1PragueTimeOffset = ptr(hexutil.Uint64(0))
dp.L1OsakaTimeOffset = ptr(hexutil.Uint64(24))
// TODO add the BPO forks
dp.L1GenesisBlockExcessBlobGas = ptr(hexutil.Uint64(1e8)) // Jack up the blob market so we can test the blob fee calculation
},
)

miner, sequencer := env.Miner, env.Sequencer

// Bind to L1Block contract on L2
l1BlockContract, err := legacybindings.NewL1Block(predeploys.L1BlockAddr, env.Engine.EthClient())
require.NoError(t, err)

atBlockWithHash := func(hash common.Hash) *bind.CallOpts {
return &bind.CallOpts{
BlockHash: hash,
}
}

// requireConsistentBlobBaseFee requires the blob base fee to be consistent between
// the L1 Origin block (computed using the excess blob gas and l1 chain config)
// and the L1 Block contract on L2 (acessed with a contract method call).
requireConsistentBlobBaseFee := func(t actionsHelpers.StatefulTesting, l2Block eth.L2BlockRef, expectL1OriginToBeFusaka bool) {
bbfL2, err := l1BlockContract.BlobBaseFee(atBlockWithHash(l2Block.Hash))
require.NoError(t, err)

l1Origin := miner.L1Chain().GetHeaderByHash(l2Block.L1Origin.Hash)
if expectL1OriginToBeFusaka {
require.True(t, env.Sd.L1Cfg.Config.IsOsaka(l1Origin.Number, l1Origin.Time), "Osaka not active at l1 origin %d, time %d", l1Origin.Number, l1Origin.Time)
} else {
require.False(t, env.Sd.L1Cfg.Config.IsOsaka(l1Origin.Number, l1Origin.Time), "Osaka should not be active at l1 origin %d, time %d", l1Origin.Number, l1Origin.Time)
}
bbfL1 := eip4844.CalcBlobFee(env.Sd.L1Cfg.Config, l1Origin)

require.True(t, bbfL2.Cmp(bbfL1) == 0,
"blob base fee does not match, bbfL2=%d, bbfL1=%d, l1BlockNum=%d, l2BlockNum=%d", bbfL2, bbfL1, l1Origin.Number, l2Block.Number)

require.True(t, bbfL2.Cmp(big.NewInt(1)) > 0,
"blob base fee is unrealistically low and doesn't exercise the blob fee calculation")
}

// Build L1 blocks to trigger Fusaka activation
// TODO in the current version of op-geth, the blob parameters don't change between Prague and Osaka.
// So this test is no useful until we can activate different blob parameters.
Comment on lines +74 to +76
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Highlighting this. We should be able to override the blob schedule for osaka and trigger a test failure (currently it passes).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, should we create a issue or draft-PR for hint? or its okay and just keep it in mind?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to keep working on this branch (if that's ok with you) and try to get the test to fail with a custom blob schedule. Then we will get a review from another teammate and look to merge the PR (potentially with the test skipped).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bug may be preventing us from installing a custom blob schedule ethereum-optimism/op-geth#685

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got around that with a custom op-geth build here #17666

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to keep working on this branch (if that's ok with you) and try to get the test to fail with a custom blob schedule. Then we will get a review from another teammate and look to merge the PR (potentially with the test skipped).

Its 100% fine with me, sorry for slow respond, stuck in some personal task recently.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got around that with a custom op-geth build here #17666

Looks like this has been merged, should I close this PR now?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep I will close it. Thanks for kicking this off, you are attributed on the merged PR.

l1Block := miner.ActBuildToOsaka(t)
require.Equal(t, uint64(2), l1Block.Number().Uint64())

// Build an empty L2 block which has a pre-Fusaka L1 origin, and check the blob fee is correct
sequencer.ActL2EmptyBlock(t)
l2Block := sequencer.SyncStatus().UnsafeL2
require.Equal(t, uint64(1), l2Block.Number)
requireConsistentBlobBaseFee(t, l2Block, false)

// Advance L2 chain until L1 origin has Fusaka activ
sequencer.ActL1HeadSignal(t)
sequencer.ActBuildToL1HeadUnsafe(t)

// Check that the L1 origin is now a Fusaka block, and that the blob fee is correct
l2Block = sequencer.L2Unsafe()
require.Greater(t, l2Block.Number, uint64(1))
requireConsistentBlobBaseFee(t, l2Block, true)

// Final sync
env.BatchMineAndSync(t)

// Run fault proof program
safeL2Head := sequencer.L2Safe()
env.RunFaultProofProgramFromGenesis(t, safeL2Head.Number, testCfg.CheckResult, testCfg.InputParams...)
}

matrix := helpers.NewMatrix[any]()
defer matrix.Run(gt)
matrix.AddDefaultTestCases(nil, helpers.NewForkMatrix(helpers.LatestFork), runL1FusakaTest)
}
6 changes: 5 additions & 1 deletion op-e2e/actions/proofs/l1_prague_fork_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ func Test_ProgramAction_PragueForkAfterGenesis(gt *testing.T) {
},
),
func(dp *genesis.DeployConfig) {
dp.L1PragueTimeOffset = ptr(hexutil.Uint64(24)) // Activate at second l1 block
dp.L1PragueTimeOffset = ptr(hexutil.Uint64(24)) // Activate at second l1 block
dp.L1GenesisBlockExcessBlobGas = ptr(hexutil.Uint64(1e8)) // Jack up the blob market so we can test the blob fee calculation

},
)

Expand Down Expand Up @@ -98,6 +100,8 @@ func Test_ProgramAction_PragueForkAfterGenesis(gt *testing.T) {
bbf, err := l1Block.BlobBaseFee(&bind.CallOpts{BlockHash: l2Block.Hash})
require.NoError(t, err, "failed to get blob base fee")
require.Equal(t, expectedBbf.Uint64(), bbf.Uint64(), "l1Block blob base fee does not match expectation, l1BlockNum %d, l2BlockNum %d", l1BlockID.Number, l2Block.Number)
require.Greater(t, bbf.Uint64(), uint64(1),
"blob base fee is unrealistically low and doesn't exercise the blob fee calculation")
}

requireSafeHeadProgression := func(t actionsHelpers.StatefulTesting, safeL2Before, safeL2After eth.L2BlockRef, batchedWithSetCodeTx bool) {
Expand Down