diff --git a/op-e2e/actions/eip4844_test.go b/op-e2e/actions/eip4844_test.go new file mode 100644 index 000000000000..cc97025d806a --- /dev/null +++ b/op-e2e/actions/eip4844_test.go @@ -0,0 +1,147 @@ +package actions + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + + batcherFlags "github.com/ethereum-optimism/optimism/op-batcher/flags" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" + "github.com/ethereum-optimism/optimism/op-node/rollup/sync" + "github.com/ethereum-optimism/optimism/op-service/testlog" +) + +func setupEIP4844Test(t Testing, log log.Logger) (*e2eutils.SetupData, *e2eutils.DeployParams, *L1Miner, *L2Sequencer, *L2Engine, *L2Verifier, *L2Engine) { + dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) + genesisActivation := hexutil.Uint64(0) + dp.DeployConfig.L1CancunTimeOffset = &genesisActivation + dp.DeployConfig.L2GenesisCanyonTimeOffset = &genesisActivation + dp.DeployConfig.L2GenesisDeltaTimeOffset = &genesisActivation + dp.DeployConfig.L2GenesisEcotoneTimeOffset = &genesisActivation + + sd := e2eutils.Setup(t, dp, defaultAlloc) + miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) + miner.ActL1SetFeeRecipient(common.Address{'A'}) + sequencer.ActL2PipelineFull(t) + verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) + return sd, dp, miner, sequencer, seqEngine, verifier, verifEngine +} + +func setupBatcher(t Testing, log log.Logger, sd *e2eutils.SetupData, dp *e2eutils.DeployParams, miner *L1Miner, + sequencer *L2Sequencer, engine *L2Engine, daType batcherFlags.DataAvailabilityType) *L2Batcher { + return NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ + MinL1TxSize: 0, + MaxL1TxSize: 128_000, + BatcherKey: dp.Secrets.Batcher, + DataAvailabilityType: daType, + }, sequencer.RollupClient(), miner.EthClient(), engine.EthClient(), engine.EngineClient(t, sd.RollupCfg)) +} + +func TestEIP4844DataAvailability(gt *testing.T) { + t := NewDefaultTesting(gt) + + log := testlog.Logger(t, log.LvlDebug) + sd, dp, miner, sequencer, seqEngine, verifier, _ := setupEIP4844Test(t, log) + + batcher := setupBatcher(t, log, sd, dp, miner, sequencer, seqEngine, batcherFlags.BlobsType) + + sequencer.ActL2PipelineFull(t) + verifier.ActL2PipelineFull(t) + + // build empty L1 block + miner.ActEmptyBlock(t) + // finalize it, so the L1 geth blob pool doesn't log errors about missing finality + miner.ActL1SafeNext(t) + miner.ActL1FinalizeNext(t) + + // Create L2 blocks, and reference the L1 head as origin + sequencer.ActL1HeadSignal(t) + sequencer.ActBuildToL1Head(t) + + // submit all new L2 blocks + batcher.ActSubmitAll(t) + batchTx := batcher.LastSubmitted + + // new L1 block with L2 batch + miner.ActL1StartBlock(12)(t) + miner.ActL1IncludeTxByHash(batchTx.Hash())(t) + miner.ActL1EndBlock(t) + + require.Equal(t, uint8(types.BlobTxType), batchTx.Type(), "batch tx must be blob-tx") + + // verifier picks up the L2 chain that was submitted + verifier.ActL1HeadSignal(t) + verifier.ActL2PipelineFull(t) + require.Equal(t, verifier.L2Safe(), sequencer.L2Unsafe(), "verifier syncs from sequencer via L1") + require.NotEqual(t, sequencer.L2Safe(), sequencer.L2Unsafe(), "sequencer has not processed L1 yet") +} + +func TestEIP4844DataAvailabilitySwitch(gt *testing.T) { + t := NewDefaultTesting(gt) + + log := testlog.Logger(t, log.LvlDebug) + sd, dp, miner, sequencer, seqEngine, verifier, _ := setupEIP4844Test(t, log) + + oldBatcher := setupBatcher(t, log, sd, dp, miner, sequencer, seqEngine, batcherFlags.CalldataType) + + sequencer.ActL2PipelineFull(t) + verifier.ActL2PipelineFull(t) + + // build empty L1 block + miner.ActEmptyBlock(t) + // finalize it, so the L1 geth blob pool doesn't log errors about missing finality + miner.ActL1SafeNext(t) + miner.ActL1FinalizeNext(t) + + // Create L2 blocks, and reference the L1 head as origin + sequencer.ActL1HeadSignal(t) + sequencer.ActBuildToL1Head(t) + + // submit all new L2 blocks, with legacy calldata DA + oldBatcher.ActSubmitAll(t) + batchTx := oldBatcher.LastSubmitted + + // new L1 block with L2 batch + miner.ActL1StartBlock(12)(t) + miner.ActL1IncludeTxByHash(batchTx.Hash())(t) + miner.ActL1EndBlock(t) + + require.Equal(t, uint8(types.DynamicFeeTxType), batchTx.Type(), "batch tx must be eip1559 tx") + + // verifier picks up the L2 chain that was submitted + verifier.ActL1HeadSignal(t) + verifier.ActL2PipelineFull(t) + require.Equal(t, verifier.L2Safe(), sequencer.L2Unsafe(), "verifier syncs from sequencer via L1") + require.NotEqual(t, sequencer.L2Safe(), sequencer.L2Unsafe(), "sequencer has not processed L1 yet") + + newBatcher := setupBatcher(t, log, sd, dp, miner, sequencer, seqEngine, batcherFlags.BlobsType) + + // build empty L1 block + miner.ActEmptyBlock(t) + + // Create L2 blocks, and reference the L1 head as origin + sequencer.ActL1HeadSignal(t) + sequencer.ActBuildToL1Head(t) + + // submit all new L2 blocks, now with Blobs DA! + newBatcher.ActSubmitAll(t) + batchTx = newBatcher.LastSubmitted + + // new L1 block with L2 batch + miner.ActL1StartBlock(12)(t) + miner.ActL1IncludeTxByHash(batchTx.Hash())(t) + miner.ActL1EndBlock(t) + + require.Equal(t, uint8(types.BlobTxType), batchTx.Type(), "batch tx must be blob-tx") + + // verifier picks up the L2 chain that was submitted + verifier.ActL1HeadSignal(t) + verifier.ActL2PipelineFull(t) + require.Equal(t, verifier.L2Safe(), sequencer.L2Unsafe(), "verifier syncs from sequencer via L1") + require.NotEqual(t, sequencer.L2Safe(), sequencer.L2Unsafe(), "sequencer has not processed L1 yet") +} diff --git a/op-e2e/actions/l1_miner.go b/op-e2e/actions/l1_miner.go index 56f508d095d2..6ca4090bcba5 100644 --- a/op-e2e/actions/l1_miner.go +++ b/op-e2e/actions/l1_miner.go @@ -3,20 +3,28 @@ package actions import ( "math/big" + "github.com/stretchr/testify/require" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie" - "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-service/eth" ) // L1Miner wraps a L1Replica with instrumented block building ability. type L1Miner struct { L1Replica + blobStore *e2eutils.BlobsStore + // L1 block building preferences prefCoinbase common.Address @@ -29,6 +37,8 @@ type L1Miner struct { l1Receipts []*types.Receipt // collect receipts of ongoing building l1Building bool l1TxFailed []*types.Transaction // log of failed transactions which could not be included + // sidecars that come with the transactions + l1BuildingBlobSidecars []*types.BlobTxSidecar } // NewL1Miner creates a new L1Replica that can also build blocks. @@ -36,9 +46,14 @@ func NewL1Miner(t Testing, log log.Logger, genesis *core.Genesis) *L1Miner { rep := NewL1Replica(t, log, genesis) return &L1Miner{ L1Replica: *rep, + blobStore: e2eutils.NewBlobStore(), } } +func (s *L1Miner) BlobStore() derive.L1BlobsFetcher { + return s.blobStore +} + // ActL1StartBlock returns an action to build a new L1 block on top of the head block, // with timeDelta added to the head block time. func (s *L1Miner) ActL1StartBlock(timeDelta uint64) Action { @@ -77,11 +92,9 @@ func (s *L1Miner) ActL1StartBlock(timeDelta uint64) Action { header.WithdrawalsHash = &types.EmptyWithdrawalsHash } if s.l1Cfg.Config.IsCancun(header.Number, header.Time) { - var root common.Hash - var zero uint64 - header.BlobGasUsed = &zero - header.ExcessBlobGas = &zero - header.ParentBeaconRoot = &root + header.BlobGasUsed = new(uint64) + header.ExcessBlobGas = new(uint64) + header.ParentBeaconRoot = new(common.Hash) } s.l1Building = true @@ -90,6 +103,7 @@ func (s *L1Miner) ActL1StartBlock(timeDelta uint64) Action { s.l1Receipts = make([]*types.Receipt, 0) s.l1Transactions = make([]*types.Transaction, 0) s.pendingIndices = make(map[common.Address]uint64) + s.l1BuildingBlobSidecars = make([]*types.BlobTxSidecar, 0) s.l1GasPool = new(core.GasPool).AddGas(header.GasLimit) } @@ -111,6 +125,22 @@ func (s *L1Miner) ActL1IncludeTx(from common.Address) Action { } } +// ActL1IncludeTxByHash tries to include a tx by tx-hash. +func (s *L1Miner) ActL1IncludeTxByHash(txHash common.Hash) Action { + return func(t Testing) { + if !s.l1Building { + t.InvalidAction("no tx inclusion when not building l1 block") + return + } + tx := s.eth.TxPool().Get(txHash) + require.NotNil(t, tx, "cannot find tx %s", txHash) + s.IncludeTx(t, tx) + from, err := s.l1Signer.Sender(tx) + require.NoError(t, err) + s.pendingIndices[from] = s.pendingIndices[from] + 1 // won't retry the tx + } +} + func (s *L1Miner) IncludeTx(t Testing, tx *types.Transaction) { from, err := s.l1Signer.Sender(tx) require.NoError(t, err) @@ -124,13 +154,21 @@ func (s *L1Miner) IncludeTx(t Testing, tx *types.Transaction) { } s.l1BuildingState.SetTxContext(tx.Hash(), len(s.l1Transactions)) receipt, err := core.ApplyTransaction(s.l1Cfg.Config, s.l1Chain, &s.l1BuildingHeader.Coinbase, - s.l1GasPool, s.l1BuildingState, s.l1BuildingHeader, tx, &s.l1BuildingHeader.GasUsed, *s.l1Chain.GetVMConfig()) + s.l1GasPool, s.l1BuildingState, s.l1BuildingHeader, tx.WithoutBlobTxSidecar(), &s.l1BuildingHeader.GasUsed, *s.l1Chain.GetVMConfig()) if err != nil { s.l1TxFailed = append(s.l1TxFailed, tx) t.Fatalf("failed to apply transaction to L1 block (tx %d): %v", len(s.l1Transactions), err) } s.l1Receipts = append(s.l1Receipts, receipt) - s.l1Transactions = append(s.l1Transactions, tx) + s.l1Transactions = append(s.l1Transactions, tx.WithoutBlobTxSidecar()) + if tx.Type() == types.BlobTxType { + require.True(t, s.l1Cfg.Config.IsCancun(s.l1BuildingHeader.Number, s.l1BuildingHeader.Time), "L1 must be cancun to process blob tx") + sidecar := tx.BlobTxSidecar() + if sidecar != nil { + s.l1BuildingBlobSidecars = append(s.l1BuildingBlobSidecars, sidecar) + } + *s.l1BuildingHeader.BlobGasUsed += receipt.BlobGasUsed + } } func (s *L1Miner) ActL1SetFeeRecipient(coinbase common.Address) { @@ -154,6 +192,19 @@ func (s *L1Miner) ActL1EndBlock(t Testing) { if s.l1Cfg.Config.IsShanghai(s.l1BuildingHeader.Number, s.l1BuildingHeader.Time) { block = block.WithWithdrawals(make([]*types.Withdrawal, 0)) } + if s.l1Cfg.Config.IsCancun(s.l1BuildingHeader.Number, s.l1BuildingHeader.Time) { + parent := s.l1Chain.GetHeaderByHash(s.l1BuildingHeader.ParentHash) + var ( + parentExcessBlobGas uint64 + parentBlobGasUsed uint64 + ) + if parent.ExcessBlobGas != nil { + parentExcessBlobGas = *parent.ExcessBlobGas + parentBlobGasUsed = *parent.BlobGasUsed + } + excessBlobGas := eip4844.CalcExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed) + s.l1BuildingHeader.ExcessBlobGas = &excessBlobGas + } // Write state changes to db root, err := s.l1BuildingState.Commit(s.l1BuildingHeader.Number.Uint64(), s.l1Cfg.Config.IsEIP158(s.l1BuildingHeader.Number)) @@ -163,7 +214,13 @@ func (s *L1Miner) ActL1EndBlock(t Testing) { if err := s.l1BuildingState.Database().TrieDB().Commit(root, false); err != nil { t.Fatalf("l1 trie write error: %v", err) } - + // now that the blob txs are in a canonical block, flush them to the blob store + for _, sidecar := range s.l1BuildingBlobSidecars { + for i, h := range sidecar.BlobHashes() { + blob := (*eth.Blob)(&sidecar.Blobs[i]) + s.blobStore.StoreBlob(block.Hash(), h, blob) + } + } _, err = s.l1Chain.InsertChain(types.Blocks{block}) if err != nil { t.Fatalf("failed to insert block into l1 chain") diff --git a/op-e2e/actions/l2_batcher.go b/op-e2e/actions/l2_batcher.go index 66e3b9ea2182..7a0cb40c6aae 100644 --- a/op-e2e/actions/l2_batcher.go +++ b/op-e2e/actions/l2_batcher.go @@ -8,18 +8,24 @@ import ( "io" "math/big" + "github.com/holiman/uint256" + "github.com/stretchr/testify/require" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "github.com/stretchr/testify/require" "github.com/ethereum-optimism/optimism/op-batcher/compressor" + batcherFlags "github.com/ethereum-optimism/optimism/op-batcher/flags" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/txmgr" ) type SyncStatusAPI interface { @@ -47,6 +53,17 @@ type BatcherCfg struct { ForceSubmitSingularBatch bool ForceSubmitSpanBatch bool + + DataAvailabilityType batcherFlags.DataAvailabilityType +} + +func DefaultBatcherCfg(dp *e2eutils.DeployParams) *BatcherCfg { + return &BatcherCfg{ + MinL1TxSize: 0, + MaxL1TxSize: 128_000, + BatcherKey: dp.Secrets.Batcher, + DataAvailabilityType: batcherFlags.CalldataType, + } } type L2BlockRefs interface { @@ -76,6 +93,8 @@ type L2Batcher struct { l2SubmittedBlock eth.L2BlockRef l2BatcherCfg *BatcherCfg batcherAddr common.Address + + LastSubmitted *types.Transaction } func NewL2Batcher(log log.Logger, rollupCfg *rollup.Config, batcherCfg *BatcherCfg, api SyncStatusAPI, l1 L1TxAPI, l2 BlocksAPI, engCl L2BlockRefs) *L2Batcher { @@ -220,26 +239,58 @@ func (s *L2Batcher) ActL2BatchSubmit(t Testing, txOpts ...func(tx *types.Dynamic require.NoError(t, err, "need l1 pending header for gas price estimation") gasFeeCap := new(big.Int).Add(gasTipCap, new(big.Int).Mul(pendingHeader.BaseFee, big.NewInt(2))) - rawTx := &types.DynamicFeeTx{ - ChainID: s.rollupCfg.L1ChainID, - Nonce: nonce, - To: &s.rollupCfg.BatchInboxAddress, - GasTipCap: gasTipCap, - GasFeeCap: gasFeeCap, - Data: data.Bytes(), - } - for _, opt := range txOpts { - opt(rawTx) + var txData types.TxData + if s.l2BatcherCfg.DataAvailabilityType == batcherFlags.CalldataType { + rawTx := &types.DynamicFeeTx{ + ChainID: s.rollupCfg.L1ChainID, + Nonce: nonce, + To: &s.rollupCfg.BatchInboxAddress, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Data: data.Bytes(), + } + for _, opt := range txOpts { + opt(rawTx) + } + + gas, err := core.IntrinsicGas(rawTx.Data, nil, false, true, true, false) + require.NoError(t, err, "need to compute intrinsic gas") + rawTx.Gas = gas + txData = rawTx + } else if s.l2BatcherCfg.DataAvailabilityType == batcherFlags.BlobsType { + var b eth.Blob + require.NoError(t, b.FromData(data.Bytes()), "must turn data into blob") + sidecar, blobHashes, err := txmgr.MakeSidecar([]*eth.Blob{&b}) + require.NoError(t, err) + require.NotNil(t, pendingHeader.ExcessBlobGas, "need L1 header with 4844 properties") + blobBaseFee := eip4844.CalcBlobFee(*pendingHeader.ExcessBlobGas) + blobFeeCap := new(uint256.Int).Mul(uint256.NewInt(2), uint256.MustFromBig(blobBaseFee)) + if blobFeeCap.Lt(uint256.NewInt(params.GWei)) { // ensure we meet 1 gwei geth tx-pool minimum + blobFeeCap = uint256.NewInt(params.GWei) + } + txData = &types.BlobTx{ + To: s.rollupCfg.BatchInboxAddress, + Data: nil, + Gas: params.TxGas, // intrinsic gas only + BlobHashes: blobHashes, + Sidecar: sidecar, + ChainID: uint256.MustFromBig(s.rollupCfg.L1ChainID), + GasTipCap: uint256.MustFromBig(gasTipCap), + GasFeeCap: uint256.MustFromBig(gasFeeCap), + BlobFeeCap: blobFeeCap, + Value: uint256.NewInt(0), + Nonce: nonce, + } + } else { + t.Fatalf("unrecognized DA type: %q", string(s.l2BatcherCfg.DataAvailabilityType)) } - gas, err := core.IntrinsicGas(rawTx.Data, nil, false, true, true, false) - require.NoError(t, err, "need to compute intrinsic gas") - rawTx.Gas = gas - tx, err := types.SignNewTx(s.l2BatcherCfg.BatcherKey, s.l1Signer, rawTx) + tx, err := types.SignNewTx(s.l2BatcherCfg.BatcherKey, s.l1Signer, txData) require.NoError(t, err, "need to sign tx") err = s.l1.SendTransaction(t.Ctx(), tx) require.NoError(t, err, "need to send tx") + s.LastSubmitted = tx } // ActL2BatchSubmitGarbage constructs a malformed channel frame and submits it to the diff --git a/op-e2e/actions/l2_batcher_test.go b/op-e2e/actions/l2_batcher_test.go index e3bb484a655d..1d1a43533069 100644 --- a/op-e2e/actions/l2_batcher_test.go +++ b/op-e2e/actions/l2_batcher_test.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/require" + batcherFlags "github.com/ethereum-optimism/optimism/op-batcher/flags" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/sync" @@ -84,14 +85,11 @@ func NormalBatcher(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { sd := e2eutils.Setup(t, dp, defaultAlloc) log := testlog.Logger(t, log.LvlDebug) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) - verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) rollupSeqCl := sequencer.RollupClient() - batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) // Alice makes a L2 tx cl := seqEngine.EthClient() @@ -186,11 +184,8 @@ func L2Finalization(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { require.Equal(t, uint64(1), sequencer.SyncStatus().FinalizedL1.Number) require.Equal(t, uint64(0), sequencer.SyncStatus().FinalizedL2.Number, "L2 block has to be included on L1 before it can be finalized") - batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, sequencer.RollupClient(), miner.EthClient(), engine.EthClient(), engine.EngineClient(t, sd.RollupCfg)) + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + sequencer.RollupClient(), miner.EthClient(), engine.EthClient(), engine.EngineClient(t, sd.RollupCfg)) heightToSubmit := sequencer.SyncStatus().UnsafeL2.Number @@ -273,11 +268,8 @@ func L2FinalizationWithSparseL1(gt *testing.T, deltaTimeOffset *hexutil.Uint64) startStatus := sequencer.SyncStatus() require.Less(t, startStatus.SafeL2.Number, startStatus.UnsafeL2.Number, "sequencer has unsafe L2 block") - batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, sequencer.RollupClient(), miner.EthClient(), engine.EthClient(), engine.EngineClient(t, sd.RollupCfg)) + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + sequencer.RollupClient(), miner.EthClient(), engine.EthClient(), engine.EngineClient(t, sd.RollupCfg)) batcher.ActSubmitAll(t) // include in L1 @@ -325,13 +317,9 @@ func GarbageBatch(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { log := testlog.Logger(t, log.LvlError) miner, engine, sequencer := setupSequencerTest(t, sd, log) - _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) - batcherCfg := &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - } + batcherCfg := DefaultBatcherCfg(dp) if garbageKind == MALFORM_RLP || garbageKind == INVALID_COMPRESSION { // If the garbage kind is `INVALID_COMPRESSION` or `MALFORM_RLP`, use the `actions` packages @@ -409,13 +397,10 @@ func ExtendedTimeWithoutL1Batches(gt *testing.T, deltaTimeOffset *hexutil.Uint64 log := testlog.Logger(t, log.LvlError) miner, engine, sequencer := setupSequencerTest(t, sd, log) - _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) - batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, sequencer.RollupClient(), miner.EthClient(), engine.EthClient(), engine.EngineClient(t, sd.RollupCfg)) + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + sequencer.RollupClient(), miner.EthClient(), engine.EthClient(), engine.EngineClient(t, sd.RollupCfg)) sequencer.ActL2PipelineFull(t) verifier.ActL2PipelineFull(t) @@ -468,12 +453,13 @@ func BigL2Txs(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { log := testlog.Logger(t, log.LvlInfo) miner, engine, sequencer := setupSequencerTest(t, sd, log) - _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 40_000, // try a small batch size, to force the data to be split between more frames - BatcherKey: dp.Secrets.Batcher, + MinL1TxSize: 0, + MaxL1TxSize: 40_000, // try a small batch size, to force the data to be split between more frames + BatcherKey: dp.Secrets.Batcher, + DataAvailabilityType: batcherFlags.CalldataType, }, sequencer.RollupClient(), miner.EthClient(), engine.EthClient(), engine.EngineClient(t, sd.RollupCfg)) sequencer.ActL2PipelineFull(t) diff --git a/op-e2e/actions/l2_proposer_test.go b/op-e2e/actions/l2_proposer_test.go index 1c346f020680..003996020a73 100644 --- a/op-e2e/actions/l2_proposer_test.go +++ b/op-e2e/actions/l2_proposer_test.go @@ -48,11 +48,8 @@ func RunProposerTest(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) rollupSeqCl := sequencer.RollupClient() - batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) proposer := NewL2Proposer(t, log, &ProposerCfg{ OutputOracleAddr: &sd.DeploymentsL1.L2OutputOracleProxy, diff --git a/op-e2e/actions/l2_sequencer.go b/op-e2e/actions/l2_sequencer.go index f479a46ce337..ead864171acf 100644 --- a/op-e2e/actions/l2_sequencer.go +++ b/op-e2e/actions/l2_sequencer.go @@ -30,18 +30,6 @@ func (m *MockL1OriginSelector) FindL1Origin(ctx context.Context, l2Head eth.L2Bl return m.actual.FindL1Origin(ctx, l2Head) } -// emptyL1BlobsFetcher is a no-op blobs provider. The actions test batcher currently only supports using calldata. -type emptyL1BlobsFetcher struct { - t Testing -} - -var _ derive.L1BlobsFetcher = &emptyL1BlobsFetcher{} - -func (e *emptyL1BlobsFetcher) GetBlobs(ctx context.Context, ref eth.L1BlockRef, hashes []eth.IndexedBlobHash) ([]*eth.Blob, error) { - e.t.Fatal("actions test do not support blobs") - return nil, nil -} - // L2Sequencer is an actor that functions like a rollup node, // without the full P2P/API/Node stack, but just the derivation state, and simplified driver with sequencing ability. type L2Sequencer struct { @@ -54,9 +42,9 @@ type L2Sequencer struct { mockL1OriginSelector *MockL1OriginSelector } -func NewL2Sequencer(t Testing, log log.Logger, l1 derive.L1Fetcher, eng L2API, cfg *rollup.Config, seqConfDepth uint64) *L2Sequencer { - mockBlobFetcher := &emptyL1BlobsFetcher{t: t} - ver := NewL2Verifier(t, log, l1, mockBlobFetcher, eng, cfg, &sync.Config{}) +func NewL2Sequencer(t Testing, log log.Logger, l1 derive.L1Fetcher, blobSrc derive.L1BlobsFetcher, + eng L2API, cfg *rollup.Config, seqConfDepth uint64) *L2Sequencer { + ver := NewL2Verifier(t, log, l1, blobSrc, eng, cfg, &sync.Config{}) attrBuilder := derive.NewFetchingAttributesBuilder(cfg, l1, eng) seqConfDepthL1 := driver.NewConfDepth(seqConfDepth, ver.l1State.L1Head, l1) l1OriginSelector := &MockL1OriginSelector{ diff --git a/op-e2e/actions/l2_sequencer_test.go b/op-e2e/actions/l2_sequencer_test.go index 2bfd8d6cb0dc..60086a97ebd9 100644 --- a/op-e2e/actions/l2_sequencer_test.go +++ b/op-e2e/actions/l2_sequencer_test.go @@ -47,7 +47,7 @@ func setupSequencerTest(t Testing, sd *e2eutils.SetupData, log log.Logger) (*L1M l2Cl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) require.NoError(t, err) - sequencer := NewL2Sequencer(t, log, l1F, l2Cl, sd.RollupCfg, 0) + sequencer := NewL2Sequencer(t, log, l1F, miner.BlobStore(), l2Cl, sd.RollupCfg, 0) return miner, engine, sequencer } diff --git a/op-e2e/actions/l2_verifier_test.go b/op-e2e/actions/l2_verifier_test.go index 4150c984f695..29f980ae15be 100644 --- a/op-e2e/actions/l2_verifier_test.go +++ b/op-e2e/actions/l2_verifier_test.go @@ -13,19 +13,18 @@ import ( "github.com/ethereum-optimism/optimism/op-service/testlog" ) -func setupVerifier(t Testing, sd *e2eutils.SetupData, log log.Logger, l1F derive.L1Fetcher, syncCfg *sync.Config) (*L2Engine, *L2Verifier) { +func setupVerifier(t Testing, sd *e2eutils.SetupData, log log.Logger, l1F derive.L1Fetcher, blobSrc derive.L1BlobsFetcher, syncCfg *sync.Config) (*L2Engine, *L2Verifier) { jwtPath := e2eutils.WriteDefaultJWT(t) engine := NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath, EngineWithP2P()) engCl := engine.EngineClient(t, sd.RollupCfg) - mockBlobFetcher := &emptyL1BlobsFetcher{t: t} - verifier := NewL2Verifier(t, log, l1F, mockBlobFetcher, engCl, sd.RollupCfg, syncCfg) + verifier := NewL2Verifier(t, log, l1F, blobSrc, engCl, sd.RollupCfg, syncCfg) return engine, verifier } func setupVerifierOnlyTest(t Testing, sd *e2eutils.SetupData, log log.Logger) (*L1Miner, *L2Engine, *L2Verifier) { miner := NewL1Miner(t, log, sd.L1Cfg) l1Cl := miner.L1Client(t, sd.RollupCfg) - engine, verifier := setupVerifier(t, sd, log, l1Cl, &sync.Config{}) + engine, verifier := setupVerifier(t, sd, log, l1Cl, miner.BlobStore(), &sync.Config{}) return miner, engine, verifier } diff --git a/op-e2e/actions/reorg_test.go b/op-e2e/actions/reorg_test.go index 25804e59f0fc..3339cb53ac68 100644 --- a/op-e2e/actions/reorg_test.go +++ b/op-e2e/actions/reorg_test.go @@ -36,13 +36,10 @@ func setupReorgTestActors(t Testing, dp *e2eutils.DeployParams, sd *e2eutils.Set miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) miner.ActL1SetFeeRecipient(common.Address{'A'}) sequencer.ActL2PipelineFull(t) - verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) rollupSeqCl := sequencer.RollupClient() - batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) return sd, dp, miner, sequencer, seqEngine, verifier, verifEngine, batcher } @@ -620,13 +617,10 @@ func RestartOpGeth(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { engRpc := &rpcWrapper{seqEng.RPCClient()} l2Cl, err := sources.NewEngineClient(engRpc, log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) require.NoError(t, err) - sequencer := NewL2Sequencer(t, log, l1F, l2Cl, sd.RollupCfg, 0) + sequencer := NewL2Sequencer(t, log, l1F, miner.BlobStore(), l2Cl, sd.RollupCfg, 0) - batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, sequencer.RollupClient(), miner.EthClient(), seqEng.EthClient(), seqEng.EngineClient(t, sd.RollupCfg)) + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + sequencer.RollupClient(), miner.EthClient(), seqEng.EthClient(), seqEng.EngineClient(t, sd.RollupCfg)) // start sequencer.ActL2PipelineFull(t) @@ -711,12 +705,9 @@ func ConflictingL2Blocks(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { require.NoError(t, err) l1F, err := sources.NewL1Client(miner.RPCClient(), log, nil, sources.L1ClientDefaultConfig(sd.RollupCfg, false, sources.RPCKindStandard)) require.NoError(t, err) - altSequencer := NewL2Sequencer(t, log, l1F, altSeqEngCl, sd.RollupCfg, 0) - altBatcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, altSequencer.RollupClient(), miner.EthClient(), altSeqEng.EthClient(), altSeqEng.EngineClient(t, sd.RollupCfg)) + altSequencer := NewL2Sequencer(t, log, l1F, miner.BlobStore(), altSeqEngCl, sd.RollupCfg, 0) + altBatcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + altSequencer.RollupClient(), miner.EthClient(), altSeqEng.EthClient(), altSeqEng.EngineClient(t, sd.RollupCfg)) // And set up user Alice, using the alternative sequencer endpoint l2Cl := altSeqEng.EthClient() diff --git a/op-e2e/actions/span_batch_test.go b/op-e2e/actions/span_batch_test.go index 07bdd91ee3b9..001634fe3464 100644 --- a/op-e2e/actions/span_batch_test.go +++ b/op-e2e/actions/span_batch_test.go @@ -8,9 +8,8 @@ import ( "math/rand" "testing" - "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" - "github.com/ethereum-optimism/optimism/op-node/rollup/sync" - "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/stretchr/testify/require" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" @@ -20,7 +19,11 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "github.com/stretchr/testify/require" + + batcherFlags "github.com/ethereum-optimism/optimism/op-batcher/flags" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" + "github.com/ethereum-optimism/optimism/op-node/rollup/sync" + "github.com/ethereum-optimism/optimism/op-service/testlog" ) // TestDropSpanBatchBeforeHardfork tests behavior of op-node before Delta hardfork. @@ -39,7 +42,7 @@ func TestDropSpanBatchBeforeHardfork(gt *testing.T) { sd := e2eutils.Setup(t, dp, defaultAlloc) log := testlog.Logger(t, log.LvlError) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) - verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) rollupSeqCl := sequencer.RollupClient() // Force batcher to submit SpanBatches to L1. @@ -48,6 +51,7 @@ func TestDropSpanBatchBeforeHardfork(gt *testing.T) { MaxL1TxSize: 128_000, BatcherKey: dp.Secrets.Batcher, ForceSubmitSpanBatch: true, + DataAvailabilityType: batcherFlags.CalldataType, }, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) // Alice makes a L2 tx @@ -129,7 +133,7 @@ func TestHardforkMiddleOfSpanBatch(gt *testing.T) { sd := e2eutils.Setup(t, dp, defaultAlloc) log := testlog.Logger(t, log.LvlError) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) - verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) minerCl := miner.EthClient() rollupSeqCl := sequencer.RollupClient() @@ -139,6 +143,7 @@ func TestHardforkMiddleOfSpanBatch(gt *testing.T) { MaxL1TxSize: 128_000, BatcherKey: dp.Secrets.Batcher, ForceSubmitSpanBatch: true, + DataAvailabilityType: batcherFlags.CalldataType, }, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) // Alice makes a L2 tx @@ -236,7 +241,7 @@ func TestAcceptSingularBatchAfterHardfork(gt *testing.T) { sd := e2eutils.Setup(t, dp, defaultAlloc) log := testlog.Logger(t, log.LvlError) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) - verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) rollupSeqCl := sequencer.RollupClient() // Force batcher to submit SingularBatches to L1. @@ -245,6 +250,7 @@ func TestAcceptSingularBatchAfterHardfork(gt *testing.T) { MaxL1TxSize: 128_000, BatcherKey: dp.Secrets.Batcher, ForceSubmitSingularBatch: true, + DataAvailabilityType: batcherFlags.CalldataType, }, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) // Alice makes a L2 tx @@ -321,7 +327,7 @@ func TestMixOfBatchesAfterHardfork(gt *testing.T) { sd := e2eutils.Setup(t, dp, defaultAlloc) log := testlog.Logger(t, log.LvlError) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) - verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) rollupSeqCl := sequencer.RollupClient() seqEngCl := seqEngine.EthClient() @@ -361,6 +367,7 @@ func TestMixOfBatchesAfterHardfork(gt *testing.T) { BatcherKey: dp.Secrets.Batcher, ForceSubmitSpanBatch: i%2 == 0, // Submit SpanBatch for odd numbered batches ForceSubmitSingularBatch: i%2 == 1, // Submit SingularBatch for even numbered batches + DataAvailabilityType: batcherFlags.CalldataType, } batcher := NewL2Batcher(log, sd.RollupCfg, &batcherCfg, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) // Submit all new blocks @@ -410,14 +417,11 @@ func TestSpanBatchEmptyChain(gt *testing.T) { sd := e2eutils.Setup(t, dp, defaultAlloc) log := testlog.Logger(t, log.LvlError) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) - _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) rollupSeqCl := sequencer.RollupClient() - batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) sequencer.ActL2PipelineFull(t) verifier.ActL2PipelineFull(t) @@ -476,14 +480,11 @@ func TestSpanBatchLowThroughputChain(gt *testing.T) { sd := e2eutils.Setup(t, dp, defaultAlloc) log := testlog.Logger(t, log.LvlError) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) - _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) rollupSeqCl := sequencer.RollupClient() - batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) cl := seqEngine.EthClient() const numTestUsers = 5 @@ -604,10 +605,10 @@ func TestBatchEquivalence(gt *testing.T) { seqEngCl := seqEngine.EthClient() // Setup Delta activated spanVerifier - _, spanVerifier := setupVerifier(t, sdDeltaActivated, log, miner.L1Client(t, sdDeltaActivated.RollupCfg), &sync.Config{}) + _, spanVerifier := setupVerifier(t, sdDeltaActivated, log, miner.L1Client(t, sdDeltaActivated.RollupCfg), miner.BlobStore(), &sync.Config{}) // Setup Delta deactivated spanVerifier - _, singularVerifier := setupVerifier(t, sdDeltaDeactivated, log, miner.L1Client(t, sdDeltaDeactivated.RollupCfg), &sync.Config{}) + _, singularVerifier := setupVerifier(t, sdDeltaDeactivated, log, miner.L1Client(t, sdDeltaDeactivated.RollupCfg), miner.BlobStore(), &sync.Config{}) // Setup SpanBatcher spanBatcher := NewL2Batcher(log, sdDeltaActivated.RollupCfg, &BatcherCfg{ @@ -615,6 +616,7 @@ func TestBatchEquivalence(gt *testing.T) { MaxL1TxSize: 128_000, BatcherKey: dp.Secrets.Batcher, ForceSubmitSpanBatch: true, + DataAvailabilityType: batcherFlags.CalldataType, }, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sdDeltaActivated.RollupCfg)) // Setup SingularBatcher @@ -623,6 +625,7 @@ func TestBatchEquivalence(gt *testing.T) { MaxL1TxSize: 128_000, BatcherKey: dp.Secrets.Batcher, ForceSubmitSingularBatch: true, + DataAvailabilityType: batcherFlags.CalldataType, }, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sdDeltaDeactivated.RollupCfg)) const numTestUsers = 5 diff --git a/op-e2e/actions/sync_test.go b/op-e2e/actions/sync_test.go index efecb376ca51..9aca9407aee2 100644 --- a/op-e2e/actions/sync_test.go +++ b/op-e2e/actions/sync_test.go @@ -172,7 +172,7 @@ func TestELSync(gt *testing.T) { miner, seqEng, sequencer := setupSequencerTest(t, sd, log) // Enable engine P2P sync - verEng, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{SyncMode: sync.ELSync}) + verEng, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{SyncMode: sync.ELSync}) seqEng.AddPeers(verEng.Enode()) verEng.AddPeers(seqEng.Enode()) diff --git a/op-e2e/actions/system_config_test.go b/op-e2e/actions/system_config_test.go index f796c9ea6cc2..43abb2f8ee46 100644 --- a/op-e2e/actions/system_config_test.go +++ b/op-e2e/actions/system_config_test.go @@ -59,22 +59,18 @@ func BatcherKeyRotation(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) miner.ActL1SetFeeRecipient(common.Address{'A'}) sequencer.ActL2PipelineFull(t) - _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) rollupSeqCl := sequencer.RollupClient() // the default batcher - batcherA := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) + batcherA := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) // a batcher with a new key - batcherB := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Bob, - }, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) + altCfg := *DefaultBatcherCfg(dp) + altCfg.BatcherKey = dp.Secrets.Bob + batcherB := NewL2Batcher(log, sd.RollupCfg, &altCfg, + rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) sequencer.ActL2PipelineFull(t) verifier.ActL2PipelineFull(t) @@ -235,11 +231,8 @@ func GPOParamsChange(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { sd := e2eutils.Setup(t, dp, defaultAlloc) log := testlog.Logger(t, log.LvlDebug) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) - batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, sequencer.RollupClient(), miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + sequencer.RollupClient(), miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) alice := NewBasicUser[any](log, dp.Secrets.Alice, rand.New(rand.NewSource(1234))) alice.SetUserEnv(&BasicUserEnv[any]{ @@ -365,11 +358,8 @@ func GasLimitChange(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { sd := e2eutils.Setup(t, dp, defaultAlloc) log := testlog.Logger(t, log.LvlDebug) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) - batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, sequencer.RollupClient(), miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + sequencer.RollupClient(), miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) sequencer.ActL2PipelineFull(t) miner.ActEmptyBlock(t) @@ -412,7 +402,7 @@ func GasLimitChange(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { miner.ActL1IncludeTx(dp.Addresses.Batcher)(t) miner.ActL1EndBlock(t) - _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), &sync.Config{}) + _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) verifier.ActL2PipelineFull(t) require.Equal(t, sequencer.L2Unsafe(), verifier.L2Safe(), "verifier stays in sync, even with gaslimit changes") diff --git a/op-e2e/actions/user_test.go b/op-e2e/actions/user_test.go index bed5466baa28..02a40ee7a1ca 100644 --- a/op-e2e/actions/user_test.go +++ b/op-e2e/actions/user_test.go @@ -129,11 +129,8 @@ func runCrossLayerUserTest(gt *testing.T, test hardforkScheduledTest) { require.Equal(t, dp.Secrets.Addresses().Proposer, dp.DeployConfig.L2OutputOracleProposer) miner, seqEngine, seq := setupSequencerTest(t, sd, log) - batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ - MinL1TxSize: 0, - MaxL1TxSize: 128_000, - BatcherKey: dp.Secrets.Batcher, - }, seq.RollupClient(), miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), + seq.RollupClient(), miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) proposer := NewL2Proposer(t, log, &ProposerCfg{ OutputOracleAddr: &sd.DeploymentsL1.L2OutputOracleProxy, ProposerKey: dp.Secrets.Proposer, diff --git a/op-e2e/e2eutils/blobs.go b/op-e2e/e2eutils/blobs.go new file mode 100644 index 000000000000..730aae1d94c5 --- /dev/null +++ b/op-e2e/e2eutils/blobs.go @@ -0,0 +1,49 @@ +package e2eutils + +import ( + "context" + "fmt" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-service/eth" +) + +// BlobsStore is a simple in-memory store of blobs, for testing purposes +type BlobsStore struct { + // blockhash -> blob versioned hash -> blob + blobs map[common.Hash]map[common.Hash]*eth.Blob +} + +func NewBlobStore() *BlobsStore { + return &BlobsStore{blobs: make(map[common.Hash]map[common.Hash]*eth.Blob)} +} + +func (store *BlobsStore) StoreBlob(blockHash common.Hash, versionedHash common.Hash, blob *eth.Blob) { + m, ok := store.blobs[blockHash] + if !ok { + m = make(map[common.Hash]*eth.Blob) + store.blobs[blockHash] = m + } + m[versionedHash] = blob +} + +func (store *BlobsStore) GetBlobs(ctx context.Context, ref eth.L1BlockRef, hashes []eth.IndexedBlobHash) ([]*eth.Blob, error) { + out := make([]*eth.Blob, 0, len(hashes)) + m, ok := store.blobs[ref.Hash] + if !ok { + return nil, fmt.Errorf("no blobs known with given time: %w", ethereum.NotFound) + } + for _, h := range hashes { + b, ok := m[h.Hash] + if !ok { + return nil, fmt.Errorf("blob %d %s is not in store: %w", h.Index, h.Hash, ethereum.NotFound) + } + out = append(out, b) + } + return out, nil +} + +var _ derive.L1BlobsFetcher = (*BlobsStore)(nil) diff --git a/op-service/txmgr/txmgr.go b/op-service/txmgr/txmgr.go index 195ab14b9041..bd4bddcaa5f0 100644 --- a/op-service/txmgr/txmgr.go +++ b/op-service/txmgr/txmgr.go @@ -277,7 +277,7 @@ func (m *SimpleTxManager) craftTx(ctx context.Context, candidate TxCandidate) (* if candidate.To == nil { return nil, errors.New("blob txs cannot deploy contracts") } - if sidecar, blobHashes, err = makeSidecar(candidate.Blobs); err != nil { + if sidecar, blobHashes, err = MakeSidecar(candidate.Blobs); err != nil { return nil, fmt.Errorf("failed to make sidecar: %w", err) } } @@ -314,9 +314,9 @@ func (m *SimpleTxManager) craftTx(ctx context.Context, candidate TxCandidate) (* } -// makeSidecar builds & returns the BlobTxSidecar and corresponding blob hashes from the raw blob +// MakeSidecar builds & returns the BlobTxSidecar and corresponding blob hashes from the raw blob // data. -func makeSidecar(blobs []*eth.Blob) (*types.BlobTxSidecar, []common.Hash, error) { +func MakeSidecar(blobs []*eth.Blob) (*types.BlobTxSidecar, []common.Hash, error) { sidecar := &types.BlobTxSidecar{} blobHashes := []common.Hash{} for i, blob := range blobs { diff --git a/op-service/txmgr/txmgr_test.go b/op-service/txmgr/txmgr_test.go index b316acc4fc32..2b7c51bf0681 100644 --- a/op-service/txmgr/txmgr_test.go +++ b/op-service/txmgr/txmgr_test.go @@ -2,6 +2,7 @@ package txmgr import ( "context" + "crypto/rand" "errors" "fmt" "math/big" @@ -1403,3 +1404,24 @@ func TestCloseWaitingForConfirmation(t *testing.T) { require.True(t, h.mgr.closed.Load()) require.NoError(t, err) } + +func TestMakeSidecar(t *testing.T) { + var blob eth.Blob + _, err := rand.Read(blob[:]) + require.NoError(t, err) + // get the field-elements into a valid range + for i := 0; i < 4096; i++ { + blob[32*i] &= 0b0011_1111 + } + sidecar, hashes, err := MakeSidecar([]*eth.Blob{&blob}) + require.NoError(t, err) + require.Equal(t, len(hashes), 1) + require.Equal(t, len(sidecar.Blobs), len(hashes)) + require.Equal(t, len(sidecar.Proofs), len(hashes)) + require.Equal(t, len(sidecar.Commitments), len(hashes)) + + for i, commit := range sidecar.Commitments { + require.NoError(t, eth.VerifyBlobProof((*eth.Blob)(&sidecar.Blobs[i]), commit, sidecar.Proofs[i]), "proof must be valid") + require.Equal(t, hashes[i], eth.KZGToVersionedHash(commit)) + } +}