From 95a1cb9377d557b47810baed5fffbcab496bb2fa Mon Sep 17 00:00:00 2001
From: Julien Robert
Date: Fri, 23 May 2025 16:01:30 +0200
Subject: [PATCH 1/8] feat(server): add migration command
---
server/{commands.go => init_cmd.go} | 0
server/migration_cmd.go | 223 ++++++++++++++++++++++++++++
2 files changed, 223 insertions(+)
rename server/{commands.go => init_cmd.go} (100%)
create mode 100644 server/migration_cmd.go
diff --git a/server/commands.go b/server/init_cmd.go
similarity index 100%
rename from server/commands.go
rename to server/init_cmd.go
diff --git a/server/migration_cmd.go b/server/migration_cmd.go
new file mode 100644
index 00000000..417ddb56
--- /dev/null
+++ b/server/migration_cmd.go
@@ -0,0 +1,223 @@
+package server
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "path/filepath"
+
+ dbm "github.com/cometbft/cometbft-db"
+ abci "github.com/cometbft/cometbft/abci/types"
+ cometbftcmd "github.com/cometbft/cometbft/cmd/cometbft/commands"
+ cfg "github.com/cometbft/cometbft/config"
+ "github.com/cometbft/cometbft/libs/os"
+ cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
+ "github.com/cometbft/cometbft/state"
+ "github.com/cometbft/cometbft/store"
+ cometbfttypes "github.com/cometbft/cometbft/types"
+ rollkitstore "github.com/rollkit/rollkit/pkg/store"
+ rollkittypes "github.com/rollkit/rollkit/types"
+ "github.com/spf13/cobra"
+)
+
+// MigrateToRollkitCmd returns a command that migrates the data from the CometBFT chain to Rollkit.
+func MigrateToRollkitCmd() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "rollkit-migration",
+ Short: "Migrate the data from the CometBFT chain to Rollkit",
+ Long: "Migrate the data from the CometBFT chain to Rollkit. This command should be used to migrate nodes or the sequencer.",
+ Args: cobra.ExactArgs(0),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ config, err := cometbftcmd.ParseConfig(cmd)
+ if err != nil {
+ return err
+ }
+
+ cometBlockStore, cometStateStore, err := loadStateAndBlockStore(config)
+ if err != nil {
+ return err
+ }
+
+ cometBFTstate, err := cometStateStore.Load()
+ if err != nil {
+ return err
+ }
+
+ lastBlockHeight := cometBFTstate.LastBlockHeight
+
+ cmd.Printf("Last block height: %d\n", lastBlockHeight)
+
+ rollkitStore, err := loadRollkitStateStore(config.RootDir, config.DBPath)
+ if err != nil {
+ return err
+ }
+
+ rollkitState, err := rollkitStateFromCometBFTState(cometBFTstate)
+ if err != nil {
+ return err
+ }
+
+ if err = rollkitStore.UpdateState(context.Background(), rollkitState); err != nil {
+ return err
+ }
+
+ for height := lastBlockHeight; height > 0; height-- {
+ block := cometBlockStore.LoadBlock(height)
+ header, data, signature := cometBlockToRollkit(block)
+
+ if err = rollkitStore.SaveBlockData(context.Background(), header, data, &signature); err != nil {
+ return err
+ }
+
+ // Only save extended commit info if vote extensions are enabled
+ if cometBFTstate.ConsensusParams.ABCI.VoteExtensionsEnabled(block.Height) {
+ extendedCommit := cometBlockStore.LoadBlockExtendedCommit(lastBlockHeight)
+
+ extendedCommitInfo := abci.ExtendedCommitInfo{
+ Round: extendedCommit.Round,
+ }
+
+ for _, vote := range extendedCommit.ToExtendedVoteSet("", cometBFTstate.LastValidators).List() {
+ power := int64(0)
+ for _, v := range cometBFTstate.LastValidators.Validators {
+ if bytes.Equal(v.Address.Bytes(), vote.ValidatorAddress) {
+ power = v.VotingPower
+ break
+ }
+ }
+
+ extendedCommitInfo.Votes = append(extendedCommitInfo.Votes, abci.ExtendedVoteInfo{
+ Validator: abci.Validator{
+ Address: vote.ValidatorAddress,
+ Power: power,
+ },
+ VoteExtension: vote.Extension,
+ ExtensionSignature: vote.ExtensionSignature,
+ BlockIdFlag: cmtproto.BlockIDFlag(vote.CommitSig().BlockIDFlag),
+ })
+ }
+
+ // rollkitStore.SaveExtendedCommit(context.Background(), header.Height(), &extendedCommitInfo)
+ panic("Saving extended commit info is not implemented yet")
+ }
+
+ cmd.Println("Block", height, "migrated")
+ }
+
+ cmd.Println("Migration completed successfully")
+ return rollkitStore.Close()
+ },
+ }
+ return cmd
+}
+
+// cometBlockToRollkit converts a cometBFT block to a rollkit block
+func cometBlockToRollkit(block *cometbfttypes.Block) (*rollkittypes.SignedHeader, *rollkittypes.Data, rollkittypes.Signature) {
+ var (
+ header *rollkittypes.SignedHeader
+ data *rollkittypes.Data
+ signature rollkittypes.Signature
+ )
+
+ // find proposer signature
+ for _, sig := range block.LastCommit.Signatures {
+ if bytes.Equal(sig.ValidatorAddress.Bytes(), block.ProposerAddress.Bytes()) {
+ signature = sig.Signature
+ break
+ }
+ }
+
+ header = &rollkittypes.SignedHeader{
+ Header: rollkittypes.Header{
+ BaseHeader: rollkittypes.BaseHeader{
+ Height: uint64(block.Height),
+ Time: uint64(block.Time.UnixNano()),
+ ChainID: block.ChainID,
+ },
+ Version: rollkittypes.Version{
+ Block: block.Version.Block,
+ App: block.Version.App,
+ },
+ LastHeaderHash: block.LastCommitHash.Bytes(),
+ LastCommitHash: block.LastCommitHash.Bytes(),
+ DataHash: block.DataHash.Bytes(),
+ ConsensusHash: block.ConsensusHash.Bytes(),
+ AppHash: block.AppHash.Bytes(),
+ LastResultsHash: block.LastResultsHash.Bytes(),
+ ValidatorHash: block.ValidatorsHash.Bytes(),
+ ProposerAddress: block.ProposerAddress.Bytes(),
+ },
+ Signature: signature, // TODO: figure out this.
+ }
+
+ data = &rollkittypes.Data{
+ Metadata: &rollkittypes.Metadata{
+ ChainID: block.ChainID,
+ Height: uint64(block.Height),
+ Time: uint64(block.Time.UnixNano()),
+ LastDataHash: block.DataHash.Bytes(),
+ },
+ }
+
+ for _, tx := range block.Data.Txs {
+ data.Txs = append(data.Txs, rollkittypes.Tx(tx))
+ }
+ return header, data, signature
+}
+
+func loadStateAndBlockStore(config *cfg.Config) (*store.BlockStore, state.Store, error) {
+ dbType := dbm.BackendType(config.DBBackend)
+
+ if !os.FileExists(filepath.Join(config.DBDir(), "blockstore.db")) {
+ return nil, nil, fmt.Errorf("no blockstore found in %v", config.DBDir())
+ }
+
+ // Get BlockStore
+ blockStoreDB, err := dbm.NewDB("blockstore", dbType, config.DBDir())
+ if err != nil {
+ return nil, nil, err
+ }
+ blockStore := store.NewBlockStore(blockStoreDB)
+
+ if !os.FileExists(filepath.Join(config.DBDir(), "state.db")) {
+ return nil, nil, fmt.Errorf("no statestore found in %v", config.DBDir())
+ }
+
+ // Get StateStore
+ stateDB, err := dbm.NewDB("state", dbType, config.DBDir())
+ if err != nil {
+ return nil, nil, err
+ }
+ stateStore := state.NewStore(stateDB, state.StoreOptions{
+ DiscardABCIResponses: config.Storage.DiscardABCIResponses,
+ })
+
+ return blockStore, stateStore, nil
+}
+
+func loadRollkitStateStore(rootDir, dbPath string) (rollkitstore.Store, error) {
+ baseKV, err := rollkitstore.NewDefaultKVStore(rootDir, dbPath, "rollkit")
+ if err != nil {
+ return nil, err
+ }
+
+ return rollkitstore.New(baseKV), nil
+}
+
+func rollkitStateFromCometBFTState(cometBFTState state.State) (rollkittypes.State, error) {
+ return rollkittypes.State{
+ Version: rollkittypes.Version{
+ Block: cometBFTState.Version.Consensus.Block,
+ App: cometBFTState.Version.Consensus.App,
+ },
+ ChainID: cometBFTState.ChainID,
+ InitialHeight: uint64(cometBFTState.LastBlockHeight), // The initial height is the migration height
+ LastBlockHeight: uint64(cometBFTState.LastBlockHeight),
+ LastBlockTime: cometBFTState.LastBlockTime,
+
+ DAHeight: 1, // TODO: set this to the correct value
+
+ LastResultsHash: cometBFTState.LastResultsHash,
+ AppHash: cometBFTState.AppHash,
+ }, nil
+}
From c73a674e9f7d8e52500c290a855b35a1ba86a998 Mon Sep 17 00:00:00 2001
From: Julien Robert
Date: Fri, 23 May 2025 16:11:08 +0200
Subject: [PATCH 2/8] lint
---
server/migration_cmd.go | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/server/migration_cmd.go b/server/migration_cmd.go
index 417ddb56..2e1d4938 100644
--- a/server/migration_cmd.go
+++ b/server/migration_cmd.go
@@ -15,9 +15,10 @@ import (
"github.com/cometbft/cometbft/state"
"github.com/cometbft/cometbft/store"
cometbfttypes "github.com/cometbft/cometbft/types"
+ "github.com/spf13/cobra"
+
rollkitstore "github.com/rollkit/rollkit/pkg/store"
rollkittypes "github.com/rollkit/rollkit/types"
- "github.com/spf13/cobra"
)
// MigrateToRollkitCmd returns a command that migrates the data from the CometBFT chain to Rollkit.
@@ -159,7 +160,7 @@ func cometBlockToRollkit(block *cometbfttypes.Block) (*rollkittypes.SignedHeader
},
}
- for _, tx := range block.Data.Txs {
+ for _, tx := range block.Txs {
data.Txs = append(data.Txs, rollkittypes.Tx(tx))
}
return header, data, signature
From e255af523ae92a83c4628b52b5898d6a725e2be1 Mon Sep 17 00:00:00 2001
From: Julien Robert
Date: Fri, 20 Jun 2025 14:50:46 +0200
Subject: [PATCH 3/8] updates
---
go.mod | 2 +-
server/migration_cmd.go | 14 +++++++++++---
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/go.mod b/go.mod
index 4a1ae45a..909508c6 100644
--- a/go.mod
+++ b/go.mod
@@ -16,6 +16,7 @@ require (
cosmossdk.io/math v1.5.3
cosmossdk.io/store v1.1.2
github.com/cometbft/cometbft v0.38.17
+ github.com/cometbft/cometbft-db v0.14.1
github.com/cosmos/cosmos-proto v1.0.0-beta.5
github.com/cosmos/cosmos-sdk v0.50.13
github.com/cosmos/gogoproto v1.7.0
@@ -87,7 +88,6 @@ require (
github.com/cockroachdb/pebble v1.1.2 // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
- github.com/cometbft/cometbft-db v0.14.1 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
diff --git a/server/migration_cmd.go b/server/migration_cmd.go
index 2e1d4938..27af6371 100644
--- a/server/migration_cmd.go
+++ b/server/migration_cmd.go
@@ -52,17 +52,23 @@ func MigrateToRollkitCmd() *cobra.Command {
if err != nil {
return err
}
+ defer rollkitStore.Close()
rollkitState, err := rollkitStateFromCometBFTState(cometBFTstate)
if err != nil {
return err
}
- if err = rollkitStore.UpdateState(context.Background(), rollkitState); err != nil {
+ if err = rollkitStore.UpdateState(
+ context.Background(), rollkitState,
+ ); err != nil {
return err
}
+ // migrate all the blocks from the cometBFT block store to the rollkit store
for height := lastBlockHeight; height > 0; height-- {
+ cmd.Printf("Migrating block %d...\n", height)
+
block := cometBlockStore.LoadBlock(height)
header, data, signature := cometBlockToRollkit(block)
@@ -71,7 +77,7 @@ func MigrateToRollkitCmd() *cobra.Command {
}
// Only save extended commit info if vote extensions are enabled
- if cometBFTstate.ConsensusParams.ABCI.VoteExtensionsEnabled(block.Height) {
+ if enabled := cometBFTstate.ConsensusParams.ABCI.VoteExtensionsEnabled(block.Height); enabled {
extendedCommit := cometBlockStore.LoadBlockExtendedCommit(lastBlockHeight)
extendedCommitInfo := abci.ExtendedCommitInfo{
@@ -98,6 +104,7 @@ func MigrateToRollkitCmd() *cobra.Command {
})
}
+ _ = extendedCommitInfo
// rollkitStore.SaveExtendedCommit(context.Background(), header.Height(), &extendedCommitInfo)
panic("Saving extended commit info is not implemented yet")
}
@@ -106,7 +113,7 @@ func MigrateToRollkitCmd() *cobra.Command {
}
cmd.Println("Migration completed successfully")
- return rollkitStore.Close()
+ return nil
},
}
return cmd
@@ -163,6 +170,7 @@ func cometBlockToRollkit(block *cometbfttypes.Block) (*rollkittypes.SignedHeader
for _, tx := range block.Txs {
data.Txs = append(data.Txs, rollkittypes.Tx(tx))
}
+
return header, data, signature
}
From 899ab6f4bbd0668e8e68b456652c36c9e6a7be33 Mon Sep 17 00:00:00 2001
From: Julien Robert
Date: Fri, 20 Jun 2025 14:57:18 +0200
Subject: [PATCH 4/8] updates
---
server/migration_cmd.go | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/server/migration_cmd.go b/server/migration_cmd.go
index 27af6371..1fd9b15e 100644
--- a/server/migration_cmd.go
+++ b/server/migration_cmd.go
@@ -52,7 +52,6 @@ func MigrateToRollkitCmd() *cobra.Command {
if err != nil {
return err
}
- defer rollkitStore.Close()
rollkitState, err := rollkitStateFromCometBFTState(cometBFTstate)
if err != nil {
@@ -113,7 +112,7 @@ func MigrateToRollkitCmd() *cobra.Command {
}
cmd.Println("Migration completed successfully")
- return nil
+ return rollkitStore.Close()
},
}
return cmd
From e7fa02a65683a7bccc0dbf3d46751685b66a713d Mon Sep 17 00:00:00 2001
From: Julien Robert
Date: Fri, 20 Jun 2025 15:18:06 +0200
Subject: [PATCH 5/8] allow passing da height
---
server/migration_cmd.go | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/server/migration_cmd.go b/server/migration_cmd.go
index 1fd9b15e..32f3fefe 100644
--- a/server/migration_cmd.go
+++ b/server/migration_cmd.go
@@ -21,6 +21,8 @@ import (
rollkittypes "github.com/rollkit/rollkit/types"
)
+var flagDaHeight = "da-height"
+
// MigrateToRollkitCmd returns a command that migrates the data from the CometBFT chain to Rollkit.
func MigrateToRollkitCmd() *cobra.Command {
cmd := &cobra.Command{
@@ -53,7 +55,11 @@ func MigrateToRollkitCmd() *cobra.Command {
return err
}
- rollkitState, err := rollkitStateFromCometBFTState(cometBFTstate)
+ daHeight, err := cmd.Flags().GetUint64(flagDaHeight)
+ if err != nil {
+ return fmt.Errorf("error reading %s flag: %w", flagDaHeight, err)
+ }
+ rollkitState, err := rollkitStateFromCometBFTState(cometBFTstate, daHeight)
if err != nil {
return err
}
@@ -115,6 +121,9 @@ func MigrateToRollkitCmd() *cobra.Command {
return rollkitStore.Close()
},
}
+
+ cmd.Flags().Uint64(flagDaHeight, 1, "The DA height to set in the Rollkit state. Defaults to 1.")
+
return cmd
}
@@ -212,7 +221,7 @@ func loadRollkitStateStore(rootDir, dbPath string) (rollkitstore.Store, error) {
return rollkitstore.New(baseKV), nil
}
-func rollkitStateFromCometBFTState(cometBFTState state.State) (rollkittypes.State, error) {
+func rollkitStateFromCometBFTState(cometBFTState state.State, daHeight uint64) (rollkittypes.State, error) {
return rollkittypes.State{
Version: rollkittypes.Version{
Block: cometBFTState.Version.Consensus.Block,
@@ -223,7 +232,7 @@ func rollkitStateFromCometBFTState(cometBFTState state.State) (rollkittypes.Stat
LastBlockHeight: uint64(cometBFTState.LastBlockHeight),
LastBlockTime: cometBFTState.LastBlockTime,
- DAHeight: 1, // TODO: set this to the correct value
+ DAHeight: daHeight,
LastResultsHash: cometBFTState.LastResultsHash,
AppHash: cometBFTState.AppHash,
From bcc8c8086689b44b22d7545bda0656310608ce2c Mon Sep 17 00:00:00 2001
From: Julien Robert
Date: Fri, 20 Jun 2025 15:23:24 +0200
Subject: [PATCH 6/8] updates
---
server/migration_cmd.go | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/server/migration_cmd.go b/server/migration_cmd.go
index 32f3fefe..cb07b118 100644
--- a/server/migration_cmd.go
+++ b/server/migration_cmd.go
@@ -3,6 +3,7 @@ package server
import (
"bytes"
"context"
+ "errors"
"fmt"
"path/filepath"
@@ -70,11 +71,17 @@ func MigrateToRollkitCmd() *cobra.Command {
return err
}
- // migrate all the blocks from the cometBFT block store to the rollkit store
+ // migrate all the blocks from the CometBFT block store to the rollkit store
+ // the migration is done in reverse order, starting from the last block
for height := lastBlockHeight; height > 0; height-- {
cmd.Printf("Migrating block %d...\n", height)
block := cometBlockStore.LoadBlock(height)
+ if block == nil {
+ cmd.Printf("Block %d not found in CometBFT block store, skipping...\n", height)
+ continue
+ }
+
header, data, signature := cometBlockToRollkit(block)
if err = rollkitStore.SaveBlockData(context.Background(), header, data, &signature); err != nil {
@@ -118,7 +125,7 @@ func MigrateToRollkitCmd() *cobra.Command {
}
cmd.Println("Migration completed successfully")
- return rollkitStore.Close()
+ return errors.Join(rollkitStore.Close(), cometBlockStore.Close(), cometStateStore.Close())
},
}
From f237f738713a865e66f9a1a5c8be27ab694f9478 Mon Sep 17 00:00:00 2001
From: Julien Robert
Date: Fri, 20 Jun 2025 15:50:20 +0200
Subject: [PATCH 7/8] updates
---
server/migration_cmd.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/migration_cmd.go b/server/migration_cmd.go
index cb07b118..8f3c5458 100644
--- a/server/migration_cmd.go
+++ b/server/migration_cmd.go
@@ -161,7 +161,7 @@ func cometBlockToRollkit(block *cometbfttypes.Block) (*rollkittypes.SignedHeader
Block: block.Version.Block,
App: block.Version.App,
},
- LastHeaderHash: block.LastCommitHash.Bytes(),
+ LastHeaderHash: block.LastBlockID.Hash.Bytes(),
LastCommitHash: block.LastCommitHash.Bytes(),
DataHash: block.DataHash.Bytes(),
ConsensusHash: block.ConsensusHash.Bytes(),
From 0e08d966dbaf964cd79b3050a1b304fc49d38bef Mon Sep 17 00:00:00 2001
From: Julien Robert
Date: Tue, 24 Jun 2025 01:14:21 +0200
Subject: [PATCH 8/8] add maxmixed blocks
---
server/migration_cmd.go | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/server/migration_cmd.go b/server/migration_cmd.go
index 8f3c5458..b13e546f 100644
--- a/server/migration_cmd.go
+++ b/server/migration_cmd.go
@@ -22,7 +22,10 @@ import (
rollkittypes "github.com/rollkit/rollkit/types"
)
-var flagDaHeight = "da-height"
+var (
+ flagDaHeight = "da-height"
+ maxMissedBlock = 50
+)
// MigrateToRollkitCmd returns a command that migrates the data from the CometBFT chain to Rollkit.
func MigrateToRollkitCmd() *cobra.Command {
@@ -73,11 +76,18 @@ func MigrateToRollkitCmd() *cobra.Command {
// migrate all the blocks from the CometBFT block store to the rollkit store
// the migration is done in reverse order, starting from the last block
+ missedBlocks := make(map[int64]bool)
for height := lastBlockHeight; height > 0; height-- {
cmd.Printf("Migrating block %d...\n", height)
+ if len(missedBlocks) >= maxMissedBlock {
+ cmd.Println("Too many missed blocks, the node was probably pruned... Stopping now, everything should be fine.")
+ break
+ }
+
block := cometBlockStore.LoadBlock(height)
if block == nil {
+ missedBlocks[height] = true
cmd.Printf("Block %d not found in CometBFT block store, skipping...\n", height)
continue
}