diff --git a/op-e2e/actions/dencun_fork_test.go b/op-e2e/actions/dencun_fork_test.go new file mode 100644 index 000000000000..7b01a92a5210 --- /dev/null +++ b/op-e2e/actions/dencun_fork_test.go @@ -0,0 +1,53 @@ +package actions + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" + "github.com/ethereum-optimism/optimism/op-service/testlog" +) + +func TestDencunL1Fork(gt *testing.T) { + t := NewDefaultTesting(gt) + dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) + sd := e2eutils.Setup(t, dp, defaultAlloc) + activation := sd.L1Cfg.Timestamp + 24 + sd.L1Cfg.Config.CancunTime = &activation + log := testlog.Logger(t, log.LvlDebug) + _, _, miner, sequencer, _, verifier, _, batcher := setupReorgTestActors(t, dp, sd, log) + + l1Head := miner.l1Chain.CurrentBlock() + require.False(t, sd.L1Cfg.Config.IsCancun(l1Head.Number, l1Head.Time), "Cancun not active yet") + + // start op-nodes + sequencer.ActL2PipelineFull(t) + verifier.ActL2PipelineFull(t) + + // build empty L1 blocks, crossing the fork boundary + miner.ActL1SetFeeRecipient(common.Address{'A', 0}) + miner.ActEmptyBlock(t) + miner.ActEmptyBlock(t) // Cancun activates here + miner.ActEmptyBlock(t) + // verify Cancun is active + l1Head = miner.l1Chain.CurrentBlock() + require.True(t, sd.L1Cfg.Config.IsCancun(l1Head.Number, l1Head.Time), "Cancun active") + + // build L2 chain up to and including L2 blocks referencing Cancun L1 blocks + sequencer.ActL1HeadSignal(t) + sequencer.ActBuildToL1Head(t) + miner.ActL1StartBlock(12)(t) + batcher.ActSubmitAll(t) + miner.ActL1IncludeTx(batcher.batcherAddr)(t) + miner.ActL1EndBlock(t) + + // sync verifier + verifier.ActL1HeadSignal(t) + verifier.ActL2PipelineFull(t) + // verify verifier accepted Cancun L1 inputs + require.Equal(t, l1Head.Hash(), verifier.SyncStatus().SafeL2.L1Origin.Hash, "verifier synced L1 chain that includes Cancun headers") + require.Equal(t, sequencer.SyncStatus().UnsafeL2, verifier.SyncStatus().UnsafeL2, "verifier and sequencer agree") +} diff --git a/op-e2e/actions/l1_miner.go b/op-e2e/actions/l1_miner.go index 8ba3ee857b67..56f508d095d2 100644 --- a/op-e2e/actions/l1_miner.go +++ b/op-e2e/actions/l1_miner.go @@ -76,6 +76,13 @@ func (s *L1Miner) ActL1StartBlock(timeDelta uint64) Action { if s.l1Cfg.Config.IsShanghai(header.Number, header.Time) { 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 + } s.l1Building = true s.l1BuildingHeader = header diff --git a/op-service/sources/types.go b/op-service/sources/types.go index 7ccf4c3b65c5..90c0002018c3 100644 --- a/op-service/sources/types.go +++ b/op-service/sources/types.go @@ -103,7 +103,16 @@ type rpcHeader struct { BaseFee *hexutil.Big `json:"baseFeePerGas"` // WithdrawalsRoot was added by EIP-4895 and is ignored in legacy headers. - WithdrawalsRoot *common.Hash `json:"withdrawalsRoot"` + WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"` + + // BlobGasUsed was added by EIP-4844 and is ignored in legacy headers. + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed,omitempty"` + + // ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers. + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas,omitempty"` + + // ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers. + ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot,omitempty"` // untrusted info included by RPC, may have to be checked Hash common.Hash `json:"hash"` @@ -156,6 +165,10 @@ func (hdr *rpcHeader) createGethHeader() *types.Header { Nonce: hdr.Nonce, BaseFee: (*big.Int)(hdr.BaseFee), WithdrawalsHash: hdr.WithdrawalsRoot, + // Cancun + BlobGasUsed: (*uint64)(hdr.BlobGasUsed), + ExcessBlobGas: (*uint64)(hdr.ExcessBlobGas), + ParentBeaconRoot: hdr.ParentBeaconRoot, } } @@ -185,7 +198,7 @@ func (block *rpcBlock) verify() error { } for i, tx := range block.Transactions { if tx == nil { - return fmt.Errorf("block tx %d is null", i) + return fmt.Errorf("block tx %d is nil", i) } } if computed := types.DeriveSha(types.Transactions(block.Transactions), trie.NewStackTrie(nil)); block.TxHash != computed {