From f4cf9c6dbd2b0d4f74014165bf92b74f5643aebd Mon Sep 17 00:00:00 2001 From: galaio <12880651+galaio@users.noreply.github.com> Date: Wed, 24 Jul 2024 15:58:56 +0800 Subject: [PATCH] txdag: support write & read TxDAG from file; (#9) txdag: record txdag metrics; txdag: opt txdag flag name; Co-authored-by: galaio --- cmd/geth/main.go | 2 + cmd/utils/flags.go | 21 ++++++ core/blockchain.go | 123 ++++++++++++++++++++++++++++--- core/blockchain_insert.go | 2 +- core/blockchain_test.go | 32 ++++++++ core/parallel_state_processor.go | 30 ++++++-- core/state/statedb.go | 11 +++ core/state_processor.go | 26 ++++--- core/types/dag.go | 5 +- core/types/dag_test.go | 3 +- core/types/mvstates.go | 5 +- eth/backend.go | 3 + eth/ethconfig/config.go | 2 + miner/miner.go | 3 +- miner/worker.go | 14 ++-- 15 files changed, 242 insertions(+), 40 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 74158321bc..5f786bbcee 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -169,6 +169,8 @@ var ( utils.RollupSuperchainUpgradesFlag, utils.ParallelTxFlag, utils.ParallelTxNumFlag, + utils.ParallelTxDAGFlag, + utils.ParallelTxDAGFileFlag, configFileFlag, utils.LogDebugFlag, utils.LogBacktraceAtFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index a9bc65bd18..06761c34a6 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1095,6 +1095,19 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server. Category: flags.VMCategory, } + ParallelTxDAGFlag = &cli.BoolFlag{ + Name: "parallel.txdag", + Usage: "Enable the experimental parallel TxDAG generation, only valid in full sync mode (default = false)", + Category: flags.VMCategory, + } + + ParallelTxDAGFileFlag = &cli.StringFlag{ + Name: "parallel.txdagfile", + Usage: "It indicates the TxDAG file path", + Value: "./parallel-txdag-output.csv", + Category: flags.VMCategory, + } + VMOpcodeOptimizeFlag = &cli.BoolFlag{ Name: "vm.opcode.optimize", Usage: "enable opcode optimization", @@ -2005,6 +2018,14 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.ParallelTxNum = parallelNum } + if ctx.IsSet(ParallelTxDAGFlag.Name) { + cfg.EnableParallelTxDAG = ctx.Bool(ParallelTxDAGFlag.Name) + } + + if ctx.IsSet(ParallelTxDAGFileFlag.Name) { + cfg.ParallelTxDAGFile = ctx.String(ParallelTxDAGFileFlag.Name) + } + if ctx.IsSet(VMOpcodeOptimizeFlag.Name) { cfg.EnableOpcodeOptimizing = ctx.Bool(VMOpcodeOptimizeFlag.Name) if cfg.EnableOpcodeOptimizing { diff --git a/core/blockchain.go b/core/blockchain.go index 53f9d4d531..ecdb3044a9 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -18,11 +18,16 @@ package core import ( + "bufio" + "bytes" + "encoding/hex" "errors" "fmt" "io" "math/big" + "os" "runtime" + "strconv" "strings" "sync" "sync/atomic" @@ -92,6 +97,9 @@ var ( triedbCommitExternalTimer = metrics.NewRegisteredTimer("chain/triedb/commit/external", nil) innerExecutionTimer = metrics.NewRegisteredTimer("chain/inner/execution", nil) + txDAGGenerateTimer = metrics.NewRegisteredTimer("chain/block/txdag/gen", nil) + txDAGDispatchTimer = metrics.NewRegisteredTimer("chain/block/txdag/dispatch", nil) + blockGasUsedGauge = metrics.NewRegisteredGauge("chain/block/gas/used", nil) mgaspsGauge = metrics.NewRegisteredGauge("chain/mgas/ps", nil) @@ -297,6 +305,9 @@ type BlockChain struct { forker *ForkChoice vmConfig vm.Config parallelExecution bool + enableTxDAG bool + txDAGWriteCh chan TxDAGOutputItem + txDAGMapping map[uint64]types.TxDAG } // NewBlockChain returns a fully initialised block chain using information @@ -1924,16 +1935,16 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) return it.index, err } - // TODO(galaio): use txDAG in some accelerate scenarios. - if len(block.TxDAG()) > 0 { - txDAG, err := types.DecodeTxDAG(block.TxDAG()) - if err != nil { - return it.index, err - } - log.Info("Insert chain", "block", block.NumberU64(), "txDAG", txDAG.Type()) - } + // TODO(galaio): use txDAG in some accelerate scenarios, like state pre-fetcher. + //if bc.enableTxDAG && len(block.TxDAG()) > 0 { + // txDAG, err := types.DecodeTxDAG(block.TxDAG()) + // if err != nil { + // return it.index, err + // } + // log.Info("Insert chain", "block", block.NumberU64(), "txDAG", txDAG) + //} // TODO(galaio): need hardfork - if bc.chainConfig.Optimism != nil && len(block.Header().Extra) > 0 { + if bc.enableTxDAG && bc.chainConfig.Optimism != nil && len(block.Header().Extra) > 0 { txDAG, err := types.DecodeTxDAG(block.Header().Extra) if err != nil { return it.index, err @@ -1995,8 +2006,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation) accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation) storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation) - blockExecutionTimer.Update(ptime) // The time spent on block execution - blockValidationTimer.Update(vtime) // The time spent on block validation + txDAGGenerateTimer.Update(statedb.TxDAGGenerate) + blockExecutionTimer.Update(ptime) // The time spent on block execution + blockValidationTimer.Update(vtime) // The time spent on block validation innerExecutionTimer.Update(DebugInnerExecutionDuration) @@ -2820,3 +2832,92 @@ func createDelFn(bc *BlockChain) func(db ethdb.KeyValueWriter, hash common.Hash, func (bc *BlockChain) HeaderChainForceSetHead(headNumber uint64) { bc.hc.SetHead(headNumber, nil, createDelFn(bc)) } + +func (bc *BlockChain) TxDAGEnabled() bool { + return bc.enableTxDAG +} + +func (bc *BlockChain) EnableTxDAGGeneration(output string) { + bc.enableTxDAG = true + if len(output) == 0 { + return + } + // read TxDAG file, and cache in mem + var err error + bc.txDAGMapping, err = readTxDAGMappingFromFile(output) + if err != nil { + log.Error("read TxDAG err", err) + } + + // write handler + bc.txDAGWriteCh = make(chan TxDAGOutputItem, 10000) + go func() { + writeHandle, err := os.OpenFile(output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) + if err != nil { + log.Error("OpenFile when open the txDAG output file", "file", output) + return + } + defer writeHandle.Close() + for { + select { + case <-bc.quit: + return + case item := <-bc.txDAGWriteCh: + if err := writeTxDAGToFile(writeHandle, item); err != nil { + log.Error("encode TxDAG err in OutputHandler", "err", err) + continue + } + } + } + }() +} + +type TxDAGOutputItem struct { + blockNumber uint64 + txDAG types.TxDAG +} + +func writeTxDAGToFile(writeHandle *os.File, item TxDAGOutputItem) error { + var buf bytes.Buffer + buf.WriteString(strconv.FormatUint(item.blockNumber, 10)) + buf.WriteByte(',') + enc, err := types.EncodeTxDAG(item.txDAG) + if err != nil { + return err + } + buf.WriteString(hex.EncodeToString(enc)) + buf.WriteByte('\n') + _, err = writeHandle.Write(buf.Bytes()) + return err +} + +func readTxDAGMappingFromFile(output string) (map[uint64]types.TxDAG, error) { + file, err := os.Open(output) + if err != nil { + return nil, err + } + defer file.Close() + + mapping := make(map[uint64]types.TxDAG) + scanner := bufio.NewScanner(file) + for scanner.Scan() { + tokens := strings.Split(scanner.Text(), ",") + if len(tokens) != 2 { + return nil, errors.New("txDAG output contain wrong size") + } + num, err := strconv.Atoi(tokens[0]) + if err != nil { + return nil, err + } + enc, err := hex.DecodeString(tokens[1]) + if err != nil { + return nil, err + } + txDAG, err := types.DecodeTxDAG(enc) + if err != nil { + return nil, err + } + mapping[uint64(num)] = txDAG + } + return mapping, nil +} diff --git a/core/blockchain_insert.go b/core/blockchain_insert.go index 82a480d0be..2667323c1e 100644 --- a/core/blockchain_insert.go +++ b/core/blockchain_insert.go @@ -60,7 +60,7 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn "blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000, "elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed), } - mgaspsGauge.Update(int64(st.usedGas)*1000/int64(elapsed)) + mgaspsGauge.Update(int64(st.usedGas) * 1000 / int64(elapsed)) if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute { context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...) } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 62466c26b4..d99e92d8b7 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -22,10 +22,13 @@ import ( "math/big" "math/rand" "os" + "path/filepath" "sync" "testing" "time" + "github.com/stretchr/testify/require" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus" @@ -4716,3 +4719,32 @@ func TestEIP3651(t *testing.T) { t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) } } + +func TestTxDAGFile_ReadWrite(t *testing.T) { + path := filepath.Join(os.TempDir(), "test.csv") + except := map[uint64]types.TxDAG{ + 0: types.NewEmptyTxDAG(), + 1: makeEmptyPlainTxDAG(1), + 2: makeEmptyPlainTxDAG(2), + } + writeFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) + require.NoError(t, err) + for num, dag := range except { + require.NoError(t, writeTxDAGToFile(writeFile, TxDAGOutputItem{blockNumber: num, txDAG: dag})) + } + writeFile.Close() + + actual, err := readTxDAGMappingFromFile(path) + require.NoError(t, err) + for num, dag := range except { + require.Equal(t, dag, actual[num]) + } +} + +func makeEmptyPlainTxDAG(cnt int) *types.PlainTxDAG { + dag := types.NewPlainTxDAG(cnt) + for i := range dag.TxDeps { + dag.TxDeps[i].TxIndexes = make([]uint64, 0) + } + return dag +} diff --git a/core/parallel_state_processor.go b/core/parallel_state_processor.go index 9fe363f151..7cbfe4cad9 100644 --- a/core/parallel_state_processor.go +++ b/core/parallel_state_processor.go @@ -3,6 +3,11 @@ package core import ( "errors" "fmt" + "runtime" + "sync" + "sync/atomic" + "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" @@ -11,10 +16,8 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" - "runtime" - "sync" - "sync/atomic" ) const ( @@ -189,6 +192,11 @@ func (p *ParallelStateProcessor) doStaticDispatchV2(txReqs []*ParallelTxRequest, return } + if metrics.EnabledExpensive { + defer func(start time.Time) { + txDAGDispatchTimer.Update(time.Since(start)) + }(time.Now()) + } // resolve isolate execution paths from TxDAG, it indicates the tx dispatch paths := types.MergeTxDAGExecutionPaths(txDAG) log.Info("doStaticDispatchV2 merge parallel execution paths", "slots", len(p.slotState), "paths", len(paths)) @@ -735,14 +743,20 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat txDAG types.TxDAG err error ) - if len(block.TxDAG()) != 0 { - txDAG, err = types.DecodeTxDAG(block.TxDAG()) - if err != nil { - return nil, nil, 0, err + if p.bc.enableTxDAG { + if len(block.TxDAG()) != 0 { + txDAG, err = types.DecodeTxDAG(block.TxDAG()) + if err != nil { + return nil, nil, 0, err + } + } + // load cache txDAG from file + if txDAG == nil && len(p.bc.txDAGMapping) > 0 { + txDAG = p.bc.txDAGMapping[block.NumberU64()] } } // TODO(galaio): need hardfork - if p.bc.chainConfig.Optimism != nil && len(block.Header().Extra) > 0 { + if p.bc.enableTxDAG && p.bc.chainConfig.Optimism != nil && len(block.Header().Extra) > 0 { txDAG, err = types.DecodeTxDAG(block.Header().Extra) if err != nil { return nil, nil, 0, err diff --git a/core/state/statedb.go b/core/state/statedb.go index 3b6f6a373a..5edec2f6b6 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -271,6 +271,7 @@ type StateDB struct { TrieDBCommits time.Duration TrieCommits time.Duration CodeCommits time.Duration + TxDAGGenerate time.Duration AccountUpdated int StorageUpdated int @@ -2424,6 +2425,11 @@ func (s *StateDB) FinaliseRWSet() error { if s.mvStates == nil || s.rwSet == nil { return nil } + if metrics.EnabledExpensive { + defer func(start time.Time) { + s.TxDAGGenerate += time.Since(start) + }(time.Now()) + } ver := types.StateVersion{ TxIndex: s.txIndex, } @@ -2491,6 +2497,11 @@ func (s *StateDB) MVStates2TxDAG() (types.TxDAG, map[int]*types.ExeStat) { if s.mvStates == nil { return types.NewEmptyTxDAG(), nil } + if metrics.EnabledExpensive { + defer func(start time.Time) { + s.TxDAGGenerate += time.Since(start) + }(time.Now()) + } return s.mvStates.ResolveTxDAG(), s.mvStates.Stats() } diff --git a/core/state_processor.go b/core/state_processor.go index f887b80955..9dff86489f 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -29,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" ) @@ -91,7 +90,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) } statedb.MarkFullProcessed() - statedb.ResetMVStates(len(block.Transactions())) + if p.bc.enableTxDAG { + statedb.ResetMVStates(len(block.Transactions())) + } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { statedb.BeginTxStat(i) @@ -121,13 +122,20 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), withdrawals) - // TODO(galaio): append dag into block body, TxDAGPerformance will print metrics when profile is enabled - // compare input TxDAG when it enable in consensus - dag, exrStats := statedb.MVStates2TxDAG() - types.EvaluateTxDAGPerformance(dag, exrStats) - //fmt.Print(types.EvaluateTxDAGPerformance(dag, exrStats)) - log.Info("Process result", "block", block.NumberU64(), "txDAG", dag) - + if p.bc.enableTxDAG { + // TODO(galaio): append dag into block body, TxDAGPerformance will print metrics when profile is enabled + // compare input TxDAG when it enable in consensus + dag, exrStats := statedb.MVStates2TxDAG() + fmt.Print(types.EvaluateTxDAGPerformance(dag, exrStats)) + //log.Info("Process result", "block", block.NumberU64(), "txDAG", dag) + // try write txDAG into file + if p.bc.txDAGWriteCh != nil && dag != nil { + p.bc.txDAGWriteCh <- TxDAGOutputItem{ + blockNumber: block.NumberU64(), + txDAG: dag, + } + } + } return receipts, allLogs, *usedGas, nil } diff --git a/core/types/dag.go b/core/types/dag.go index b407c2bc77..dbd7cf9ea8 100644 --- a/core/types/dag.go +++ b/core/types/dag.go @@ -4,11 +4,12 @@ import ( "bytes" "errors" "fmt" + "strings" + "time" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" "golang.org/x/exp/slices" - "strings" - "time" ) // TxDAGType Used to extend TxDAG and customize a new DAG structure diff --git a/core/types/dag_test.go b/core/types/dag_test.go index bf12324246..bfda2de6e3 100644 --- a/core/types/dag_test.go +++ b/core/types/dag_test.go @@ -1,10 +1,11 @@ package types import ( - "github.com/cometbft/cometbft/libs/rand" "testing" "time" + "github.com/cometbft/cometbft/libs/rand" + "github.com/ethereum/go-ethereum/common" "github.com/holiman/uint256" "github.com/stretchr/testify/require" diff --git a/core/types/mvstates.go b/core/types/mvstates.go index 4db7586727..2584807467 100644 --- a/core/types/mvstates.go +++ b/core/types/mvstates.go @@ -4,12 +4,13 @@ import ( "encoding/hex" "errors" "fmt" + "strings" + "sync" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/holiman/uint256" "golang.org/x/exp/slices" - "strings" - "sync" ) const ( diff --git a/eth/backend.go b/eth/backend.go index aa51d85a71..d1fa93277e 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -269,6 +269,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { return nil, err } + if config.EnableParallelTxDAG { + eth.blockchain.EnableTxDAGGeneration(config.ParallelTxDAGFile) + } if chainConfig := eth.blockchain.Config(); chainConfig.Optimism != nil { // config.Genesis.Config.ChainID cannot be used because it's based on CLI flags only, thus default to mainnet L1 config.NetworkId = chainConfig.ChainID.Uint64() // optimism defaults eth network ID to chain ID eth.networkID = config.NetworkId diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 0e93f58328..c68a0bdde6 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -219,6 +219,8 @@ type Config struct { ParallelTxMode bool // Whether to execute transaction in parallel mode when do full sync ParallelTxNum int // Number of slot for transaction execution EnableOpcodeOptimizing bool + EnableParallelTxDAG bool + ParallelTxDAGFile string } // CreateConsensusEngine creates a consensus engine for the given chain config. diff --git a/miner/miner.go b/miner/miner.go index 2174d6ca81..8734233000 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -62,7 +62,8 @@ var ( snapshotAccountReadTimer = metrics.NewRegisteredTimer("miner/snapshot/account/reads", nil) snapshotStorageReadTimer = metrics.NewRegisteredTimer("miner/snapshot/storage/reads", nil) - waitPayloadTimer = metrics.NewRegisteredTimer("miner/wait/payload", nil) + waitPayloadTimer = metrics.NewRegisteredTimer("miner/wait/payload", nil) + txDAGGenerateTimer = metrics.NewRegisteredTimer("miner/txdag/gen", nil) isBuildBlockInterruptCounter = metrics.NewRegisteredCounter("miner/build/interrupt", nil) ) diff --git a/miner/worker.go b/miner/worker.go index efa125df13..0a732f117d 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1138,7 +1138,9 @@ func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) err } start := time.Now() - env.state.ResetMVStates(0) + if w.chain.TxDAGEnabled() { + env.state.ResetMVStates(0) + } pending := w.eth.TxPool().Pending(true) packFromTxpoolTimer.UpdateSince(start) log.Debug("packFromTxpoolTimer", "duration", common.PrettyDuration(time.Since(start)), "hash", env.header.Hash()) @@ -1268,7 +1270,7 @@ func (w *worker) generateWork(genParams *generateParams) *newPayloadResult { } // Because the TxDAG appends after sidecar, so we only enable after cancun - if w.chainConfig.IsCancun(block.Number(), block.Time()) && w.chainConfig.Optimism == nil { + if w.chain.TxDAGEnabled() && w.chainConfig.IsCancun(block.Number(), block.Time()) && w.chainConfig.Optimism == nil { txDAG, _ := work.state.MVStates2TxDAG() rawTxDAG, err := types.EncodeTxDAG(txDAG) if err != nil { @@ -1278,7 +1280,7 @@ func (w *worker) generateWork(genParams *generateParams) *newPayloadResult { } // TODO(galaio): need hardfork - if w.chainConfig.Optimism != nil { + if w.chain.TxDAGEnabled() && w.chainConfig.Optimism != nil { txDAG, _ := work.state.MVStates2TxDAG() rawTxDAG, err := types.EncodeTxDAG(txDAG) if err != nil { @@ -1286,6 +1288,7 @@ func (w *worker) generateWork(genParams *generateParams) *newPayloadResult { } block.Header().Extra = rawTxDAG } + assembleBlockTimer.UpdateSince(start) log.Debug("assembleBlockTimer", "duration", common.PrettyDuration(time.Since(start)), "parentHash", genParams.parentHash) @@ -1297,6 +1300,7 @@ func (w *worker) generateWork(genParams *generateParams) *newPayloadResult { storageUpdateTimer.Update(work.state.StorageUpdates) // Storage updates are complete(in FinalizeAndAssemble) accountHashTimer.Update(work.state.AccountHashes) // Account hashes are complete(in FinalizeAndAssemble) storageHashTimer.Update(work.state.StorageHashes) // Storage hashes are complete(in FinalizeAndAssemble) + txDAGGenerateTimer.Update(work.state.TxDAGGenerate) innerExecutionTimer.Update(core.DebugInnerExecutionDuration) @@ -1393,7 +1397,7 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti } // Because the TxDAG appends after sidecar, so we only enable after cancun - if w.chainConfig.IsCancun(env.header.Number, env.header.Time) && w.chainConfig.Optimism == nil { + if w.chain.TxDAGEnabled() && w.chainConfig.IsCancun(env.header.Number, env.header.Time) && w.chainConfig.Optimism == nil { for i := len(env.txs); i < len(block.Transactions()); i++ { env.state.RecordSystemTxRWSet(i) } @@ -1406,7 +1410,7 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti } // TODO(galaio): need hardfork - if w.chainConfig.Optimism != nil { + if w.chain.TxDAGEnabled() && w.chainConfig.Optimism != nil { txDAG, _ := env.state.MVStates2TxDAG() rawTxDAG, err := types.EncodeTxDAG(txDAG) if err != nil {