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 }