From 3a037f83af38dcd1fe15b57c2180fce497ce5dec Mon Sep 17 00:00:00 2001 From: Pangsu Date: Sat, 9 Nov 2024 14:03:42 +0900 Subject: [PATCH 1/5] feat: handle mpt transition time --- go.mod | 4 +- go.sum | 8 +- kroma-chain-ops/genesis/genesis.go | 10 +- op-e2e/actions/user_test.go | 16 +- op-e2e/migration_test.go | 264 +++++++++++++++++++++ op-node/rollup/derive/engine_controller.go | 4 +- op-node/rollup/driver/state.go | 6 + op-node/rollup/superchain.go | 2 - op-service/flags/flags.go | 2 +- ops-devnet/docker-compose.yml | 2 +- 10 files changed, 288 insertions(+), 30 deletions(-) create mode 100644 op-e2e/migration_test.go diff --git a/go.mod b/go.mod index 44477b478..d57cb0ffb 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/kroma-network/kroma go 1.21 require ( - github.com/btcsuite/btcd v0.24.0 + github.com/btcsuite/btcd v0.24.2 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/cockroachdb/pebble v0.0.0-20231018212520-f6cde3fc2fa4 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 @@ -207,4 +207,4 @@ require ( replace github.com/ethereum-optimism/optimism v1.7.2 => ./ -replace github.com/ethereum/go-ethereum v1.13.8 => github.com/kroma-network/go-ethereum v0.5.0 +replace github.com/ethereum/go-ethereum v1.13.8 => github.com/kroma-network/go-ethereum v1.101308.3-0.20241108083948-d6420143dedc diff --git a/go.sum b/go.sum index d87843cde..ef2f4d5aa 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= -github.com/btcsuite/btcd v0.24.0 h1:gL3uHE/IaFj6fcZSu03SvqPMSx7s/dPzfpG/atRwWdo= -github.com/btcsuite/btcd v0.24.0/go.mod h1:K4IDc1593s8jKXIF7yS7yCTSxrknB9z0STzc2j6XgE4= +github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= +github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= @@ -395,8 +395,8 @@ github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kroma-network/go-ethereum v0.5.0 h1:HqQ7khCuhQSWFCJu6/WnRQaBFlwDpPNNaLqDI3e+Br4= -github.com/kroma-network/go-ethereum v0.5.0/go.mod h1:SEk7AN/4FrDNJZg2pE1ja6xtPxLIJjfsbVVTweOafyk= +github.com/kroma-network/go-ethereum v1.101308.3-0.20241108083948-d6420143dedc h1:9vyH0TEawz5CcO7Kw3jFtDiwo6JJkU0dOZ0OHwhER9A= +github.com/kroma-network/go-ethereum v1.101308.3-0.20241108083948-d6420143dedc/go.mod h1:ZG4M8oph2j0C+R6CtUXuHeeUk5TuN5hVyl9gfwZawJg= github.com/kroma-network/zktrie v0.5.1-0.20230420142222-950ce7a8ce84 h1:VpLCQx+tFV6Nk0hbs3Noyxma/q9wIDdyacKpGQWUMI8= github.com/kroma-network/zktrie v0.5.1-0.20230420142222-950ce7a8ce84/go.mod h1:w54LrYo5rJEV503BgMPRNONsLTOEQv5V87q+uYaw9sM= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= diff --git a/kroma-chain-ops/genesis/genesis.go b/kroma-chain-ops/genesis/genesis.go index 6219df6d5..948877184 100644 --- a/kroma-chain-ops/genesis/genesis.go +++ b/kroma-chain-ops/genesis/genesis.go @@ -62,8 +62,7 @@ func NewL2Genesis(config *DeployConfig, block *types.Block) (*core.Genesis, erro ShanghaiTime: config.CanyonTime(block.Time()), CancunTime: config.EcotoneTime(block.Time()), EcotoneTime: config.EcotoneTime(block.Time()), - // TODO(seolaoh): uncomment this when geth updated - // KromaMPTTime: config.KromaMPTTime(block.Time()), + KromaMPTTime: config.KromaMPTTime(block.Time()), InteropTime: config.InteropTime(block.Time()), Kroma: ¶ms.KromaConfig{ EIP1559Denominator: eip1559Denom, @@ -73,10 +72,9 @@ func NewL2Genesis(config *DeployConfig, block *types.Block) (*core.Genesis, erro Zktrie: true, } - // TODO(seolaoh): turn off Zktrie when MPT time is past in genesis - //if kromaChainConfig.IsKromaMPT(block.Time()) { - // kromaChainConfig.Zktrie = false - //} + if kromaChainConfig.IsKromaMPT(block.Time()) { + kromaChainConfig.Zktrie = false + } gasLimit := config.L2GenesisBlockGasLimit if gasLimit == 0 { diff --git a/op-e2e/actions/user_test.go b/op-e2e/actions/user_test.go index 72ea00bf8..b87b3bcfc 100644 --- a/op-e2e/actions/user_test.go +++ b/op-e2e/actions/user_test.go @@ -5,12 +5,13 @@ import ( "math/rand" "testing" - "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" - "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "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" ) type hardforkScheduledTest struct { @@ -18,7 +19,6 @@ type hardforkScheduledTest struct { canyonTime *hexutil.Uint64 deltaTime *hexutil.Uint64 ecotoneTime *hexutil.Uint64 - kromaMPTTime *hexutil.Uint64 fjordTime *hexutil.Uint64 runToFork string } @@ -35,8 +35,6 @@ func (tc *hardforkScheduledTest) fork(fork string) **hexutil.Uint64 { switch fork { case "fjord": return &tc.fjordTime - case "mpt": - return &tc.kromaMPTTime case "ecotone": return &tc.ecotoneTime case "delta": @@ -67,10 +65,6 @@ func TestCrossLayerUser(t *testing.T) { "canyon", "delta", "ecotone", - // [Kroma: START] - // TODO(seolaoh): uncomment below forks when geth updated - //"mpt", - // [Kroma: END] //"fjord", } for i, fork := range forks { @@ -119,7 +113,6 @@ func runCrossLayerUserTest(gt *testing.T, test hardforkScheduledTest) { dp.DeployConfig.L2GenesisCanyonTimeOffset = test.canyonTime dp.DeployConfig.L2GenesisDeltaTimeOffset = test.deltaTime dp.DeployConfig.L2GenesisEcotoneTimeOffset = test.ecotoneTime - dp.DeployConfig.L2GenesisKromaMPTTimeOffset = test.kromaMPTTime dp.DeployConfig.L2GenesisFjordTimeOffset = test.fjordTime // [Kroma: START] @@ -133,9 +126,6 @@ func runCrossLayerUserTest(gt *testing.T, test hardforkScheduledTest) { if test.ecotoneTime != nil { require.Zero(t, uint64(*test.ecotoneTime)%uint64(dp.DeployConfig.L2BlockTime), "ecotone fork must be aligned") } - if test.kromaMPTTime != nil { - require.Zero(t, uint64(*test.kromaMPTTime)%uint64(dp.DeployConfig.L2BlockTime), "kroma mpt fork must be aligned") - } sd := e2eutils.Setup(t, dp, defaultAlloc) log := testlog.Logger(t, log.LevelDebug) diff --git a/op-e2e/migration_test.go b/op-e2e/migration_test.go new file mode 100644 index 000000000..0a25524ec --- /dev/null +++ b/op-e2e/migration_test.go @@ -0,0 +1,264 @@ +package op_e2e + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/ethclient/gethclient" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rlp" + "github.com/kroma-network/zktrie/trie" + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" + rollupNode "github.com/ethereum-optimism/optimism/op-node/node" + "github.com/ethereum-optimism/optimism/op-node/rollup/driver" + "github.com/ethereum-optimism/optimism/op-node/rollup/sync" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/kroma-network/kroma/kroma-bindings/predeploys" + "github.com/kroma-network/kroma/op-e2e/e2eutils/wait" +) + +func TestMigration(t *testing.T) { + InitParallel(t) + + zero := hexutil.Uint64(0) + one := hexutil.Uint64(1) + mptTimeOffset := hexutil.Uint64(60) + + cfg := DefaultSystemConfig(t) + cfg.DeployConfig.L2GenesisDeltaTimeOffset = &zero + cfg.DeployConfig.L2GenesisEcotoneTimeOffset = &one + cfg.DeployConfig.L2GenesisKromaMPTTimeOffset = &mptTimeOffset + + // Setup historical rpc node. + historicalRpcPort := 8045 + cfg.Nodes["historical"] = &rollupNode.Config{ + Driver: driver.Config{ + VerifierConfDepth: 0, + SequencerConfDepth: 0, + SequencerEnabled: false, + }, + L1EpochPollInterval: time.Second * 4, + RuntimeConfigReloadInterval: time.Minute * 10, + ConfigPersistence: &rollupNode.DisabledConfigPersistence{}, + Sync: sync.Config{SyncMode: sync.CLSync}, + } + cfg.Loggers["historical"] = testlog.Logger(t, log.LevelInfo).New("role", "historical") + cfg.GethOptions["historical"] = append(cfg.GethOptions["historical"], []geth.GethOption{ + func(ethCfg *ethconfig.Config, nodeCfg *node.Config) error { + nodeCfg.HTTPPort = historicalRpcPort + nodeCfg.HTTPModules = []string{"debug", "eth"} + nodeCfg.HTTPHost = "127.0.0.1" + return nil + }, + }...) + + // Set historical rpc endpoint. + for name := range cfg.Nodes { + name := name + cfg.GethOptions[name] = append(cfg.GethOptions[name], []geth.GethOption{ + func(ethCfg *ethconfig.Config, nodeCfg *node.Config) error { + // Since the migration process requires preimages, enable storing preimage option. + ethCfg.Preimages = true + ethCfg.RollupHistoricalRPC = fmt.Sprintf("http://127.0.0.1:%d", historicalRpcPort) + if name == "historical" { + ethCfg.RollupHistoricalRPC = "" + ethCfg.DisableMPTMigration = true + } + // Deep copy the genesis + dst := &core.Genesis{} + b, _ := json.Marshal(ethCfg.Genesis) + err := json.Unmarshal(b, dst) + if err != nil { + return err + } + ethCfg.Genesis = dst + return nil + }, + }...) + } + + sys, err := cfg.Start(t) + defer sys.Close() + require.Nil(t, err, "Error starting up system") + l1Cl := sys.Clients["l1"] + l2Seq := sys.Clients["sequencer"] + l2Verif := sys.Clients["verifier"] + + // Send L2 TX + _, err = wait.ForTransferTxOnL2(sys.Cfg.L2ChainIDBig(), l2Seq, l2Verif, cfg.Secrets.Alice, common.Address{0xff, 0xff}, common.Big1) + require.NoError(t, err) + + // Deploy contract on L2 + minimalCode := common.Hex2Bytes("601680600c6000396000f3fe3615600b576000803555005b60016000540160005500") + chainId := cfg.L2ChainIDBig() + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + nonce, err := l2Seq.PendingNonceAt(ctx, cfg.Secrets.Addresses().Alice) + require.NoError(t, err) + tx := types.MustSignNewTx(cfg.Secrets.Alice, types.LatestSignerForChainID(chainId), &types.DynamicFeeTx{ + ChainID: chainId, + Nonce: nonce, + To: nil, + GasTipCap: big.NewInt(10), + GasFeeCap: big.NewInt(200), + Gas: 100_000, + Data: minimalCode, + }) + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) + defer cancel() + err = l2Seq.SendTransaction(ctx, tx) + require.NoError(t, err) + deployReceipt, err := geth.WaitForTransaction(tx.Hash(), l2Verif, 4*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) + require.NoError(t, err) + // Set storage + tx = types.MustSignNewTx(cfg.Secrets.Alice, types.LatestSignerForChainID(chainId), &types.DynamicFeeTx{ + ChainID: chainId, + Nonce: tx.Nonce() + 1, + To: &deployReceipt.ContractAddress, + GasTipCap: big.NewInt(10), + GasFeeCap: big.NewInt(200), + Gas: 100_000, + }) + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) + defer cancel() + err = l2Seq.SendTransaction(ctx, tx) + require.NoError(t, err) + _, err = geth.WaitForTransaction(tx.Hash(), l2Verif, 4*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) + require.NoError(t, err) + // Check storage value + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + slot := common.Hash{} + value, err := l2Verif.StorageAt(ctx, *tx.To(), slot, nil) + require.NoError(t, err) + require.True(t, new(big.Int).SetBytes(value).Uint64() > 0) + // Delete storage + tx = types.MustSignNewTx(cfg.Secrets.Alice, types.LatestSignerForChainID(chainId), &types.DynamicFeeTx{ + ChainID: chainId, + Nonce: tx.Nonce() + 1, + To: &deployReceipt.ContractAddress, + GasTipCap: big.NewInt(10), + GasFeeCap: big.NewInt(200), + Gas: 100_000, + Data: slot.Bytes(), + }) + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) + defer cancel() + err = l2Seq.SendTransaction(ctx, tx) + require.NoError(t, err) + _, err = geth.WaitForTransaction(tx.Hash(), l2Verif, 4*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) + require.NoError(t, err) + // Check storage value + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + value, err = l2Verif.StorageAt(ctx, *tx.To(), slot, nil) + require.NoError(t, err) + require.True(t, new(big.Int).SetBytes(value).Uint64() == 0) + + transitionBlockNumber := new(big.Int).SetUint64(uint64(mptTimeOffset) / cfg.DeployConfig.L2BlockTime) + _, err = geth.WaitForBlock(transitionBlockNumber, l2Verif, time.Minute) + require.Nil(t, err) + + // Ensure that the transition block inserted into chain. + ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + transitionBlock, err := l2Verif.BlockByNumber(ctx, transitionBlockNumber) + require.Nil(t, err) + require.Equal(t, []byte("BEDROCK"), transitionBlock.Extra()) + + // Ensure that the transition block has been finalized. + l2Finalized, err := geth.WaitForBlockToBeFinalized(transitionBlockNumber, l2Verif, 1*time.Minute) + require.NoError(t, err, "must be able to fetch a finalized L2 block") + require.NotZerof(t, l2Finalized.NumberU64(), "must have finalized L2 block") + + validateL1BlockTxProof(t, l1Cl, l2Verif, transitionBlockNumber) + + // Check states after migration + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + balance, err := l2Verif.BalanceAt(ctx, common.Address{0xff, 0xff}, nil) + require.Nil(t, err) + require.True(t, balance.Cmp(common.Big0) == 1) + + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + code, err := l2Verif.CodeAt(ctx, deployReceipt.ContractAddress, nil) + require.Nil(t, err) + require.True(t, len(code) > 0) +} + +func validateL1BlockTxProof(t *testing.T, l1Cl *ethclient.Client, l2Cl *ethclient.Client, number *big.Int) { + l1BlockHashSlot := "0x2" + l2GethCl := gethclient.New(l2Cl.Client()) + + validateZktProof := func(hex string) { + b := common.Hex2Bytes(strings.TrimPrefix(hex, "0x")) + _, err := trie.DecodeSMTProof(b) + require.Nil(t, err) + } + validateMptProof := func(hex string) { + b := common.Hex2Bytes(strings.TrimPrefix(hex, "0x")) + _, _, err := rlp.SplitList(b) + require.Nil(t, err) + } + validateL1BlockHash := func(v *big.Int) { + _, err := l1Cl.BlockByHash(context.Background(), common.BigToHash(v)) + require.Nil(t, err) + } + proof, err := l2GethCl.GetProof(context.Background(), predeploys.L1BlockAddr, []string{l1BlockHashSlot}, new(big.Int).Sub(number, common.Big1)) + require.Nil(t, err, "failed to validate state proof for pre-transition block") + for _, accProof := range proof.AccountProof { + validateZktProof(accProof) + } + for _, storageProof := range proof.StorageProof { + for _, p := range storageProof.Proof { + validateZktProof(p) + } + if storageProof.Key == l1BlockHashSlot { + validateL1BlockHash(storageProof.Value) + } + } + + proof, err = l2GethCl.GetProof(context.Background(), predeploys.L1BlockAddr, []string{l1BlockHashSlot}, number) + require.Nil(t, err, "failed to validate state proof for transition block") + for _, accProof := range proof.AccountProof { + validateMptProof(accProof) + } + for _, storageProof := range proof.StorageProof { + for _, p := range storageProof.Proof { + validateMptProof(p) + } + if storageProof.Key == l1BlockHashSlot { + validateL1BlockHash(storageProof.Value) + } + } + + proof, err = l2GethCl.GetProof(context.Background(), predeploys.L1BlockAddr, []string{l1BlockHashSlot}, new(big.Int).Add(number, common.Big1)) + require.Nil(t, err) + require.Nil(t, err, "failed to validate state proof for post-transition block") + for _, accProof := range proof.AccountProof { + validateMptProof(accProof) + } + for _, storageProof := range proof.StorageProof { + for _, p := range storageProof.Proof { + validateMptProof(p) + } + if storageProof.Key == l1BlockHashSlot { + validateL1BlockHash(storageProof.Value) + } + } +} diff --git a/op-node/rollup/derive/engine_controller.go b/op-node/rollup/derive/engine_controller.go index d9103fcd5..60635b5e4 100644 --- a/op-node/rollup/derive/engine_controller.go +++ b/op-node/rollup/derive/engine_controller.go @@ -327,7 +327,9 @@ func (e *EngineController) InsertUnsafePayload(ctx context.Context, envelope *et // Check if there is a finalized head once when doing EL sync. If so, transition to CL sync if e.syncStatus == syncStatusWillStartEL { b, err := e.engine.L2BlockRefByLabel(ctx, eth.Finalized) - isTransitionBlock := e.rollupCfg.Genesis.L2.Number != 0 && b.Hash == e.rollupCfg.Genesis.L2.Hash + // [Kroma: ZKT to MPT] + isTransitionBlock := e.rollupCfg.Genesis.L2.Number != 0 && b.Hash == e.rollupCfg.Genesis.L2.Hash || e.rollupCfg.IsKromaMPT(ref.Time) + // [Kroma: END] if errors.Is(err, ethereum.NotFound) || isTransitionBlock { e.syncStatus = syncStatusStartedEL e.log.Info("Starting EL sync") diff --git a/op-node/rollup/driver/state.go b/op-node/rollup/driver/state.go index 33859ab8a..a7ac01237 100644 --- a/op-node/rollup/driver/state.go +++ b/op-node/rollup/driver/state.go @@ -7,10 +7,12 @@ import ( "errors" "fmt" "io" + "strings" gosync "sync" "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-node/rollup" @@ -389,6 +391,10 @@ func (s *Driver) eventLoop() { s.metrics.RecordPipelineReset() continue } else if err != nil && errors.Is(err, derive.ErrTemporary) { + if strings.Contains(err.Error(), core.HaltOnStateTransition.Error()) { + s.log.Warn("Stopping derivation pipeline", "reason", err) + return + } s.log.Warn("Derivation process temporary error", "attempts", stepAttempts, "err", err) reqStep() continue diff --git a/op-node/rollup/superchain.go b/op-node/rollup/superchain.go index cc74b209a..586859634 100644 --- a/op-node/rollup/superchain.go +++ b/op-node/rollup/superchain.go @@ -101,8 +101,6 @@ func LoadOPStackRollupConfig(chainID uint64) (*Config, error) { CanyonTime: chConfig.CanyonTime, DeltaTime: chConfig.DeltaTime, EcotoneTime: chConfig.EcotoneTime, - // TODO(seolaoh): uncomment this when geth updated - // KromaMPTTime: chConfig.KromaMPTTime, FjordTime: chConfig.FjordTime, BatchInboxAddress: common.Address(chConfig.BatchInboxAddr), DepositContractAddress: common.Address(addrs.OptimismPortalProxy), diff --git a/op-service/flags/flags.go b/op-service/flags/flags.go index eacc515d2..673b9a5a7 100644 --- a/op-service/flags/flags.go +++ b/op-service/flags/flags.go @@ -45,7 +45,7 @@ func CLIFlags(envPrefix string, category string) []cli.Flag { &cli.Uint64Flag{ Name: KromaMPTOverrideFlagName, Usage: "Manually specify the Kroma MPT fork timestamp, overriding the bundled setting", - EnvVars: opservice.PrefixEnvVar(envPrefix, "OVERRIDE_KROMA_MPT"), + EnvVars: opservice.PrefixEnvVar(envPrefix, "OVERRIDE_MPT"), Hidden: false, Category: category, }, diff --git a/ops-devnet/docker-compose.yml b/ops-devnet/docker-compose.yml index ed2e87201..e8eabb625 100644 --- a/ops-devnet/docker-compose.yml +++ b/ops-devnet/docker-compose.yml @@ -107,7 +107,7 @@ services: l2: pid: host # allow debugging - image: kromanetwork/geth:v0.5.0 + image: kromanetwork/geth:mpt-d6420143 ports: - "9545:8545" - "9546:8546" From 11f2c8fa7d45910d0ff6af03d6debc57962dd374 Mon Sep 17 00:00:00 2001 From: Pangsu Date: Fri, 12 Jul 2024 18:45:49 +0900 Subject: [PATCH 2/5] feat: separate DepositTx into OP version and Kroma version --- go.mod | 2 +- go.sum | 4 +- op-e2e/op_geth_test.go | 136 +++++++----------- op-e2e/system_tob_test.go | 15 +- op-e2e/testdata/challenge_test_data.go | 4 +- op-node/p2p/sync.go | 16 +++ op-node/rollup/derive/attributes.go | 33 +++++ op-node/rollup/derive/deposit_log.go | 2 - op-node/rollup/derive/deposit_log_tob_test.go | 16 +-- .../derive/ecotone_upgrade_transactions.go | 82 +++++------ op-node/rollup/derive/engine_consolidate.go | 13 +- op-node/rollup/derive/engine_queue.go | 1 - op-node/rollup/derive/l1_block_info.go | 23 +-- op-node/rollup/derive/l1_block_info_test.go | 8 -- op-service/testutils/deposits.go | 15 +- ops-devnet/docker-compose.yml | 2 +- 16 files changed, 187 insertions(+), 185 deletions(-) diff --git a/go.mod b/go.mod index d57cb0ffb..5f11aff51 100644 --- a/go.mod +++ b/go.mod @@ -207,4 +207,4 @@ require ( replace github.com/ethereum-optimism/optimism v1.7.2 => ./ -replace github.com/ethereum/go-ethereum v1.13.8 => github.com/kroma-network/go-ethereum v1.101308.3-0.20241108083948-d6420143dedc +replace github.com/ethereum/go-ethereum v1.13.8 => github.com/kroma-network/go-ethereum v1.101308.3-0.20241108085945-79ec5dfd8be5 diff --git a/go.sum b/go.sum index ef2f4d5aa..d50ac2c7c 100644 --- a/go.sum +++ b/go.sum @@ -395,8 +395,8 @@ github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kroma-network/go-ethereum v1.101308.3-0.20241108083948-d6420143dedc h1:9vyH0TEawz5CcO7Kw3jFtDiwo6JJkU0dOZ0OHwhER9A= -github.com/kroma-network/go-ethereum v1.101308.3-0.20241108083948-d6420143dedc/go.mod h1:ZG4M8oph2j0C+R6CtUXuHeeUk5TuN5hVyl9gfwZawJg= +github.com/kroma-network/go-ethereum v1.101308.3-0.20241108085945-79ec5dfd8be5 h1:SKBXidreKLNJCV5cPD5Tefd0Iu1N8si3TXThr82rXO0= +github.com/kroma-network/go-ethereum v1.101308.3-0.20241108085945-79ec5dfd8be5/go.mod h1:ZG4M8oph2j0C+R6CtUXuHeeUk5TuN5hVyl9gfwZawJg= github.com/kroma-network/zktrie v0.5.1-0.20230420142222-950ce7a8ce84 h1:VpLCQx+tFV6Nk0hbs3Noyxma/q9wIDdyacKpGQWUMI8= github.com/kroma-network/zktrie v0.5.1-0.20230420142222-950ce7a8ce84/go.mod h1:w54LrYo5rJEV503BgMPRNONsLTOEQv5V87q+uYaw9sM= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= diff --git a/op-e2e/op_geth_test.go b/op-e2e/op_geth_test.go index 188c1bb7f..f0c7cafd6 100644 --- a/op-e2e/op_geth_test.go +++ b/op-e2e/op_geth_test.go @@ -87,13 +87,11 @@ func TestInvalidDepositInFCU(t *testing.T) { require.Equal(t, 0, balance.Cmp(common.Big0)) badDepositTx := types.NewTx(&types.DepositTx{ - From: fromAddr, - To: &fromAddr, // send it to ourselves - Value: big.NewInt(params.Ether), - Gas: 25000, - /* [Kroma: START] + From: fromAddr, + To: &fromAddr, // send it to ourselves + Value: big.NewInt(params.Ether), + Gas: 25000, IsSystemTransaction: false, - [Kroma: END] */ }) // We are inserting a block with an invalid deposit. @@ -259,13 +257,11 @@ func TestPreregolith(t *testing.T) { // Simple transfer deposit tx depositTx := types.NewTx(&types.DepositTx{ - From: fromAddr, - To: &fromAddr, // send it to ourselves - Value: big.NewInt(params.Ether), - Gas: 25000, - /* [Kroma: START] + From: fromAddr, + To: &fromAddr, // send it to ourselves + Value: big.NewInt(params.Ether), + Gas: 25000, IsSystemTransaction: false, - [Kroma: END] */ }) envelope, err := opGeth.AddL2Block(ctx, depositTx) @@ -306,24 +302,20 @@ func TestPreregolith(t *testing.T) { fromAddr := cfg.Secrets.Addresses().Alice // Include a tx just to ensure Alice's nonce isn't 0 incrementNonceTx := types.NewTx(&types.DepositTx{ - From: fromAddr, - To: &fromAddr, - Value: big.NewInt(0), - Gas: 21_000, - /* [Kroma: START] + From: fromAddr, + To: &fromAddr, + Value: big.NewInt(0), + Gas: 21_000, IsSystemTransaction: false, - [Kroma: END] */ }) // Contract creation deposit tx contractCreateTx := types.NewTx(&types.DepositTx{ - From: fromAddr, - Value: big.NewInt(params.Ether), - Gas: 1000001, - Data: []byte{}, - /* [Kroma: START] + From: fromAddr, + Value: big.NewInt(params.Ether), + Gas: 1000001, + Data: []byte{}, IsSystemTransaction: false, - [Kroma: END] */ }) _, err = opGeth.AddL2Block(ctx, incrementNonceTx, contractCreateTx) @@ -372,10 +364,8 @@ func TestPreregolith(t *testing.T) { To: &fromAddr, // send it to ourselves Value: big.NewInt(params.Ether), // SystemTx is assigned 1M gas limit - Gas: uint64(cfg.DeployConfig.L2GenesisBlockGasLimit) - 1_000_000, - /* [Kroma: START] + Gas: uint64(cfg.DeployConfig.L2GenesisBlockGasLimit) - 1_000_000, IsSystemTransaction: false, - [Kroma: END] */ }) signer := types.LatestSigner(opGeth.L2ChainConfig) @@ -410,9 +400,7 @@ func TestPreregolith(t *testing.T) { rollupCfg := rollup.Config{} systemTx, err := derive.L1InfoDeposit(&rollupCfg, opGeth.SystemConfig, 1, opGeth.L1Head, 0) - /* [Kroma: START] systemTx.IsSystemTransaction = true - [Kroma: END] */ require.NoError(t, err) _, err = opGeth.AddL2Block(ctx, types.NewTx(systemTx)) @@ -458,13 +446,11 @@ func TestRegolith(t *testing.T) { // Simple transfer deposit tx depositTx := types.NewTx(&types.DepositTx{ - From: fromAddr, - To: &fromAddr, // send it to ourselves - Value: big.NewInt(params.Ether), - Gas: 25000, - /* [Kroma: START] + From: fromAddr, + To: &fromAddr, // send it to ourselves + Value: big.NewInt(params.Ether), + Gas: 25000, IsSystemTransaction: false, - [Kroma: END] */ }) envelope, err := opGeth.AddL2Block(ctx, depositTx) @@ -508,24 +494,20 @@ func TestRegolith(t *testing.T) { fromAddr := cfg.Secrets.Addresses().Alice // Include a tx just to ensure Alice's nonce isn't 0 incrementNonceTx := types.NewTx(&types.DepositTx{ - From: fromAddr, - To: &fromAddr, - Value: big.NewInt(0), - Gas: 21_000, - /* [Kroma: START] + From: fromAddr, + To: &fromAddr, + Value: big.NewInt(0), + Gas: 21_000, IsSystemTransaction: false, - [Kroma: END] */ }) // Contract creation deposit tx contractCreateTx := types.NewTx(&types.DepositTx{ - From: fromAddr, - Value: big.NewInt(params.Ether), - Gas: 1000001, - Data: []byte{}, - /* [Kroma: START] + From: fromAddr, + Value: big.NewInt(params.Ether), + Gas: 1000001, + Data: []byte{}, IsSystemTransaction: false, - [Kroma: END] */ }) _, err = opGeth.AddL2Block(ctx, incrementNonceTx, contractCreateTx) @@ -581,10 +563,8 @@ func TestRegolith(t *testing.T) { To: &fromAddr, // send it to ourselves Value: big.NewInt(params.Ether), // SystemTx is assigned 1M gas limit - Gas: uint64(cfg.DeployConfig.L2GenesisBlockGasLimit) - 1_000_000, - /* [Kroma: START] + Gas: uint64(cfg.DeployConfig.L2GenesisBlockGasLimit) - 1_000_000, IsSystemTransaction: false, - [Kroma: END] */ }) signer := types.LatestSigner(opGeth.L2ChainConfig) @@ -622,9 +602,7 @@ func TestRegolith(t *testing.T) { rollupCfg := rollup.Config{} systemTx, err := derive.L1InfoDeposit(&rollupCfg, opGeth.SystemConfig, 1, opGeth.L1Head, 0) - /* [Kroma: START] systemTx.IsSystemTransaction = true - [Kroma: END] */ require.NoError(t, err) _, err = opGeth.AddL2Block(ctx, types.NewTx(systemTx)) @@ -686,50 +664,42 @@ func TestRegolith(t *testing.T) { // Deposit TX to deploy a contract that lets us store an arbitrary value deployTx := types.NewTx(&types.DepositTx{ - From: fromAddr, - Value: common.Big0, - Data: deployData, - Gas: 1_000_000, - /* [Kroma: START] + From: fromAddr, + Value: common.Big0, + Data: deployData, + Gas: 1_000_000, IsSystemTransaction: false, - [Kroma: END] */ }) // Store a non-zero value storeTx := types.NewTx(&types.DepositTx{ - From: fromAddr, - To: &storeContractAddr, - Value: common.Big0, - Data: []byte{0x06}, - Gas: 1_000_000, - /* [Kroma: START] + From: fromAddr, + To: &storeContractAddr, + Value: common.Big0, + Data: []byte{0x06}, + Gas: 1_000_000, IsSystemTransaction: false, - [Kroma: END] */ }) // Store a non-zero value zeroTx := types.NewTx(&types.DepositTx{ - From: fromAddr, - To: &storeContractAddr, - Value: common.Big0, - Data: []byte{0x00}, - Gas: 1_000_000, - /* [Kroma: START] + From: fromAddr, + To: &storeContractAddr, + Value: common.Big0, + Data: []byte{0x00}, + Gas: 1_000_000, IsSystemTransaction: false, - [Kroma: END] */ }) // Store a non-zero value again // Has same gas cost as zeroTx, except the first tx gets a gas refund for clearing the storage slot rezeroTx := types.NewTx(&types.DepositTx{ - From: fromAddr, - To: &storeContractAddr, - Value: common.Big0, - Data: []byte{0x00}, - Gas: 1_000_001, - /* [Kroma: START] + From: fromAddr, + To: &storeContractAddr, + Value: common.Big0, + Data: []byte{0x00}, + Gas: 1_000_001, IsSystemTransaction: false, - [Kroma: END] */ }) _, err = opGeth.AddL2Block(ctx, deployTx, storeTx, zeroTx, rezeroTx) @@ -815,9 +785,7 @@ func TestPreCanyon(t *testing.T) { Data: []byte{ byte(vm.PUSH0), }, - /* [Kroma: START] IsSystemTransaction: false, - [Kroma: END] */ }) _, err = opGeth.AddL2Block(ctx, pushZeroContractCreateTxn) @@ -891,9 +859,7 @@ func TestCanyon(t *testing.T) { Data: []byte{ byte(vm.PUSH0), }, - /* [Kroma: START] IsSystemTransaction: false, - [Kroma: END] */ }) _, err = opGeth.AddL2Block(ctx, pushZeroContractCreateTxn) @@ -961,9 +927,7 @@ func TestPreEcotone(t *testing.T) { byte(vm.PUSH2), byte(vm.TSTORE), }, - /* [Kroma: START] IsSystemTransaction: false, - [Kroma: END] */ }) _, err = opGeth.AddL2Block(ctx, tstoreTxn) @@ -1044,9 +1008,7 @@ func TestEcotone(t *testing.T) { byte(vm.TSTORE), byte(vm.PUSH0), }, - /* [Kroma: START] IsSystemTransaction: false, - [Kroma: END] */ }) _, err = opGeth.AddL2Block(ctx, tstoreTxn) diff --git a/op-e2e/system_tob_test.go b/op-e2e/system_tob_test.go index f3a57bb12..6e97efe87 100644 --- a/op-e2e/system_tob_test.go +++ b/op-e2e/system_tob_test.go @@ -139,13 +139,14 @@ func TestL2SequencerRPCDepositTx(t *testing.T) { // Create a deposit tx to send over RPC. tx := types.NewTx(&types.DepositTx{ - SourceHash: common.Hash{}, - From: crypto.PubkeyToAddress(txSigningKey.PublicKey), - To: &common.Address{0xff, 0xff}, - Mint: big.NewInt(1000), - Value: big.NewInt(1000), - Gas: 0, - Data: nil, + SourceHash: common.Hash{}, + From: crypto.PubkeyToAddress(txSigningKey.PublicKey), + To: &common.Address{0xff, 0xff}, + Mint: big.NewInt(1000), + Value: big.NewInt(1000), + Gas: 0, + IsSystemTransaction: false, + Data: nil, }) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) diff --git a/op-e2e/testdata/challenge_test_data.go b/op-e2e/testdata/challenge_test_data.go index 54c670585..935bc137c 100644 --- a/op-e2e/testdata/challenge_test_data.go +++ b/op-e2e/testdata/challenge_test_data.go @@ -105,7 +105,7 @@ func SetPrevOutputWithProofResponse(o *eth.OutputWithProofResponse) error { } toAddr := common.HexToAddress("0x4200000000000000000000000000000000000002") o.PublicInputProof.NextTransactions = types.Transactions{ - types.NewTx(&types.DepositTx{ + types.NewTx(&types.KromaDepositTx{ SourceHash: common.HexToHash("0x81e84a0b340571d1b0ef61008afa413d4dc9b50884003177f02294cb961b7503"), From: common.HexToAddress("0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001"), To: &toAddr, @@ -173,7 +173,7 @@ func SetTargetOutputWithProofResponse(o *eth.OutputWithProofResponse) error { } toAddr := common.HexToAddress("0x4200000000000000000000000000000000000002") o.PublicInputProof.NextTransactions = types.Transactions{ - types.NewTx(&types.DepositTx{ + types.NewTx(&types.KromaDepositTx{ SourceHash: common.HexToHash("0x40bcbd870b6c68f66e727762654cf39e1491b20be579a13d231a6a6d21f204ce"), From: common.HexToAddress("0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001"), To: &toAddr, diff --git a/op-node/p2p/sync.go b/op-node/p2p/sync.go index 50ea1797e..fa8d4f5f3 100644 --- a/op-node/p2p/sync.go +++ b/op-node/p2p/sync.go @@ -21,10 +21,12 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/kroma-network/kroma/op-node/rollup/derive" ) // StreamCtxFn provides a new context to use when handling stream requests @@ -668,6 +670,20 @@ func (s *SyncClient) readExecutionPayload(data []byte, expectedTime uint64) (*et return nil, fmt.Errorf("failed to decode response: %w", err) } + // [Kroma: START] Use KromaDepositTx instead of DepositTx before MPT time + if !s.cfg.IsKromaMPT(expectedTime) { + for i, otx := range res.Transactions { + if otx[0] == types.DepositTxType { + var err error + res.Transactions[i], err = derive.ToKromaDepositBytes(otx) + if err != nil { + return nil, fmt.Errorf("failed to convert deposit tx: %w", err) + } + } + } + } + // [Kroma: END] + return ð.ExecutionPayloadEnvelope{ExecutionPayload: &res}, nil } diff --git a/op-node/rollup/derive/attributes.go b/op-node/rollup/derive/attributes.go index 19cc47a00..fce69e197 100644 --- a/op-node/rollup/derive/attributes.go +++ b/op-node/rollup/derive/attributes.go @@ -117,6 +117,24 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex txs = append(txs, depositTxs...) txs = append(txs, upgradeTxs...) + // [Kroma: START] + // In Kroma, the IsSystemTransaction field was deleted from DepositTx. + // After transitioning to MPT, we bring back the IsSystemTransaction field for compatibility with OP. + // Therefore, before MPT time, use KromaDepositTx struct to create deposit transactions without that field. + if !ba.rollupCfg.IsKromaMPT(nextL2Time) { + for i, otx := range txs { + if otx[0] != types.DepositTxType { + continue + } + tx, err := ToKromaDepositBytes(otx) + if err != nil { + return nil, NewCriticalError(err) + } + txs[i] = tx + } + } + // [Kroma: END] + var withdrawals *types.Withdrawals if ba.rollupCfg.IsCanyon(nextL2Time) { withdrawals = &types.Withdrawals{} @@ -141,3 +159,18 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex ParentBeaconBlockRoot: parentBeaconRoot, }, nil } + +func ToKromaDepositBytes(input hexutil.Bytes) (hexutil.Bytes, error) { + if input[0] != types.DepositTxType { + return nil, fmt.Errorf("unexpected transaction type: %d", input[0]) + } + if types.IsKromaDepositTx(input[1:]) { + return input, nil + } + var tx types.Transaction + if err := tx.UnmarshalBinary(input); err != nil { + return nil, fmt.Errorf("failed to decode deposit tx: %w", err) + } + kromaDepTx := tx.ToKromaDepositTx() + return kromaDepTx.MarshalBinary() +} diff --git a/op-node/rollup/derive/deposit_log.go b/op-node/rollup/derive/deposit_log.go index 465a4d17a..74fde28c5 100644 --- a/op-node/rollup/derive/deposit_log.go +++ b/op-node/rollup/derive/deposit_log.go @@ -81,9 +81,7 @@ func UnmarshalDepositLogEvent(ev *types.Log) (*types.DepositTx, error) { } dep.SourceHash = source.SourceHash() dep.From = from - /* [Kroma: START] dep.IsSystemTransaction = false - [Kroma: END] */ var err error switch version { diff --git a/op-node/rollup/derive/deposit_log_tob_test.go b/op-node/rollup/derive/deposit_log_tob_test.go index 21d838552..c8d3b1dcf 100644 --- a/op-node/rollup/derive/deposit_log_tob_test.go +++ b/op-node/rollup/derive/deposit_log_tob_test.go @@ -61,16 +61,14 @@ func fuzzReceipts(typeProvider *fuzz.Fuzzer, blockHash common.Hash, depositContr // Create our deposit transaction dep := &types.DepositTx{ - SourceHash: source.SourceHash(), - From: *fuzzedDepositInfo.FromAddr, - To: fuzzedDepositInfo.ToAddr, - Value: fuzzedDepositInfo.Value, - Gas: fuzzedDepositInfo.Gas, - Data: fuzzedDepositInfo.Data, - Mint: fuzzedDepositInfo.Mint, - /* [Kroma: START] + SourceHash: source.SourceHash(), + From: *fuzzedDepositInfo.FromAddr, + To: fuzzedDepositInfo.ToAddr, + Value: fuzzedDepositInfo.Value, + Gas: fuzzedDepositInfo.Gas, + Data: fuzzedDepositInfo.Data, + Mint: fuzzedDepositInfo.Mint, IsSystemTransaction: false, - [Kroma: END] */ } // Marshal our actual log event diff --git a/op-node/rollup/derive/ecotone_upgrade_transactions.go b/op-node/rollup/derive/ecotone_upgrade_transactions.go index 582035c65..119a4abf8 100644 --- a/op-node/rollup/derive/ecotone_upgrade_transactions.go +++ b/op-node/rollup/derive/ecotone_upgrade_transactions.go @@ -53,8 +53,8 @@ func EcotoneNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { Data: l1BlockDeploymentBytecode, // [Kroma: START] Gas: 500_000, - // IsSystemTransaction: false, // [Kroma: END] + IsSystemTransaction: false, }).MarshalBinary() if err != nil { return nil, err @@ -63,16 +63,14 @@ func EcotoneNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { upgradeTxns = append(upgradeTxns, deployL1BlockTransaction) deployGasPriceOracle, err := types.NewTx(&types.DepositTx{ - SourceHash: deployGasPriceOracleSource.SourceHash(), - From: GasPriceOracleDeployerAddress, - To: nil, - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 1_000_000, - /* [Kroma: START] + SourceHash: deployGasPriceOracleSource.SourceHash(), + From: GasPriceOracleDeployerAddress, + To: nil, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 1_000_000, IsSystemTransaction: false, - [Kroma: END] */ - Data: gasPriceOracleDeploymentBytecode, + Data: gasPriceOracleDeploymentBytecode, }).MarshalBinary() if err != nil { return nil, err @@ -81,16 +79,14 @@ func EcotoneNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { upgradeTxns = append(upgradeTxns, deployGasPriceOracle) updateL1BlockProxy, err := types.NewTx(&types.DepositTx{ - SourceHash: updateL1BlockProxySource.SourceHash(), - From: common.Address{}, - To: &predeploys.L1BlockAddr, - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 50_000, - /* [Kroma: START] + SourceHash: updateL1BlockProxySource.SourceHash(), + From: common.Address{}, + To: &predeploys.L1BlockAddr, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 50_000, IsSystemTransaction: false, - [Kroma: END] */ - Data: upgradeToCalldata(newL1BlockAddress), + Data: upgradeToCalldata(newL1BlockAddress), }).MarshalBinary() if err != nil { return nil, err @@ -99,16 +95,14 @@ func EcotoneNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { upgradeTxns = append(upgradeTxns, updateL1BlockProxy) updateGasPriceOracleProxy, err := types.NewTx(&types.DepositTx{ - SourceHash: updateGasPriceOracleSource.SourceHash(), - From: common.Address{}, - To: &predeploys.GasPriceOracleAddr, - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 50_000, - /* [Kroma: START] + SourceHash: updateGasPriceOracleSource.SourceHash(), + From: common.Address{}, + To: &predeploys.GasPriceOracleAddr, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 50_000, IsSystemTransaction: false, - [Kroma: END] */ - Data: upgradeToCalldata(newGasPriceOracleAddress), + Data: upgradeToCalldata(newGasPriceOracleAddress), }).MarshalBinary() if err != nil { return nil, err @@ -117,16 +111,14 @@ func EcotoneNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { upgradeTxns = append(upgradeTxns, updateGasPriceOracleProxy) enableEcotone, err := types.NewTx(&types.DepositTx{ - SourceHash: enableEcotoneSource.SourceHash(), - From: L1InfoDepositerAddress, - To: &predeploys.GasPriceOracleAddr, - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 80_000, - /* [Kroma: START] + SourceHash: enableEcotoneSource.SourceHash(), + From: L1InfoDepositerAddress, + To: &predeploys.GasPriceOracleAddr, + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 80_000, IsSystemTransaction: false, - [Kroma: END] */ - Data: enableEcotoneInput, + Data: enableEcotoneInput, }).MarshalBinary() if err != nil { return nil, err @@ -134,16 +126,14 @@ func EcotoneNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { upgradeTxns = append(upgradeTxns, enableEcotone) deployEIP4788, err := types.NewTx(&types.DepositTx{ - From: EIP4788From, - To: nil, // contract-deployment tx - Mint: big.NewInt(0), - Value: big.NewInt(0), - Gas: 0x3d090, // hex constant, as defined in EIP-4788 - Data: eip4788CreationData, - /* [Kroma: START] + From: EIP4788From, + To: nil, // contract-deployment tx + Mint: big.NewInt(0), + Value: big.NewInt(0), + Gas: 0x3d090, // hex constant, as defined in EIP-4788 + Data: eip4788CreationData, IsSystemTransaction: false, - [Kroma: END] */ - SourceHash: beaconRootsSource.SourceHash(), + SourceHash: beaconRootsSource.SourceHash(), }).MarshalBinary() if err != nil { return nil, err diff --git a/op-node/rollup/derive/engine_consolidate.go b/op-node/rollup/derive/engine_consolidate.go index 121a74032..65f97c70d 100644 --- a/op-node/rollup/derive/engine_consolidate.go +++ b/op-node/rollup/derive/engine_consolidate.go @@ -41,7 +41,18 @@ func AttributesMatchBlock(rollupCfg *rollup.Config, attrs *eth.PayloadAttributes return fmt.Errorf("transaction count does not match. expected: %d. got: %d", len(attrs.Transactions), len(block.Transactions)) } for i, otx := range attrs.Transactions { - if expect := block.Transactions[i]; !bytes.Equal(otx, expect) { + // [Kroma: START] + // DepositTx in the RPC response does not come as KromaDepositTx, so it is converted to KromaDepositTx. + expect := block.Transactions[i] + if expect[0] == types.DepositTxType && !rollupCfg.IsKromaMPT(uint64(attrs.Timestamp)) { + var err error + expect, err = ToKromaDepositBytes(expect) + if err != nil { + return err + } + } + // [Kroma: END] + if !bytes.Equal(otx, expect) { if i == 0 { logL1InfoTxns(rollupCfg, l, uint64(block.BlockNumber), uint64(block.Timestamp), otx, block.Transactions[i]) } diff --git a/op-node/rollup/derive/engine_queue.go b/op-node/rollup/derive/engine_queue.go index 4dd4bebec..1e6ce5d56 100644 --- a/op-node/rollup/derive/engine_queue.go +++ b/op-node/rollup/derive/engine_queue.go @@ -99,7 +99,6 @@ type LocalEngineControl interface { // The safe head may advance by more than one block in a single update // The l1Block specified is the first L1 block that includes sufficient information to derive the new safe head type SafeHeadListener interface { - // Enabled reports if this safe head listener is actively using the posted data. This allows the engine queue to // optionally skip making calls that may be expensive to prepare. // Callbacks may still be made if Enabled returns false but are not guaranteed. diff --git a/op-node/rollup/derive/l1_block_info.go b/op-node/rollup/derive/l1_block_info.go index 864826aaf..3105a5658 100644 --- a/op-node/rollup/derive/l1_block_info.go +++ b/op-node/rollup/derive/l1_block_info.go @@ -324,22 +324,18 @@ func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber // Set a very large gas limit with `IsSystemTransaction` to ensure // that the L1 Attributes Transaction does not run out of gas. out := &types.DepositTx{ - SourceHash: source.SourceHash(), - From: L1InfoDepositerAddress, - To: &L1BlockAddress, - Mint: nil, - Value: big.NewInt(0), - Gas: 150_000_000, - /* [Kroma: START] + SourceHash: source.SourceHash(), + From: L1InfoDepositerAddress, + To: &L1BlockAddress, + Mint: nil, + Value: big.NewInt(0), + Gas: 150_000_000, IsSystemTransaction: true, - [Kroma: END] */ - Data: data, + Data: data, } // With the regolith fork we disable the IsSystemTx functionality, and allocate real gas if rollupCfg.IsRegolith(l2BlockTime) { - /* [Kroma: START] out.IsSystemTransaction = false - [Kroma: END] */ out.Gas = RegolithSystemTxGas } return out, nil @@ -356,5 +352,10 @@ func L1InfoDepositBytes(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNu if err != nil { return nil, fmt.Errorf("failed to encode L1 info tx: %w", err) } + // [Kroma: START] Use KromaDepositTx instead of DepositTx before MPT time + if !rollupCfg.IsKromaMPT(l2BlockTime) { + opaqueL1Tx, err = ToKromaDepositBytes(opaqueL1Tx) + } + // [Kroma: END] return opaqueL1Tx, nil } diff --git a/op-node/rollup/derive/l1_block_info_test.go b/op-node/rollup/derive/l1_block_info_test.go index ebfe974d2..4e8e1989b 100644 --- a/op-node/rollup/derive/l1_block_info_test.go +++ b/op-node/rollup/derive/l1_block_info_test.go @@ -117,9 +117,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { } depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, 0) require.NoError(t, err) - /* [Kroma: START] require.False(t, depTx.IsSystemTransaction) - [Kroma: END] */ require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) }) t.Run("ecotone", func(t *testing.T) { @@ -132,9 +130,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { } depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, 1) require.NoError(t, err) - /* [Kroma: START] require.False(t, depTx.IsSystemTransaction) - [Kroma: END] */ require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) require.Equal(t, L1InfoEcotoneLen, len(depTx.Data)) }) @@ -149,9 +145,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { } depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, 2) require.NoError(t, err) - /* [Kroma: START] require.False(t, depTx.IsSystemTransaction) - [Kroma: END] */ require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) require.Equal(t, L1InfoBedrockLen, len(depTx.Data)) }) @@ -166,9 +160,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { } depTx, err := L1InfoDeposit(&rollupCfg, randomL1Cfg(rng, info), randomSeqNr(rng), info, 0) require.NoError(t, err) - /* [Kroma: START] require.False(t, depTx.IsSystemTransaction) - [Kroma: END] */ require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) require.Equal(t, L1InfoEcotoneLen, len(depTx.Data)) }) diff --git a/op-service/testutils/deposits.go b/op-service/testutils/deposits.go index ae24177cf..dfe8d07f4 100644 --- a/op-service/testutils/deposits.go +++ b/op-service/testutils/deposits.go @@ -25,13 +25,14 @@ func GenerateDeposit(sourceHash common.Hash, rng *rand.Rand) *types.DepositTx { } dep := &types.DepositTx{ - SourceHash: sourceHash, - From: RandomAddress(rng), - To: to, - Value: RandomETH(rng, 200), - Gas: uint64(rng.Int63n(10 * 1e6)), // 10 M gas max - Data: data, - Mint: mint, + SourceHash: sourceHash, + From: RandomAddress(rng), + To: to, + Value: RandomETH(rng, 200), + Gas: uint64(rng.Int63n(10 * 1e6)), // 10 M gas max + Data: data, + IsSystemTransaction: false, + Mint: mint, } return dep } diff --git a/ops-devnet/docker-compose.yml b/ops-devnet/docker-compose.yml index e8eabb625..c01dc51fa 100644 --- a/ops-devnet/docker-compose.yml +++ b/ops-devnet/docker-compose.yml @@ -107,7 +107,7 @@ services: l2: pid: host # allow debugging - image: kromanetwork/geth:mpt-d6420143 + image: kromanetwork/geth:mpt-79ec5dfd ports: - "9545:8545" - "9546:8546" From 7e8fc98346a3308e3d78cbe630f278c61c3279d8 Mon Sep 17 00:00:00 2001 From: Pangsu Date: Fri, 19 Jul 2024 16:18:48 +0900 Subject: [PATCH 3/5] fix: resolve test failures due to different deposit tx types --- op-e2e/actions/ecotone_fork_test.go | 12 +++++ op-e2e/actions/l2_engine_test.go | 9 ++-- op-e2e/actions/l2_verifier.go | 14 +++++ op-e2e/actions/user.go | 12 ++++- op-e2e/bridge_test.go | 5 +- op-e2e/op_geth.go | 2 +- op-e2e/op_geth_test.go | 7 +++ op-e2e/system_adminrpc_test.go | 4 +- op-e2e/tx_helper.go | 3 ++ .../rollup/derive/attributes_queue_test.go | 52 ++++++++++++------- op-node/rollup/derive/attributes_test.go | 7 ++- .../client/l2/engineapi/l2_engine_api.go | 5 +- .../l2/engineapi/test/l2_engine_api_tests.go | 14 +++-- op-service/eth/types.go | 15 ++++-- .../scripts/go-ffi/differential-testing.go | 6 ++- packages/contracts/scripts/go-ffi/utils.go | 31 ++++++++--- 16 files changed, 149 insertions(+), 49 deletions(-) diff --git a/op-e2e/actions/ecotone_fork_test.go b/op-e2e/actions/ecotone_fork_test.go index 58ff227e4..d16fffbdf 100644 --- a/op-e2e/actions/ecotone_fork_test.go +++ b/op-e2e/actions/ecotone_fork_test.go @@ -91,6 +91,18 @@ func TestEcotoneNetworkUpgradeTransactions(gt *testing.T) { // See [derive.EcotoneNetworkUpgradeTransactions] require.Equal(t, 7, len(transactions)) + // [Kroma: START] Use KromaDepositTx instead of DepositTx + for i, otx := range transactions { + if otx.Type() == types.DepositTxType { + b, err := otx.MarshalBinary() + require.NoError(gt, err) + kromaDep, err := derive.ToKromaDepositBytes(b) + require.NoError(gt, err) + transactions[i].UnmarshalBinary(kromaDep) + } + } + // [Kroma: END] + l1Info, err := derive.L1BlockInfoFromBytes(sd.RollupCfg, latestBlock.Time(), transactions[0].Data()) require.NoError(t, err) require.Equal(t, derive.L1InfoBedrockLen, len(transactions[0].Data())) diff --git a/op-e2e/actions/l2_engine_test.go b/op-e2e/actions/l2_engine_test.go index 62b9c7464..5b4b9a62c 100644 --- a/op-e2e/actions/l2_engine_test.go +++ b/op-e2e/actions/l2_engine_test.go @@ -5,8 +5,6 @@ import ( "math/big" "testing" - "github.com/ethereum-optimism/optimism/op-service/client/l2/engineapi" - "github.com/ethereum-optimism/optimism/op-service/client/l2/engineapi/test" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" @@ -19,6 +17,9 @@ import ( "github.com/ethereum/go-ethereum/trie/triedb/hashdb" "github.com/stretchr/testify/require" + "github.com/ethereum-optimism/optimism/op-service/client/l2/engineapi" + "github.com/ethereum-optimism/optimism/op-service/client/l2/engineapi/test" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/sources" @@ -46,7 +47,7 @@ func TestL2EngineAPI(gt *testing.T) { chainA, _ := core.GenerateChain(sd.L2Cfg.Config, genesisBlock, consensus, db, 1, func(i int, gen *core.BlockGen) { gen.SetCoinbase(common.Address{'A'}) }) - payloadA, err := eth.BlockAsPayload(chainA[0], sd.RollupCfg.CanyonTime) + payloadA, err := eth.BlockAsPayload(chainA[0], sd.RollupCfg.CanyonTime, sd.RollupCfg.KromaMPTTime) require.NoError(t, err) // apply the payload @@ -69,7 +70,7 @@ func TestL2EngineAPI(gt *testing.T) { chainB, _ := core.GenerateChain(sd.L2Cfg.Config, genesisBlock, consensus, db, 1, func(i int, gen *core.BlockGen) { gen.SetCoinbase(common.Address{'B'}) }) - payloadB, err := eth.BlockAsPayload(chainB[0], sd.RollupCfg.CanyonTime) + payloadB, err := eth.BlockAsPayload(chainB[0], sd.RollupCfg.CanyonTime, sd.RollupCfg.KromaMPTTime) require.NoError(t, err) // apply the payload diff --git a/op-e2e/actions/l2_verifier.go b/op-e2e/actions/l2_verifier.go index e920add79..ac112d943 100644 --- a/op-e2e/actions/l2_verifier.go +++ b/op-e2e/actions/l2_verifier.go @@ -280,6 +280,13 @@ func (s *L2Verifier) ActL2PipelineFull(t Testing) { // ActL2UnsafeGossipReceive creates an action that can receive an unsafe execution payload, like gossipsub func (s *L2Verifier) ActL2UnsafeGossipReceive(payload *eth.ExecutionPayloadEnvelope) Action { return func(t Testing) { + // [Kroma: START] Use KromaDepositTx instead of DepositTx + for i, otx := range payload.ExecutionPayload.Transactions { + var err error + payload.ExecutionPayload.Transactions[i], err = derive.ToKromaDepositBytes(otx) + require.NoError(t, err) + } + // [Kroma: END] s.derivation.AddUnsafePayload(payload) } } @@ -287,6 +294,13 @@ func (s *L2Verifier) ActL2UnsafeGossipReceive(payload *eth.ExecutionPayloadEnvel // ActL2InsertUnsafePayload creates an action that can insert an unsafe execution payload func (s *L2Verifier) ActL2InsertUnsafePayload(payload *eth.ExecutionPayloadEnvelope) Action { return func(t Testing) { + // [Kroma: START] Use KromaDepositTx instead of DepositTx + for i, otx := range payload.ExecutionPayload.Transactions { + var err error + payload.ExecutionPayload.Transactions[i], err = derive.ToKromaDepositBytes(otx) + require.NoError(t, err) + } + // [Kroma: END] ref, err := derive.PayloadToBlockRef(s.rollupCfg, payload.ExecutionPayload) require.NoError(t, err) err = s.engine.InsertUnsafePayload(t.Ctx(), payload, ref) diff --git a/op-e2e/actions/user.go b/op-e2e/actions/user.go index 83c1e3c41..35161d19e 100644 --- a/op-e2e/actions/user.go +++ b/op-e2e/actions/user.go @@ -380,7 +380,17 @@ func (s *CrossLayerUser) CheckDepositTx(t Testing, l1TxHash common.Hash, index i require.Less(t, index, len(depositReceipt.Logs), "must have enough logs in receipt") reconstructedDep, err := derive.UnmarshalDepositLogEvent(depositReceipt.Logs[index]) require.NoError(t, err, "Could not reconstruct L2 Deposit") - l2Tx := types.NewTx(reconstructedDep) + // [Kroma: START] Use KromaDepositTx instead of DepositTx + l2Tx := types.NewTx(&types.KromaDepositTx{ + SourceHash: reconstructedDep.SourceHash, + From: reconstructedDep.From, + To: reconstructedDep.To, + Mint: reconstructedDep.Mint, + Value: reconstructedDep.Value, + Gas: reconstructedDep.Gas, + Data: reconstructedDep.Data, + }) + // [Kroma: END] s.L2.CheckReceipt(t, l2Success, l2Tx.Hash()) } } diff --git a/op-e2e/bridge_test.go b/op-e2e/bridge_test.go index e73915ba0..af9822d37 100644 --- a/op-e2e/bridge_test.go +++ b/op-e2e/bridge_test.go @@ -98,7 +98,10 @@ func TestERC20BridgeDeposits(t *testing.T) { depositTx, err := derive.UnmarshalDepositLogEvent(&depositEvent.Raw) require.NoError(t, err) - _, err = wait.ForReceiptOK(context.Background(), l2Client, types.NewTx(depositTx).Hash()) + // [Kroma: START] Use KromaDepositTx instead of DepositTx + kromaDepTx := types.NewTx(depositTx).ToKromaDepositTx() + _, err = wait.ForReceiptOK(context.Background(), l2Client, kromaDepTx.Hash()) + // [Kroma: END] require.NoError(t, err) // Ensure that the deposit went through diff --git a/op-e2e/op_geth.go b/op-e2e/op_geth.go index c9db7a468..2b2a9def1 100644 --- a/op-e2e/op_geth.go +++ b/op-e2e/op_geth.go @@ -111,7 +111,7 @@ func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, e l2Client, err := ethclient.Dial(selectEndpoint(node)) require.Nil(t, err) - genesisPayload, err := eth.BlockAsPayload(l2GenesisBlock, cfg.DeployConfig.CanyonTime(l2GenesisBlock.Time())) + genesisPayload, err := eth.BlockAsPayload(l2GenesisBlock, cfg.DeployConfig.CanyonTime(l2GenesisBlock.Time()), cfg.DeployConfig.KromaMPTTime(l2GenesisBlock.Time())) require.Nil(t, err) return &OpGeth{ diff --git a/op-e2e/op_geth_test.go b/op-e2e/op_geth_test.go index f0c7cafd6..0d1eb95d7 100644 --- a/op-e2e/op_geth_test.go +++ b/op-e2e/op_geth_test.go @@ -459,6 +459,13 @@ func TestRegolith(t *testing.T) { // L1Info tx should report actual gas used, not 0 or the tx gas limit infoTx, err := opGeth.L2Client.TransactionInBlock(ctx, envelope.ExecutionPayload.BlockHash, 0) require.NoError(t, err) + // [Kroma: START] Use KromaDepositTx instead of DepositTx + b, err := infoTx.MarshalBinary() + require.NoError(t, err) + kromaDep, err := derive.ToKromaDepositBytes(b) + require.NoError(t, err) + infoTx.UnmarshalBinary(kromaDep) + // [Kroma: END] infoRcpt, err := opGeth.L2Client.TransactionReceipt(ctx, infoTx.Hash()) require.NoError(t, err) require.NotZero(t, infoRcpt.GasUsed) diff --git a/op-e2e/system_adminrpc_test.go b/op-e2e/system_adminrpc_test.go index e7c0af673..dc28afeb7 100644 --- a/op-e2e/system_adminrpc_test.go +++ b/op-e2e/system_adminrpc_test.go @@ -198,7 +198,7 @@ func TestPostUnsafePayload(t *testing.T) { blockNumberOne, err := l2Seq.BlockByNumber(ctx, big.NewInt(1)) require.NoError(t, err) - payload, err := eth.BlockAsPayload(blockNumberOne, sys.RollupConfig.CanyonTime) + payload, err := eth.BlockAsPayload(blockNumberOne, sys.RollupConfig.CanyonTime, sys.RollupConfig.KromaMPTTime) require.NoError(t, err) err = rollupClient.PostUnsafePayload(ctx, ð.ExecutionPayloadEnvelope{ExecutionPayload: payload}) require.NoError(t, err) @@ -207,7 +207,7 @@ func TestPostUnsafePayload(t *testing.T) { // Test validation blockNumberTwo, err := l2Seq.BlockByNumber(ctx, big.NewInt(2)) require.NoError(t, err) - payload, err = eth.BlockAsPayload(blockNumberTwo, sys.RollupConfig.CanyonTime) + payload, err = eth.BlockAsPayload(blockNumberTwo, sys.RollupConfig.CanyonTime, sys.RollupConfig.KromaMPTTime) require.NoError(t, err) payload.BlockHash = common.Hash{0xaa} err = rollupClient.PostUnsafePayload(ctx, ð.ExecutionPayloadEnvelope{ExecutionPayload: payload}) diff --git a/op-e2e/tx_helper.go b/op-e2e/tx_helper.go index fd7c7eed4..4d0772db4 100644 --- a/op-e2e/tx_helper.go +++ b/op-e2e/tx_helper.go @@ -50,6 +50,9 @@ func SendDepositTx(t *testing.T, cfg SystemConfig, l1Client *ethclient.Client, l reconstructedDep, err := derive.UnmarshalDepositLogEvent(l1Receipt.Logs[0]) require.NoError(t, err, "Could not reconstruct L2 Deposit") tx = types.NewTx(reconstructedDep) + // [Kroma: START] Use KromaDepositTx instead of DepositTx + tx = tx.ToKromaDepositTx() + // [Kroma: END] l2Receipt, err := wait.ForReceipt(ctx, l2Client, tx.Hash(), l2Opts.ExpectedStatus) require.NoError(t, err, "Waiting for deposit tx on L2") return l2Receipt diff --git a/op-node/rollup/derive/attributes_queue_test.go b/op-node/rollup/derive/attributes_queue_test.go index 8257a5485..cd421fff1 100644 --- a/op-node/rollup/derive/attributes_queue_test.go +++ b/op-node/rollup/derive/attributes_queue_test.go @@ -32,11 +32,6 @@ func TestAttributesQueue(t *testing.T) { rng := rand.New(rand.NewSource(1234)) l1Info := testutils.RandomBlockInfo(rng) - l1Fetcher := &testutils.MockL1Source{} - defer l1Fetcher.AssertExpectations(t) - - l1Fetcher.ExpectInfoByHash(l1Info.InfoHash, l1Info, nil) - safeHead := testutils.RandomL2BlockRef(rng) safeHead.L1Origin = l1Info.ID() safeHead.Time = l1Info.InfoTime @@ -64,26 +59,43 @@ func TestAttributesQueue(t *testing.T) { ValidatorRewardScalar: [32]byte{}, } - l2Fetcher := &testutils.MockL2Client{} - l2Fetcher.ExpectSystemConfigByL2Hash(safeHead.Hash, parentL1Cfg, nil) - rollupCfg := rollup.Config{} l1InfoTx, err := L1InfoDepositBytes(&rollupCfg, expectedL1Cfg, safeHead.SequenceNumber+1, l1Info, 0) require.NoError(t, err) - attrs := eth.PayloadAttributes{ - Timestamp: eth.Uint64Quantity(safeHead.Time + cfg.BlockTime), - PrevRandao: eth.Bytes32(l1Info.InfoMixDigest), - SuggestedFeeRecipient: common.Address{}, - Transactions: []eth.Data{l1InfoTx, eth.Data("foobar"), eth.Data("example")}, - NoTxPool: true, - GasLimit: (*eth.Uint64Quantity)(&expectedL1Cfg.GasLimit), + + testAttributes := func(l1InfoTx []byte) { + l1Fetcher := &testutils.MockL1Source{} + defer l1Fetcher.AssertExpectations(t) + l1Fetcher.ExpectInfoByHash(l1Info.InfoHash, l1Info, nil) + l2Fetcher := &testutils.MockL2Client{} + l2Fetcher.ExpectSystemConfigByL2Hash(safeHead.Hash, parentL1Cfg, nil) + + attrs := eth.PayloadAttributes{ + Timestamp: eth.Uint64Quantity(safeHead.Time + cfg.BlockTime), + PrevRandao: eth.Bytes32(l1Info.InfoMixDigest), + SuggestedFeeRecipient: common.Address{}, + Transactions: []eth.Data{l1InfoTx, eth.Data("foobar"), eth.Data("example")}, + NoTxPool: true, + GasLimit: (*eth.Uint64Quantity)(&expectedL1Cfg.GasLimit), + } + attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l2Fetcher) + + aq := NewAttributesQueue(testlog.Logger(t, log.LevelError), cfg, attrBuilder, nil) + + actual, err := aq.createNextAttributes(context.Background(), &batch, safeHead) + + require.NoError(t, err) + require.Equal(t, attrs, *actual) } - attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l2Fetcher) - aq := NewAttributesQueue(testlog.Logger(t, log.LevelError), cfg, attrBuilder, nil) + t.Run("before kroma mpt time", func(st *testing.T) { + testAttributes(l1InfoTx) + }) - actual, err := aq.createNextAttributes(context.Background(), &batch, safeHead) + t.Run("after kroma mpt time", func(st *testing.T) { + zero := uint64(0) + rollupCfg.KromaMPTTime = &zero - require.NoError(t, err) - require.Equal(t, attrs, *actual) + testAttributes(l1InfoTx) + }) } diff --git a/op-node/rollup/derive/attributes_test.go b/op-node/rollup/derive/attributes_test.go index db2b76a6d..898fd2fa3 100644 --- a/op-node/rollup/derive/attributes_test.go +++ b/op-node/rollup/derive/attributes_test.go @@ -153,7 +153,12 @@ func TestPreparePayloadAttributes(t *testing.T) { require.NoError(t, err) l2Txs := append(append(make([]eth.Data, 0), l1InfoTx), usedDepositTxs...) - + // [Kroma: START] Use KromaDepositTx instead of DepositTx + for i, otx := range l2Txs { + l2Txs[i], err = ToKromaDepositBytes(otx) + require.NoError(t, err) + } + // [Kroma: END] l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, receipts, nil) attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher) attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch) diff --git a/op-service/client/l2/engineapi/l2_engine_api.go b/op-service/client/l2/engineapi/l2_engine_api.go index e17c42d72..703a41bce 100644 --- a/op-service/client/l2/engineapi/l2_engine_api.go +++ b/op-service/client/l2/engineapi/l2_engine_api.go @@ -9,7 +9,6 @@ import ( "math/big" "time" - "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" @@ -20,6 +19,8 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" + + "github.com/ethereum-optimism/optimism/op-service/eth" ) type EngineBackend interface { @@ -316,7 +317,7 @@ func (ea *L2EngineAPI) getPayload(_ context.Context, payloadId eth.PayloadID) (* return nil, engine.UnknownPayload } - return eth.BlockAsPayloadEnv(bl, ea.config().CanyonTime) + return eth.BlockAsPayloadEnv(bl, ea.config().CanyonTime, ea.config().KromaMPTTime) } func (ea *L2EngineAPI) forkchoiceUpdated(_ context.Context, state *eth.ForkchoiceState, attr *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error) { diff --git a/op-service/client/l2/engineapi/test/l2_engine_api_tests.go b/op-service/client/l2/engineapi/test/l2_engine_api_tests.go index 65925232d..82bbafb0f 100644 --- a/op-service/client/l2/engineapi/test/l2_engine_api_tests.go +++ b/op-service/client/l2/engineapi/test/l2_engine_api_tests.go @@ -4,16 +4,17 @@ import ( "context" "testing" - "github.com/ethereum-optimism/optimism/op-node/rollup" - "github.com/ethereum-optimism/optimism/op-node/rollup/derive" - "github.com/ethereum-optimism/optimism/op-service/client/l2/engineapi" - "github.com/ethereum-optimism/optimism/op-service/eth" - "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-service/client/l2/engineapi" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/testlog" ) var gasLimit = eth.Uint64Quantity(30_000_000) @@ -38,6 +39,9 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi. txData, err := derive.L1InfoDeposit(rollupCfg, eth.SystemConfig{}, 1, eth.HeaderBlockInfo(genesis), 0) api.assert.NoError(err) tx := types.NewTx(txData) + // [Kroma: START] Use KromaDepositTx instead of DepositTx + tx = tx.ToKromaDepositTx() + // [Kroma: END] block := api.addBlock(tx) api.assert.Equal(block.BlockHash, api.headHash(), "should create and import new block") imported := api.backend.GetBlockByHash(block.BlockHash) diff --git a/op-service/eth/types.go b/op-service/eth/types.go index 199139ef0..2fac3c57f 100644 --- a/op-service/eth/types.go +++ b/op-service/eth/types.go @@ -249,7 +249,7 @@ func (envelope *ExecutionPayloadEnvelope) CheckBlockHash() (actual common.Hash, return blockHash, blockHash == payload.BlockHash } -func BlockAsPayload(bl *types.Block, canyonForkTime *uint64) (*ExecutionPayload, error) { +func BlockAsPayload(bl *types.Block, canyonForkTime, mptForkTime *uint64) (*ExecutionPayload, error) { baseFee, overflow := uint256.FromBig(bl.BaseFee()) if overflow { return nil, fmt.Errorf("invalid base fee in block: %s", bl.BaseFee()) @@ -261,6 +261,15 @@ func BlockAsPayload(bl *types.Block, canyonForkTime *uint64) (*ExecutionPayload, return nil, fmt.Errorf("tx %d failed to marshal: %w", i, err) } opaqueTxs[i] = otx + // [Kroma: START] Use KromaDepositTx instead DepositTx + if tx.Type() == types.DepositTxType && (mptForkTime == nil || bl.Time() <= *mptForkTime) { + otx, err := tx.ToKromaDepositTx().MarshalBinary() + if err != nil { + return nil, fmt.Errorf("tx %d failed to convert to KromaDepositTx: %w", i, err) + } + opaqueTxs[i] = otx + } + // [Kroma: END] } payload := &ExecutionPayload{ @@ -289,8 +298,8 @@ func BlockAsPayload(bl *types.Block, canyonForkTime *uint64) (*ExecutionPayload, return payload, nil } -func BlockAsPayloadEnv(bl *types.Block, canyonForkTime *uint64) (*ExecutionPayloadEnvelope, error) { - payload, err := BlockAsPayload(bl, canyonForkTime) +func BlockAsPayloadEnv(bl *types.Block, canyonForkTime, mptForkTime *uint64) (*ExecutionPayloadEnvelope, error) { + payload, err := BlockAsPayload(bl, canyonForkTime, mptForkTime) if err != nil { return nil, err } diff --git a/packages/contracts/scripts/go-ffi/differential-testing.go b/packages/contracts/scripts/go-ffi/differential-testing.go index 6e2bf03fa..55a056923 100644 --- a/packages/contracts/scripts/go-ffi/differential-testing.go +++ b/packages/contracts/scripts/go-ffi/differential-testing.go @@ -176,9 +176,10 @@ func DiffTestUtils() { // Create deposit transaction depositTx := makeDepositTx(from, to, value, mint, gasLimit, false, data, l1BlockHash, logIndex) + kromaDepTx := toKromaDepositTx(depositTx) // RLP encode deposit transaction - encoded, err := types.NewTx(&depositTx).MarshalBinary() + encoded, err := types.NewTx(&kromaDepTx).MarshalBinary() checkErr(err, "Error encoding deposit transaction") // Hash encoded deposit transaction @@ -206,9 +207,10 @@ func DiffTestUtils() { checkOk(ok) depositTx := makeDepositTx(from, to, value, mint, gasLimit, isCreate, data, l1BlockHash, logIndex) + kromaDepTx := toKromaDepositTx(depositTx) // RLP encode deposit transaction - encoded, err := types.NewTx(&depositTx).MarshalBinary() + encoded, err := types.NewTx(&kromaDepTx).MarshalBinary() checkErr(err, "Failed to RLP encode deposit transaction") // Pack rlp encoded deposit transaction packed, err := bytesArgs.Pack(&encoded) diff --git a/packages/contracts/scripts/go-ffi/utils.go b/packages/contracts/scripts/go-ffi/utils.go index ace626f6d..4ba465e2c 100644 --- a/packages/contracts/scripts/go-ffi/utils.go +++ b/packages/contracts/scripts/go-ffi/utils.go @@ -117,14 +117,12 @@ func makeDepositTx( // Create deposit transaction depositTx := types.DepositTx{ - SourceHash: udp.SourceHash(), - From: from, - Value: value, - Gas: gasLimit.Uint64(), - /* [Kroma: START] + SourceHash: udp.SourceHash(), + From: from, + Value: value, + Gas: gasLimit.Uint64(), IsSystemTransaction: false, // This will never be a system transaction in the tests. - [Kroma: END] */ - Data: data, + Data: data, } // Fill optional fields @@ -138,6 +136,25 @@ func makeDepositTx( return depositTx } +func toKromaDepositTx(depTx types.DepositTx) types.KromaDepositTx { + kromaDepTx := types.KromaDepositTx{ + SourceHash: depTx.SourceHash, + From: depTx.From, + Value: new(big.Int).Set(depTx.Value), + Gas: depTx.Gas, + Data: depTx.Data, + } + // Fill optional fields + if depTx.Mint != nil { + kromaDepTx.Mint = new(big.Int).Set(depTx.Mint) + } + if depTx.To != nil { + to := *depTx.To + kromaDepTx.To = &to + } + return kromaDepTx +} + // Custom type to write the generated proof to type proofList [][]byte From 3473ad121a5485e5ff5499da702589e11ce86458 Mon Sep 17 00:00:00 2001 From: Pangsu Date: Wed, 17 Jul 2024 17:21:16 +0900 Subject: [PATCH 4/5] feat: set suggested fee recipient for payload attributes --- op-node/rollup/derive/attributes.go | 8 +++++- .../rollup/derive/attributes_queue_test.go | 27 ++++++++++++------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/op-node/rollup/derive/attributes.go b/op-node/rollup/derive/attributes.go index fce69e197..6df0712c3 100644 --- a/op-node/rollup/derive/attributes.go +++ b/op-node/rollup/derive/attributes.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/kroma-network/kroma/kroma-bindings/predeploys" ) // L1ReceiptsFetcher fetches L1 header info and receipts for the payload attributes derivation (the info tx and deposits) @@ -148,10 +149,15 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex } } + suggestedFeeRecipient := common.Address{} + if ba.rollupCfg.IsKromaMPT(nextL2Time) { + suggestedFeeRecipient = predeploys.ProtocolVaultAddr + } + return ð.PayloadAttributes{ Timestamp: hexutil.Uint64(nextL2Time), PrevRandao: eth.Bytes32(l1Info.MixDigest()), - SuggestedFeeRecipient: common.Address{}, + SuggestedFeeRecipient: suggestedFeeRecipient, Transactions: txs, NoTxPool: true, GasLimit: (*eth.Uint64Quantity)(&sysConfig.GasLimit), diff --git a/op-node/rollup/derive/attributes_queue_test.go b/op-node/rollup/derive/attributes_queue_test.go index cd421fff1..45bf49dbb 100644 --- a/op-node/rollup/derive/attributes_queue_test.go +++ b/op-node/rollup/derive/attributes_queue_test.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum-optimism/optimism/kroma-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/testlog" @@ -59,11 +60,7 @@ func TestAttributesQueue(t *testing.T) { ValidatorRewardScalar: [32]byte{}, } - rollupCfg := rollup.Config{} - l1InfoTx, err := L1InfoDepositBytes(&rollupCfg, expectedL1Cfg, safeHead.SequenceNumber+1, l1Info, 0) - require.NoError(t, err) - - testAttributes := func(l1InfoTx []byte) { + testAttributes := func(l1InfoTx []byte, suggestedFeeRecipient common.Address) { l1Fetcher := &testutils.MockL1Source{} defer l1Fetcher.AssertExpectations(t) l1Fetcher.ExpectInfoByHash(l1Info.InfoHash, l1Info, nil) @@ -73,7 +70,7 @@ func TestAttributesQueue(t *testing.T) { attrs := eth.PayloadAttributes{ Timestamp: eth.Uint64Quantity(safeHead.Time + cfg.BlockTime), PrevRandao: eth.Bytes32(l1Info.InfoMixDigest), - SuggestedFeeRecipient: common.Address{}, + SuggestedFeeRecipient: suggestedFeeRecipient, Transactions: []eth.Data{l1InfoTx, eth.Data("foobar"), eth.Data("example")}, NoTxPool: true, GasLimit: (*eth.Uint64Quantity)(&expectedL1Cfg.GasLimit), @@ -89,13 +86,23 @@ func TestAttributesQueue(t *testing.T) { } t.Run("before kroma mpt time", func(st *testing.T) { - testAttributes(l1InfoTx) + rollupCfg := rollup.Config{} + l1InfoTx, err := L1InfoDepositBytes(&rollupCfg, expectedL1Cfg, safeHead.SequenceNumber+1, l1Info, 0) + require.NoError(t, err) + + l1InfoTx, err = ToKromaDepositBytes(l1InfoTx) + require.NoError(st, err) + testAttributes(l1InfoTx, common.Address{}) }) t.Run("after kroma mpt time", func(st *testing.T) { zero := uint64(0) - rollupCfg.KromaMPTTime = &zero - - testAttributes(l1InfoTx) + cfg.KromaMPTTime = &zero + rollupCfg := rollup.Config{ + KromaMPTTime: &zero, + } + l1InfoTx, err := L1InfoDepositBytes(&rollupCfg, expectedL1Cfg, safeHead.SequenceNumber+1, l1Info, 0) + require.NoError(t, err) + testAttributes(l1InfoTx, predeploys.ProtocolVaultAddr) }) } From 1737b2baeb51b20f4dd223c5dee1cd3832bf3af8 Mon Sep 17 00:00:00 2001 From: 0xbenyun Date: Tue, 5 Nov 2024 13:45:47 +0900 Subject: [PATCH 5/5] feat(batcher): send batcher transaction right before KromaMPTTime --- op-batcher/batcher/channel_builder.go | 6 ++ op-batcher/batcher/channel_manager_test.go | 65 ++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/op-batcher/batcher/channel_builder.go b/op-batcher/batcher/channel_builder.go index b55071f46..c14934459 100644 --- a/op-batcher/batcher/channel_builder.go +++ b/op-batcher/batcher/channel_builder.go @@ -20,6 +20,9 @@ var ( ErrChannelTimeoutClose = errors.New("close to channel timeout") ErrSeqWindowClose = errors.New("close to sequencer window timeout") ErrTerminated = errors.New("channel terminated") + // [Kroma: START] + ErrJustBeforeKromaMPTTime = errors.New("reached the block just before KromaMPTTime") + // [Kroma: END] ) type ChannelFullError struct { @@ -174,7 +177,10 @@ func (c *ChannelBuilder) AddBlock(block *types.Block) (*derive.L1BlockInfo, erro if err = c.co.FullErr(); err != nil { c.setFullErr(err) // Adding this block still worked, so don't return error, just mark as full + } else /* [Kroma: START] */ if c.rollupCfg.KromaMPTTime != nil && block.Time() == *c.rollupCfg.KromaMPTTime-c.rollupCfg.BlockTime { + c.setFullErr(ErrJustBeforeKromaMPTTime) } + // [Kroma: END] return l1info, nil } diff --git a/op-batcher/batcher/channel_manager_test.go b/op-batcher/batcher/channel_manager_test.go index e029e69e6..a3a638090 100644 --- a/op-batcher/batcher/channel_manager_test.go +++ b/op-batcher/batcher/channel_manager_test.go @@ -483,3 +483,68 @@ func TestChannelManager_ChannelCreation(t *testing.T) { }) } } + +// [Kroma: START] +func TestChannelManager_Close_BeforeMPTBlock(t *testing.T) { + require := require.New(t) + + rng := rand.New(rand.NewSource(123)) + log := testlog.Logger(t, log.LevelError) + + cfg := defaultTestChannelConfig() + cfg.InitNoneCompressor() + cfg.BatchType = derive.SpanBatchType + rollupConfig := defaultTestRollupConfig + rollupConfig.BlockTime = 2 + kromaMPTTime := uint64(1732200100) + rollupConfig.KromaMPTTime = &kromaMPTTime + m := NewChannelManager(log, metrics.NoopMetrics, cfg, &rollupConfig) + m.Clear(eth.BlockID{}) + + numTx := 1 + zktBlock1 := derivetest.RandomL2BlockWithChainId(rng, numTx, defaultTestRollupConfig.L2ChainID) + zktBlock2 := derivetest.RandomL2BlockWithChainId(rng, numTx, defaultTestRollupConfig.L2ChainID) + mptBlock := derivetest.RandomL2BlockWithChainId(rng, numTx, defaultTestRollupConfig.L2ChainID) + + h := zktBlock1.Header() + h.Time = kromaMPTTime - rollupConfig.BlockTime*2 + zktBlock1 = zktBlock1.WithSeal(h) + + h = zktBlock2.Header() + h.Number = new(big.Int).Add(zktBlock1.Number(), big.NewInt(1)) + h.Time = zktBlock1.Time() + rollupConfig.BlockTime + h.ParentHash = zktBlock1.Hash() + zktBlock2 = zktBlock2.WithSeal(h) + + h = mptBlock.Header() + h.Number = new(big.Int).Add(zktBlock2.Number(), big.NewInt(1)) + h.Time = zktBlock2.Time() + rollupConfig.BlockTime + h.ParentHash = zktBlock2.Hash() + mptBlock = mptBlock.WithSeal(h) + + require.NoError(m.AddL2Block(zktBlock1), "adding 1st L2 block") + require.NoError(m.AddL2Block(zktBlock2), "adding 2nd L2 block") + require.NoError(m.AddL2Block(mptBlock), "adding 3nd L2 block") + + txdata, err := m.TxData(eth.BlockID{}) + require.NoError(err) + ch1 := m.txChannels[txdata.ID().String()] + require.ErrorIs(ch1.FullErr(), ErrJustBeforeKromaMPTTime) + + // current channel is not full yet + _, err = m.TxData(eth.BlockID{}) + require.ErrorIs(err, io.EOF) + + // Close immediately marks the channel as full with an ErrTerminated + // if the channel is not already full. + m.currentChannel.Close() + err = m.outputFrames() + require.NoError(err) + txdata, err = m.TxData(eth.BlockID{}) + require.NoError(err) + ch2 := m.txChannels[txdata.ID().String()] + require.ErrorIs(ch2.FullErr(), ErrTerminated) + require.NotEqual(ch1.ID(), ch2.ID()) +} + +// [Kroma: END]