diff --git a/app/test_state_store.go b/app/test_state_store.go index 194390f863..c6750123b3 100644 --- a/app/test_state_store.go +++ b/app/test_state_store.go @@ -65,6 +65,10 @@ func (s *InMemoryStateStore) Has(storeKey string, version int64, key []byte) (bo return ok, nil } +func (db *InMemoryStateStore) Set(storeKey string, key, value []byte, version int64) error { + return nil +} + func (s *InMemoryStateStore) Iterator(storeKey string, version int64, start, end []byte) (types.DBIterator, error) { s.mu.RLock() defer s.mu.RUnlock() diff --git a/cmd/seid/cmd/root.go b/cmd/seid/cmd/root.go index 07f9643b1e..c78055cd8d 100644 --- a/cmd/seid/cmd/root.go +++ b/cmd/seid/cmd/root.go @@ -312,7 +312,7 @@ func newApp( homeDir := cast.ToString(appOpts.Get(flags.FlagHome)) stateStore := app.GetStateStore() migrationHeight := cast.ToInt64(appOpts.Get("migrate-height")) - migrator := ss.NewMigrator(db, stateStore) + migrator := ss.NewMigrator(db, stateStore, stateStore) if err := migrator.Migrate(migrationHeight, homeDir); err != nil { panic(err) } diff --git a/go.mod b/go.mod index e1cc5b1b1f..d64cf0f224 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/rs/cors v1.8.2 github.com/rs/zerolog v1.30.0 github.com/sei-protocol/goutils v0.0.2 - github.com/sei-protocol/sei-db v0.0.27-0.20240123064153-d6dfa112e760 + github.com/sei-protocol/sei-db v0.0.47-0.20241231152822-f02af9285b76 github.com/sirkon/goproxy v1.4.8 github.com/spf13/cast v1.5.0 github.com/spf13/cobra v1.6.1 @@ -352,7 +352,7 @@ replace ( github.com/cosmos/ibc-go/v3 => github.com/sei-protocol/sei-ibc-go/v3 v3.3.3 github.com/ethereum/go-ethereum => github.com/sei-protocol/go-ethereum v1.13.5-sei-9.0.20241224143343-21ee50facc96 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 - github.com/sei-protocol/sei-db => github.com/sei-protocol/sei-db v0.0.46 + github.com/sei-protocol/sei-db => github.com/sei-protocol/sei-db v0.0.47-0.20250108143409-8223beaedd9e // Latest goleveldb is broken, we have to stick to this version github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tendermint/tendermint => github.com/sei-protocol/sei-tendermint v0.4.3 diff --git a/go.sum b/go.sum index a33d127827..fea18b0512 100644 --- a/go.sum +++ b/go.sum @@ -1347,8 +1347,8 @@ github.com/sei-protocol/goutils v0.0.2 h1:Bfa7Sv+4CVLNM20QcpvGb81B8C5HkQC/kW1CQp github.com/sei-protocol/goutils v0.0.2/go.mod h1:iYE2DuJfEnM+APPehr2gOUXfuLuPsVxorcDO+Tzq9q8= github.com/sei-protocol/sei-cosmos v0.3.48 h1:kSDweeTaLZ4TByLqAD6/hmtgAhAJHwXU1beyqsVXJkQ= github.com/sei-protocol/sei-cosmos v0.3.48/go.mod h1:XC417pB6NwxP/cQ2XTSZLzVnP8dMZ//4uCXS3SxFgoM= -github.com/sei-protocol/sei-db v0.0.46 h1:naXfSp1I3UgJJm/iSvXpdFzr9nofEOxp/EekcAVj7wY= -github.com/sei-protocol/sei-db v0.0.46/go.mod h1:m5g7p0QeAS3dNJHIl28zQpzOgxQmvYqPb7t4hwgIOCA= +github.com/sei-protocol/sei-db v0.0.47-0.20250108143409-8223beaedd9e h1:FD6thieZBi6w967fp0wL75hxnUTqNeVulQar4L156hM= +github.com/sei-protocol/sei-db v0.0.47-0.20250108143409-8223beaedd9e/go.mod h1:m5g7p0QeAS3dNJHIl28zQpzOgxQmvYqPb7t4hwgIOCA= github.com/sei-protocol/sei-iavl v0.2.0 h1:OisPjXiDT+oe+aeckzDEFgkZCYuUjHgs/PP8DPicN+I= github.com/sei-protocol/sei-iavl v0.2.0/go.mod h1:qRf8QYUPfrAO7K6VDB2B2l/N7K5L76OorioGBcJBIbw= github.com/sei-protocol/sei-ibc-go/v3 v3.3.3 h1:0kg1giMHiKMLXOCFLqH/9kqdl5bH+unzI8qRF6qwXzw= diff --git a/tools/cmd.go b/tools/cmd.go index dc9b79bd46..ea292d5ae0 100644 --- a/tools/cmd.go +++ b/tools/cmd.go @@ -14,6 +14,7 @@ func ToolCmd() *cobra.Command { } toolsCmd.AddCommand(scanner.ScanCmd()) toolsCmd.AddCommand(migration.MigrateCmd()) + toolsCmd.AddCommand(migration.MigrateSSCmd()) toolsCmd.AddCommand(migration.VerifyMigrationCmd()) toolsCmd.AddCommand(migration.GenerateStats()) return toolsCmd diff --git a/tools/migration/cmd/cmd.go b/tools/migration/cmd/cmd.go index be18c8ce3a..412bddab7f 100644 --- a/tools/migration/cmd/cmd.go +++ b/tools/migration/cmd/cmd.go @@ -24,7 +24,7 @@ func MigrateCmd() *cobra.Command { Short: "A tool to migrate full IAVL data store to SeiDB. Use this tool to migrate IAVL to SeiDB SC and SS database.", Run: execute, } - cmd.PersistentFlags().String("home-dir", "/root/.sei", "Sei home directory") + cmd.PersistentFlags().String("home-dir", "/root/.old_sei", "Sei home directory") return cmd } @@ -48,6 +48,50 @@ func migrateSC(version int64, homeDir string, db dbm.DB) error { return migrator.Migrate(version) } +func MigrateSSCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "migrate-ss", + Short: "A tool to migrate full ss distribution module", + Run: executeSS, + } + cmd.PersistentFlags().String("home-dir", "/root/.old_sei", "Sei home directory") + return cmd +} + +func executeSS(cmd *cobra.Command, _ []string) { + homeDir, _ := cmd.Flags().GetString("home-dir") + dataDir := filepath.Join(homeDir, "data") + db, err := dbm.NewGoLevelDB("application", dataDir) + if err != nil { + panic(err) + } + latestVersion := rootmulti.GetLatestVersion(db) + fmt.Printf("latest version: %d\n", latestVersion) + + if err = migrateSS(latestVersion, homeDir, db); err != nil { + panic(err) + } +} + +func migrateSS(version int64, homeDir string, db dbm.DB) error { + ssConfig := config.DefaultStateStoreConfig() + ssConfig.Enable = true + ssConfig.KeepRecent = 0 + + stateStore, err := sstypes.NewStateStore(log.NewNopLogger(), homeDir, ssConfig) + if err != nil { + return err + } + + oldStateStore, err := sstypes.NewStateStore(log.NewNopLogger(), "/root/.sei", ssConfig) + if err != nil { + return err + } + + migrator := ss.NewMigrator(db, stateStore, oldStateStore) + return migrator.Migrate(version, homeDir) +} + func VerifyMigrationCmd() *cobra.Command { cmd := &cobra.Command{ Use: "verify-migration", @@ -95,7 +139,7 @@ func verifySS(version int64, homeDir string, db dbm.DB) error { return err } - migrator := ss.NewMigrator(db, stateStore) + migrator := ss.NewMigrator(db, stateStore, stateStore) return migrator.Verify(version) } diff --git a/tools/migration/ss/migrator.go b/tools/migration/ss/migrator.go index e737a86133..90ec1ca91d 100644 --- a/tools/migration/ss/migrator.go +++ b/tools/migration/ss/migrator.go @@ -13,8 +13,9 @@ import ( ) type Migrator struct { - iavlDB dbm.DB - stateStore types.StateStore + iavlDB dbm.DB + stateStore types.StateStore + oldStateStore types.StateStore } // TODO: make this configurable? @@ -22,55 +23,103 @@ const ( DefaultCacheSize int = 10000 ) -func NewMigrator(db dbm.DB, stateStore types.StateStore) *Migrator { +func NewMigrator(db dbm.DB, stateStore types.StateStore, oldStateStore types.StateStore) *Migrator { return &Migrator{ - iavlDB: db, - stateStore: stateStore, + iavlDB: db, + stateStore: stateStore, + oldStateStore: oldStateStore, } } func (m *Migrator) Migrate(version int64, homeDir string) error { + // Channel to send RawSnapshotNodes to the importer. ch := make(chan types.RawSnapshotNode, 1000) + // Channel to capture errors from both goroutines below. errCh := make(chan error, 2) - // Get the latest key, if any, to resume from - latestKey, err := m.stateStore.GetLatestMigratedKey() - if err != nil { - return fmt.Errorf("failed to get latest key: %w", err) - } - - latestModule, err := m.stateStore.GetLatestMigratedModule() - if err != nil { - return fmt.Errorf("failed to get latest module: %w", err) - } - - fmt.Println("Starting migration...") + fmt.Printf("Starting migration for 'distribution' module from version %d to %d...\n", 100215000, 106789896) - // Goroutine to iterate through IAVL and export leaf nodes + // Goroutine #1: Export distribution leaf nodes into ch go func() { defer close(ch) - errCh <- ExportLeafNodesFromKey(m.iavlDB, ch, latestKey, latestModule) + errCh <- exportDistributionLeafNodes(m.oldStateStore, m.stateStore, ch, 100215000, 106789896) }() - // Import nodes into PebbleDB - go func() { - errCh <- m.stateStore.RawImport(ch) - }() - - // Block until both processes complete - for i := 0; i < 2; i++ { + // Wait for both goroutines to complete + for i := 0; i < 1; i++ { if err := <-errCh; err != nil { return err } } - // Set earliest and latest version in the database - err = m.stateStore.SetEarliestVersion(1, true) + return nil +} + +func exportDistributionLeafNodes( + oldStateStore types.StateStore, + newStateStore types.StateStore, + ch chan<- types.RawSnapshotNode, + startVersion, endVersion int64, +) error { + fmt.Printf("Starting export at %s for versions [%d..%d]\n", + time.Now().Format(time.RFC3339), startVersion, endVersion, + ) + + var totalExported int + var totalMismatch int + startTime := time.Now() + + // RawIterate will scan *all* keys in the "distribution" store. + // We'll filter them by version in the callback. + stop, err := oldStateStore.RawIterate("distribution", func(key, value []byte, version int64) bool { + bz, errorInner := newStateStore.Get("distribution", version, key) + if errorInner != nil { + panic(errorInner) + } + + if !bytes.Equal(bz, value) { + totalMismatch++ + fmt.Printf("values don't match for key %s: expected %s, got %s\n", string(key), string(value), string(bz)) + + if err := newStateStore.Set("distribution", key, value, version); err != nil { + panic(err) + } + + // Verify the write was successful + verifyBz, verifyErr := newStateStore.Get("distribution", version, key) + if verifyErr != nil { + panic(fmt.Errorf("failed to verify write: %w", verifyErr)) + } + if !bytes.Equal(verifyBz, value) { + panic(fmt.Errorf("write verification failed: value mismatch after Set")) + } + } + + totalExported++ + // Optional progress logging every 1,000,000 keys: + if totalExported%1_000_000 == 0 { + fmt.Printf("[SingleWorker][%s] Verified %d distribution keys so far mismatch %d\n", + time.Now().Format(time.RFC3339), totalExported, totalMismatch, + ) + } + // Return false to continue iterating + return false + }) if err != nil { - return err + return fmt.Errorf("RawIterate error: %w", err) + } + if stop { + fmt.Printf("[SingleWorker][%s] Iteration stopped early; callback returned true at some point.\n", + time.Now().Format(time.RFC3339), + ) } - return m.stateStore.SetLatestVersion(version) + fmt.Printf( + "[%s] Completed exporting distribution store for versions [%d..%d]. Total keys: %d. Duration: %s\n", + time.Now().Format(time.RFC3339), startVersion, endVersion, totalExported, time.Since(startTime), + ) + fmt.Printf("Finished export at %s\n", time.Now().Format(time.RFC3339)) + return nil } func (m *Migrator) Verify(version int64) error {