Skip to content

Commit

Permalink
feat: handle mpt transition time
Browse files Browse the repository at this point in the history
  • Loading branch information
Pangssu committed Nov 9, 2024
1 parent a820693 commit 3a037f8
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 30 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down
10 changes: 4 additions & 6 deletions kroma-chain-ops/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -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: &params.KromaConfig{
EIP1559Denominator: eip1559Denom,
Expand All @@ -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 {
Expand Down
16 changes: 3 additions & 13 deletions op-e2e/actions/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ 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 {
regolithTime *hexutil.Uint64
canyonTime *hexutil.Uint64
deltaTime *hexutil.Uint64
ecotoneTime *hexutil.Uint64
kromaMPTTime *hexutil.Uint64
fjordTime *hexutil.Uint64
runToFork string
}
Expand All @@ -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":
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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]
Expand All @@ -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)
Expand Down
264 changes: 264 additions & 0 deletions op-e2e/migration_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
4 changes: 3 additions & 1 deletion op-node/rollup/derive/engine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Loading

0 comments on commit 3a037f8

Please sign in to comment.