Migrating an Existing Chain to ev-abci
This guide is for developers of existing Cosmos SDK chains who want to replace their node's default CometBFT consensus engine with the ev-abci implementation. By following these steps, you will migrate your chain to run as an ev-abci node while preserving chain state.
Overview of Migration Process
The migration process involves the following key phases:
- Code Preparation: Add migration module, staking wrapper, and upgrade handler to your existing chain
- Governance Proposal: Create and pass a governance proposal to initiate the migration
- State Export: Export the current chain state at the designated upgrade height
- Node Reconfiguration: Wire the
ev-abcistart handler into your node's entrypoint - Migration Execution: Run
appd evolve-migrateto transform the exported state - Chain Restart: Start the new
ev-abcinode with the migrated state
This document will guide you through each phase.
Phase 1: Code Preparation - Add Migration Module and Staking Wrapper
The first step prepares your existing chain for migration by integrating the necessary modules.
Step 1: Add Migration Manager Module
Add the migrationmngr module to your application. This module manages the transition from a PoS validator set to a sequencer-based model.
Note: For detailed information about the migration manager, please refer to the migration manager documentation.
In your app.go file:
- Import the migration manager module:
import (
// ...
migrationmngr "github.com/evstack/ev-abci/modules/migrationmngr"
migrationmngrkeeper "github.com/evstack/ev-abci/modules/migrationmngr/keeper"
migrationmngrtypes "github.com/evstack/ev-abci/modules/migrationmngr/types"
// ...
)- Add the migration manager keeper to your app struct
- Register the module in your module manager
- Configure the migration manager in your app initialization
Step 2: Replace Staking Module with Wrapper
Goal: Ensure the migrationmngr module is the sole source of validator set updates during migration.
Replace the standard Cosmos SDK x/staking module with the staking wrapper module provided in ev-abci. The wrapper's EndBlock method prevents validator updates from the staking module, delegating that responsibility to the migrationmngr module during migration.
In your app.go file (and any other files that import the staking module):
Replace this:
import (
// ...
"github.com/cosmos/cosmos-sdk/x/staking"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
// ...
)With this:
import (
// ...
"github.com/evstack/ev-abci/modules/staking" // The wrapper module
stakingkeeper "github.com/evstack/ev-abci/modules/staking/keeper"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" // Staking types remain the same
// ...
)By changing the import path, your application will automatically use the wrapper module. No other changes to your EndBlocker method are needed.
Phase 2: Create Upgrade Handler
Create an upgrade handler in your app.go that will be triggered when the governance proposal is executed.
func (app *App) setupUpgradeHandlers() {
app.UpgradeKeeper.SetUpgradeHandler(
"v2-migrate-to-evolve", // Upgrade name must match governance proposal
func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
// The upgrade handler can initialize state for the migration manager if needed
// The actual migration will happen during the evolve-migrate step
return app.mm.RunMigrations(ctx, app.configurator, fromVM)
},
)
}Call this function in your app initialization code in app.go.
Phase 3: Create Governance Proposal for Migration
Create and submit a software upgrade governance proposal to initiate the migration at a specific block height.
# Create the governance proposal
<appd> tx gov submit-proposal software-upgrade v2-migrate-to-evolve \
--title "Migrate to Evolve" \
--description "Upgrade chain to use ev-abci consensus" \
--upgrade-height <target-height> \
--from <your-key> \
--chain-id <chain-id>
# Vote on the proposal (repeat for validators to reach quorum)
<appd> tx gov vote <proposal-id> yes --from <validator-key>Wait for the proposal to pass and for the chain to reach the upgrade height. The chain will halt at the specified height, waiting for the upgrade to be applied.
Trigger Migration to Evolve
After the upgrade proposal has passed, submit the MsgMigrateToEvolve message to initiate the actual migration process. This can be done through a governance proposal or directly if your chain's authority allows it.
# Submit MsgMigrateToEvolve governance proposal (if using governance)
<appd> tx gov submit-proposal migrate-to-evolve \
--title "Trigger Migration to Evolve" \
--description "Execute migration to ev-abci consensus" \
--from <your-key> \
--chain-id <chain-id>
# Or submit directly if authority allows (authority address depends on your chain configuration)
<appd> tx migrationmngr migrate-to-evolve \
--from <authority-key> \
--chain-id <chain-id>Once this message is processed, the migration manager module will handle the transition from the PoS validator set to the sequencer-based model.
Phase 4: Wire ev-abci Start Handler in root.go
⚠️ Important: Complete this phase BEFORE the chain halts at the upgrade height. Do NOT start your node yet - you will start it in Phase 6 after running the migration command.
Modify your node's entrypoint to use the ev-abci server commands.
Locate Your Application's Entrypoint
Open the main entrypoint file for your chain's binary, usually found at cmd/<your-app-name>/main.go or root.go.
Modify the Start Command
Add the ev-abci start handler to your root command. This is similar to the Ignite Apps evolve template.
// cmd/<appd>/main.go (or root.go)
package main
import (
"os"
"github.com/cosmos/cosmos-sdk/server"
"github.com/spf13/cobra"
// Import the ev-abci server package
evabci_server "github.com/evstack/ev-abci/server"
"<your-app-path>/app"
)
func main() {
rootCmd := &cobra.Command{
Use: "<appd>",
Short: "Your App Daemon (ev-abci enabled)",
}
// Keep existing commands (keys, export, etc.)
server.AddCommands(rootCmd, app.DefaultNodeHome, app.New, app.MakeEncodingConfig(), tx.DefaultSignModes)
// --- Wire ev-abci start handler ---
startCmd := &cobra.Command{
Use: "start",
Short: "Run the full node with ev-abci",
RunE: func(cmd *cobra.Command, _ []string) error {
return server.Start(cmd, evabci_server.StartHandler())
},
}
evabci_server.AddFlags(startCmd)
rootCmd.AddCommand(startCmd)
// --- End of ev-abci changes ---
if err := rootCmd.Execute(); err != nil {
server.HandleError(err)
os.Exit(1)
}
}Build Your Application
Re-build your application's binary with the updated code:
go build -o <appd> ./cmd/<appd>⚠️ Important: Do NOT start the node yet. Proceed directly to Phase 5 to run the migration command.
Phase 5: Run evolve-migrate
After the chain halts at the upgrade height, run the migration command to transform the CometBFT data to Evolve format.
⚠️ Critical: The node must NOT be running when you execute this command. Ensure all node processes are stopped before proceeding.
# Run the migration command
<appd> evolve-migrate
# Optional: specify the DA height for the Evolve state (defaults to 1)
<appd> evolve-migrate --da-height <height>The evolve-migrate command performs the following operations:
- Migrates all blocks from the CometBFT blockstore to the Evolve store
- Converts the CometBFT state to Evolve state format
- Creates
ev_genesis.json- a minimal genesis file that the node will automatically detect and use on subsequent startups - Saves state to the ABCI execution store for compatibility
- Seeds sync stores with the latest migrated header and data
- Cleans up migration state from the application database
Important Notes:
- The migration processes blocks in reverse order (from latest to earliest)
- If blocks are missing (e.g., due to pruning), they will be skipped. Migration stops if more than the configured maximum number of blocks are missing
- Vote extensions are not supported in Evolve - if they were enabled in your chain, they will have no effect after migration
- The command operates on the data in your node's home directory (e.g.,
~/.appd/data/) - After successful migration, the
ev_genesis.jsonfile will be used automatically on node restart
Phase 6: Start New ev-abci Node
Start your node with the migrated state:
<appd> startVerify that the node starts successfully:
# Check that ev-abci flags are available
<appd> start --help
# You should see flags like:
# --ev-node.attester-mode
# --ev-node.aggregator
# --ev-node.sequencer-url
# etc.Your node is now running with ev-abci instead of CometBFT. The chain continues from the same state but with the new consensus engine.
Summary
The migration process follows these key phases:
- Code Preparation: Modify your chain code to add the migration manager module and staking wrapper
- Create Upgrade Handler: Define the upgrade logic that will be triggered by governance
- Governance Proposal: Submit and pass a software upgrade proposal
- Wire Start Handler: Update your node's entrypoint to use the
ev-abcistart command - Execute Migration: Run
appd evolve-migrateto transform the exported state - Restart Chain: Start the new
ev-abcinode with the migrated state
This approach ensures a smooth migration with minimal downtime and preserves all chain state and history.
