diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bb3c5911f0e..68c9011fc466 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -107,6 +107,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/bank) [#15764](https://github.com/cosmos/cosmos-sdk/pull/15764) Speedup x/bank InitGenesis * (x/auth) [#15867](https://github.com/cosmos/cosmos-sdk/pull/15867) Support better logging for signature verification failure. * (types/query) [#16041](https://github.com/cosmos/cosmos-sdk/pull/16041) change pagination max limit to a variable in order to be modifed by application devs +* (server) [#16061](https://github.com/cosmos/cosmos-sdk/pull/16061) add comet bootstrap command ### State Machine Breaking diff --git a/docs/docs/run-node/01-run-node.md b/docs/docs/run-node/01-run-node.md index 871fd0d2b1ed..8369920707be 100644 --- a/docs/docs/run-node/01-run-node.md +++ b/docs/docs/run-node/01-run-node.md @@ -149,3 +149,15 @@ In config.toml: ```toml log_level: "state:info,p2p:info,consensus:info,x/staking:info,x/ibc:info,*error" ``` + +## State Sync + +State sync is the act in which a node syncs the latest or close to the latest state of a blockchain. This is useful for users who don't want to sync all the blocks in history. You can read more here: https://docs.cometbft.com/v0.37/core/state-sync + +### Local State Sync + +Local state sync work similar to normal state sync except that it works off a local snapshot of state instead of one provided via the p2p network. The steps to start local state sync are similar to normal state sync with a few different designs. + +1. As mentioned in https://docs.cometbft.com/v0.37/core/state-sync, one must set a height and hash in the config.toml along with a few rpc servers (the afromentioned link has instructions on how to do this). +2. Bootsrapping Comet state in order to start the node after the snapshot has been ingested. This can be done with the bootstrap command ` comet bootstrap-state` + diff --git a/server/cmt_cmds.go b/server/cmt_cmds.go index a37cc53173d9..a96dea241bf4 100644 --- a/server/cmt_cmds.go +++ b/server/cmt_cmds.go @@ -5,8 +5,15 @@ import ( "strconv" "strings" + "cosmossdk.io/log" + "github.com/cometbft/cometbft/light" + "github.com/cometbft/cometbft/node" "github.com/cometbft/cometbft/p2p" pvm "github.com/cometbft/cometbft/privval" + cmtstore "github.com/cometbft/cometbft/proto/tendermint/store" + sm "github.com/cometbft/cometbft/state" + "github.com/cometbft/cometbft/statesync" + "github.com/cometbft/cometbft/store" cmtversion "github.com/cometbft/cometbft/version" "github.com/spf13/cobra" "sigs.k8s.io/yaml" @@ -15,6 +22,8 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" rpc "github.com/cosmos/cosmos-sdk/client/rpc" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + servercmtlog "github.com/cosmos/cosmos-sdk/server/log" + "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" "github.com/cosmos/cosmos-sdk/version" @@ -249,3 +258,92 @@ $ %s query block --%s=%s return cmd } + +func BootstrapStateCmd(appCreator types.AppCreator) *cobra.Command { + cmd := &cobra.Command{ + Use: "bootstrap-state", + Short: "Bootstrap CometBFT state at an arbitrary block height using a light client", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + serverCtx := GetServerContextFromCmd(cmd) + logger := log.NewLogger(cmd.OutOrStdout()) + cfg := serverCtx.Config + + height, err := cmd.Flags().GetInt64("height") + if err != nil { + return err + } + if height == 0 { + home := serverCtx.Viper.GetString(flags.FlagHome) + db, err := openDB(home, GetAppDBBackend(serverCtx.Viper)) + if err != nil { + return err + } + + app := appCreator(logger, db, nil, serverCtx.Viper) + height = app.CommitMultiStore().LastCommitID().Version + } + + blockStoreDB, err := node.DefaultDBProvider(&node.DBContext{ID: "blockstore", Config: cfg}) + if err != nil { + return err + } + blockStore := store.NewBlockStore(blockStoreDB) + + stateDB, err := node.DefaultDBProvider(&node.DBContext{ID: "state", Config: cfg}) + if err != nil { + return err + } + stateStore := sm.NewStore(stateDB, sm.StoreOptions{ + DiscardABCIResponses: cfg.Storage.DiscardABCIResponses, + }) + + genState, _, err := node.LoadStateFromDBOrGenesisDocProvider(stateDB, node.DefaultGenesisDocProviderFunc(cfg)) + if err != nil { + return err + } + + stateProvider, err := statesync.NewLightClientStateProvider( + cmd.Context(), + genState.ChainID, genState.Version, genState.InitialHeight, + cfg.StateSync.RPCServers, light.TrustOptions{ + Period: cfg.StateSync.TrustPeriod, + Height: cfg.StateSync.TrustHeight, + Hash: cfg.StateSync.TrustHashBytes(), + }, servercmtlog.CometLoggerWrapper{Logger: logger.With("module", "light")}) + if err != nil { + return fmt.Errorf("failed to set up light client state provider: %w", err) + } + + state, err := stateProvider.State(cmd.Context(), uint64(height)) + if err != nil { + return fmt.Errorf("failed to get state: %w", err) + } + + commit, err := stateProvider.Commit(cmd.Context(), uint64(height)) + if err != nil { + return fmt.Errorf("failed to get commit: %w", err) + } + + if err := stateStore.Bootstrap(state); err != nil { + return fmt.Errorf("failed to bootstrap state: %w", err) + } + + if err := blockStore.SaveSeenCommit(state.LastBlockHeight, commit); err != nil { + return fmt.Errorf("failed to save seen commit: %w", err) + } + + store.SaveBlockStoreState(&cmtstore.BlockStoreState{ + // it breaks the invariant that blocks in range [Base, Height] must exists, but it do works in practice. + Base: state.LastBlockHeight, + Height: state.LastBlockHeight, + }, blockStoreDB) + + return nil + }, + } + + cmd.Flags().Int64("height", 0, "Block height to bootstrap state at, if not provided will use the latest block height in app state") + + return cmd +} diff --git a/server/util.go b/server/util.go index bd1b80371ffa..b53103a9d21f 100644 --- a/server/util.go +++ b/server/util.go @@ -320,6 +320,7 @@ func AddCommands(rootCmd *cobra.Command, defaultNodeHome string, appCreator type VersionCmd(), cmtcmd.ResetAllCmd, cmtcmd.ResetStateCmd, + BootstrapStateCmd(appCreator), ) startCmd := StartCmd(appCreator, defaultNodeHome)