From 832517befc97138d67269bd68ec56d610636cc18 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 22 Feb 2023 11:29:03 +0100 Subject: [PATCH] feat: add application genesis (#15031) --- CHANGELOG.md | 3 +- proto/cosmos/genutil/v1beta1/genesis.proto | 2 +- server/export.go | 44 +--- server/mock/app.go | 6 +- server/mock/app_test.go | 4 +- server/start.go | 15 +- simapp/simd/cmd/testnet.go | 19 +- tests/e2e/genutil/cli_test.go | 19 -- tests/e2e/genutil/migrate.go | 61 ----- tests/e2e/genutil/suite.go | 143 ------------ tests/e2e/genutil/validate_genesis.go | 101 --------- tests/e2e/server/export_test.go | 102 ++++----- testutil/network/util.go | 29 ++- testutil/sims/address_helpers.go | 4 +- testutil/sims/state_helpers.go | 10 +- x/auth/helpers/genaccounts.go | 6 +- x/genutil/README.md | 60 +++++ x/genutil/client/cli/collect.go | 13 +- x/genutil/client/cli/gentx.go | 7 +- x/genutil/client/cli/init.go | 19 +- x/genutil/client/cli/init_test.go | 2 +- x/genutil/client/cli/migrate.go | 48 ++-- x/genutil/client/cli/migrate_test.go | 44 ++-- x/genutil/client/cli/validate_genesis.go | 33 +-- x/genutil/client/cli/validate_genesis_test.go | 68 ++---- x/genutil/collect.go | 15 +- x/genutil/collect_test.go | 14 +- x/genutil/doc.go | 7 +- x/genutil/types/genesis.go | 211 ++++++++++++++++++ x/genutil/types/genesis_state.go | 21 +- x/genutil/types/genesis_test.go | 69 ++++++ x/genutil/types/testdata/app_genesis.json | 1 + x/genutil/types/testdata/cmt_genesis.json | 1 + x/genutil/utils.go | 25 +-- 34 files changed, 593 insertions(+), 633 deletions(-) delete mode 100644 tests/e2e/genutil/cli_test.go delete mode 100644 tests/e2e/genutil/migrate.go delete mode 100644 tests/e2e/genutil/suite.go delete mode 100644 tests/e2e/genutil/validate_genesis.go create mode 100644 x/genutil/README.md create mode 100644 x/genutil/types/genesis.go create mode 100644 x/genutil/types/genesis_test.go create mode 100644 x/genutil/types/testdata/app_genesis.json create mode 100644 x/genutil/types/testdata/cmt_genesis.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d9da4394fa..3eb4d78abe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features -* (cli) [#14659](https://github.com/cosmos/cosmos-sdk/pull/14659) Added ability to query blocks by events with queries directly passed to Tendermint, which will allow for full query operator support, e.g. `>`. +* (x/genutil) [#15301](https://github.com/cosmos/cosmos-sdk/pull/15031) Add application genesis. The genesis is now entirely managed by the application and passed to CometBFT at note instantiation. Functions that were taking a `cmttypes.GenesisDoc{}` now takes a `genutiltypes.AppGenesis{}`. +* (cli) [#14659](https://github.com/cosmos/cosmos-sdk/pull/14659) Added ability to query blocks by events with queries directly passed to Tendermint, which will allow for full query operator support, e.g. `>`. * [#14897](https://github.com/cosmos/cosmos-sdk/pull/14897) Migrate the Cosmos SDK to CometBFT. * (x/gov) [#14720](https://github.com/cosmos/cosmos-sdk/pull/14720) Upstream expedited proposals from Osmosis. * (x/auth) [#14650](https://github.com/cosmos/cosmos-sdk/pull/14650) Add Textual SignModeHandler. It is however **NOT** enabled by default, and should only be used for **TESTING** purposes until `SIGN_MODE_TEXTUAL` is fully released. diff --git a/proto/cosmos/genutil/v1beta1/genesis.proto b/proto/cosmos/genutil/v1beta1/genesis.proto index 45aa6bb22d..dfd34943ea 100644 --- a/proto/cosmos/genutil/v1beta1/genesis.proto +++ b/proto/cosmos/genutil/v1beta1/genesis.proto @@ -15,4 +15,4 @@ message GenesisState { (amino.field_name) = "gentxs", (amino.dont_omitempty) = true ]; -} +} \ No newline at end of file diff --git a/server/export.go b/server/export.go index a469abe9d3..056686c9f7 100644 --- a/server/export.go +++ b/server/export.go @@ -1,16 +1,15 @@ package server import ( + "encoding/json" "fmt" "os" - cmtjson "github.com/cometbft/cometbft/libs/json" - cmttypes "github.com/cometbft/cometbft/types" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/server/types" - sdk "github.com/cosmos/cosmos-sdk/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" ) const ( @@ -18,7 +17,6 @@ const ( FlagForZeroHeight = "for-zero-height" FlagJailAllowedAddrs = "jail-allowed-addrs" FlagModulesToExport = "modules-to-export" - FlagOutputDocument = "output-document" ) // ExportCmd dumps app state to JSON. @@ -66,58 +64,36 @@ func ExportCmd(appExporter types.AppExporter, defaultNodeHome string) *cobra.Com forZeroHeight, _ := cmd.Flags().GetBool(FlagForZeroHeight) jailAllowedAddrs, _ := cmd.Flags().GetStringSlice(FlagJailAllowedAddrs) modulesToExport, _ := cmd.Flags().GetStringSlice(FlagModulesToExport) - outputDocument, _ := cmd.Flags().GetString(FlagOutputDocument) + outputDocument, _ := cmd.Flags().GetString(flags.FlagOutputDocument) exported, err := appExporter(serverCtx.Logger, db, traceWriter, height, forZeroHeight, jailAllowedAddrs, serverCtx.Viper, modulesToExport) if err != nil { return fmt.Errorf("error exporting state: %v", err) } - doc, err := cmttypes.GenesisDocFromFile(serverCtx.Config.GenesisFile()) + appGenesis, err := genutiltypes.AppGenesisFromFile(serverCtx.Config.GenesisFile()) if err != nil { return err } - doc.AppState = exported.AppState - doc.Validators = exported.Validators - doc.InitialHeight = exported.Height - doc.ConsensusParams = &cmttypes.ConsensusParams{ - Block: cmttypes.BlockParams{ - MaxBytes: exported.ConsensusParams.Block.MaxBytes, - MaxGas: exported.ConsensusParams.Block.MaxGas, - }, - Evidence: cmttypes.EvidenceParams{ - MaxAgeNumBlocks: exported.ConsensusParams.Evidence.MaxAgeNumBlocks, - MaxAgeDuration: exported.ConsensusParams.Evidence.MaxAgeDuration, - MaxBytes: exported.ConsensusParams.Evidence.MaxBytes, - }, - Validator: cmttypes.ValidatorParams{ - PubKeyTypes: exported.ConsensusParams.Validator.PubKeyTypes, - }, - } + appGenesis.AppState = exported.AppState + appGenesis.InitialHeight = exported.Height + appGenesis.Consensus = genutiltypes.NewConsensusGenesis(exported.ConsensusParams, exported.Validators) - // NOTE: CometBFT uses a custom JSON decoder for GenesisDoc - // (except for stuff inside AppState). Inside AppState, we're free - // to encode as protobuf or amino. - encoded, err := cmtjson.Marshal(doc) + out, err := json.Marshal(appGenesis) if err != nil { return err } cmd.SetOut(cmd.OutOrStdout()) cmd.SetErr(cmd.OutOrStderr()) - out := sdk.MustSortJSON(encoded) if outputDocument == "" { cmd.Println(string(out)) return nil } - var exportedGenDoc cmttypes.GenesisDoc - if err = cmtjson.Unmarshal(out, &exportedGenDoc); err != nil { - return err - } - if err = exportedGenDoc.SaveAs(outputDocument); err != nil { + if err = appGenesis.SaveAs(outputDocument); err != nil { return err } @@ -130,7 +106,7 @@ func ExportCmd(appExporter types.AppExporter, defaultNodeHome string) *cobra.Com cmd.Flags().Bool(FlagForZeroHeight, false, "Export state to start at height zero (perform preproccessing)") cmd.Flags().StringSlice(FlagJailAllowedAddrs, []string{}, "Comma-separated list of operator addresses of jailed validators to unjail") cmd.Flags().StringSlice(FlagModulesToExport, []string{}, "Comma-separated list of modules to export. If empty, will export all modules") - cmd.Flags().String(FlagOutputDocument, "", "Exported state is written to the given file instead of STDOUT") + cmd.Flags().String(flags.FlagOutputDocument, "", "Exported state is written to the given file instead of STDOUT") return cmd } diff --git a/server/mock/app.go b/server/mock/app.go index 438c904c80..b1d78dcfd8 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -9,7 +9,6 @@ import ( abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" - "github.com/cometbft/cometbft/types" db "github.com/cosmos/cosmos-db" "google.golang.org/grpc" @@ -19,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" ) // NewApp creates a simple mock kvstore app for testing. It should work @@ -116,7 +116,7 @@ func InitChainer(key storetypes.StoreKey) func(sdk.Context, abci.RequestInitChai // AppGenState can be passed into InitCmd, returns a static string of a few // key-values that can be parsed by InitChainer -func AppGenState(_ *codec.LegacyAmino, _ types.GenesisDoc, _ []json.RawMessage) (appState json.RawMessage, err error) { +func AppGenState(_ *codec.LegacyAmino, _ genutiltypes.AppGenesis, _ []json.RawMessage) (appState json.RawMessage, err error) { appState = json.RawMessage(`{ "values": [ { @@ -133,7 +133,7 @@ func AppGenState(_ *codec.LegacyAmino, _ types.GenesisDoc, _ []json.RawMessage) } // AppGenStateEmpty returns an empty transaction state for mocking. -func AppGenStateEmpty(_ *codec.LegacyAmino, _ types.GenesisDoc, _ []json.RawMessage) (appState json.RawMessage, err error) { +func AppGenStateEmpty(_ *codec.LegacyAmino, _ genutiltypes.AppGenesis, _ []json.RawMessage) (appState json.RawMessage, err error) { appState = json.RawMessage(``) return } diff --git a/server/mock/app_test.go b/server/mock/app_test.go index 3435fe7ff3..afd60b399c 100644 --- a/server/mock/app_test.go +++ b/server/mock/app_test.go @@ -7,10 +7,10 @@ import ( abci "github.com/cometbft/cometbft/abci/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - "github.com/cometbft/cometbft/types" "github.com/stretchr/testify/require" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" ) func TestInitApp(t *testing.T) { @@ -21,7 +21,7 @@ func TestInitApp(t *testing.T) { } require.NoError(t, err) - appState, err := AppGenState(nil, types.GenesisDoc{}, nil) + appState, err := AppGenState(nil, genutiltypes.AppGenesis{}, nil) require.NoError(t, err) req := abci.RequestInitChain{ diff --git a/server/start.go b/server/start.go index 7ad4ca73ad..b9d886956e 100644 --- a/server/start.go +++ b/server/start.go @@ -8,12 +8,13 @@ import ( "time" "github.com/cometbft/cometbft/abci/server" - tcmd "github.com/cometbft/cometbft/cmd/cometbft/commands" + cmtcmd "github.com/cometbft/cometbft/cmd/cometbft/commands" "github.com/cometbft/cometbft/node" "github.com/cometbft/cometbft/p2p" pvm "github.com/cometbft/cometbft/privval" "github.com/cometbft/cometbft/proxy" "github.com/cometbft/cometbft/rpc/client/local" + cmttypes "github.com/cometbft/cometbft/types" "github.com/spf13/cobra" "github.com/spf13/pflag" "google.golang.org/grpc" @@ -30,6 +31,7 @@ import ( "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/telemetry" "github.com/cosmos/cosmos-sdk/types/mempool" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" ) const ( @@ -204,7 +206,7 @@ is performed. Note, when enabled, gRPC will also be automatically enabled. }) // add support for all CometBFT-specific command line options - tcmd.AddNodeFlags(cmd) + cmtcmd.AddNodeFlags(cmd) return cmd } @@ -301,7 +303,14 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App if err != nil { return err } - genDocProvider := node.DefaultGenesisDocProviderFunc(cfg) + genDocProvider := func() (*cmttypes.GenesisDoc, error) { + appGenesis, err := genutiltypes.AppGenesisFromFile(cfg.GenesisFile()) + if err != nil { + return nil, err + } + + return appGenesis.ToGenesisDoc() + } var ( tmNode *node.Node diff --git a/simapp/simd/cmd/testnet.go b/simapp/simd/cmd/testnet.go index 3e5fa258f8..2ba688b9e3 100644 --- a/simapp/simd/cmd/testnet.go +++ b/simapp/simd/cmd/testnet.go @@ -9,15 +9,15 @@ import ( "os" "path/filepath" - "cosmossdk.io/math" - "cosmossdk.io/math/unsafe" cmtconfig "github.com/cometbft/cometbft/config" - "github.com/cometbft/cometbft/types" cmttime "github.com/cometbft/cometbft/types/time" "github.com/spf13/cobra" "github.com/spf13/pflag" + "cosmossdk.io/math" + "cosmossdk.io/math/unsafe" "cosmossdk.io/simapp" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" @@ -391,15 +391,10 @@ func initGenFiles( return err } - genDoc := types.GenesisDoc{ - ChainID: chainID, - AppState: appGenStateJSON, - Validators: nil, - } - + appGenesis := genutiltypes.NewAppGenesisWithVersion(chainID, appGenStateJSON) // generate empty genesis files for each validator and save for i := 0; i < numValidators; i++ { - if err := genDoc.SaveAs(genFiles[i]); err != nil { + if err := appGenesis.SaveAs(genFiles[i]); err != nil { return err } } @@ -425,12 +420,12 @@ func collectGenFiles( nodeID, valPubKey := nodeIDs[i], valPubKeys[i] initCfg := genutiltypes.NewInitConfig(chainID, gentxsDir, nodeID, valPubKey) - genDoc, err := types.GenesisDocFromFile(nodeConfig.GenesisFile()) + appGenesis, err := genutiltypes.AppGenesisFromFile(nodeConfig.GenesisFile()) if err != nil { return err } - nodeAppState, err := genutil.GenAppStateFromConfig(clientCtx.Codec, clientCtx.TxConfig, nodeConfig, initCfg, *genDoc, genBalIterator, genutiltypes.DefaultMessageValidator) + nodeAppState, err := genutil.GenAppStateFromConfig(clientCtx.Codec, clientCtx.TxConfig, nodeConfig, initCfg, appGenesis, genBalIterator, genutiltypes.DefaultMessageValidator) if err != nil { return err } diff --git a/tests/e2e/genutil/cli_test.go b/tests/e2e/genutil/cli_test.go deleted file mode 100644 index ad7c8dfc36..0000000000 --- a/tests/e2e/genutil/cli_test.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build e2e -// +build e2e - -package genutil - -import ( - "testing" - - "cosmossdk.io/simapp" - "github.com/cosmos/cosmos-sdk/testutil/network" - - "github.com/stretchr/testify/suite" -) - -func TestE2ETestSuite(t *testing.T) { - cfg := network.DefaultConfig(simapp.NewTestNetworkFixture) - cfg.NumValidators = 1 - suite.Run(t, NewE2ETestSuite(cfg)) -} diff --git a/tests/e2e/genutil/migrate.go b/tests/e2e/genutil/migrate.go deleted file mode 100644 index 4a2a7579b5..0000000000 --- a/tests/e2e/genutil/migrate.go +++ /dev/null @@ -1,61 +0,0 @@ -package genutil - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/testutil" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" -) - -func TestGetMigrationCallback(t *testing.T) { - for _, version := range cli.GetMigrationVersions() { - require.NotNil(t, cli.GetMigrationCallback(version)) - } -} - -func (s *E2ETestSuite) TestMigrateGenesis() { - val0 := s.network.Validators[0] - - testCases := []struct { - name string - genesis string - target string - expErr bool - expErrMsg string - check func(jsonOut string) - }{ - { - "migrate 0.37 to 0.42", - v037Exported, - "v0.42", - true, "Make sure that you have correctly migrated all CometBFT consensus params", func(_ string) {}, - }, - { - "migrate 0.42 to 0.43", - v040Valid, - "v0.43", - false, "", - func(jsonOut string) { - // Make sure the json output contains the ADR-037 gov weighted votes. - s.Require().Contains(jsonOut, "\"weight\":\"1.000000000000000000\"") - }, - }, - } - - for _, tc := range testCases { - tc := tc - s.Run(tc.name, func() { - genesisFile := testutil.WriteToNewTempFile(s.T(), tc.genesis) - jsonOutput, err := clitestutil.ExecTestCLICmd(val0.ClientCtx, cli.MigrateGenesisCmd(), []string{tc.target, genesisFile.Name()}) - if tc.expErr { - s.Require().Contains(err.Error(), tc.expErrMsg) - } else { - s.Require().NoError(err) - tc.check(jsonOutput.String()) - } - }) - } -} diff --git a/tests/e2e/genutil/suite.go b/tests/e2e/genutil/suite.go deleted file mode 100644 index 882f63dbc2..0000000000 --- a/tests/e2e/genutil/suite.go +++ /dev/null @@ -1,143 +0,0 @@ -package genutil - -import ( - "fmt" - "io" - "os" - "path/filepath" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/cosmos-sdk/client/flags" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "github.com/cosmos/cosmos-sdk/testutil/network" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" - stakingcli "github.com/cosmos/cosmos-sdk/x/staking/client/cli" - "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -type E2ETestSuite struct { - suite.Suite - - cfg network.Config - network *network.Network -} - -func NewE2ETestSuite(cfg network.Config) *E2ETestSuite { - return &E2ETestSuite{cfg: cfg} -} - -func (s *E2ETestSuite) SetupSuite() { - s.T().Log("setting up e2e test suite") - - var err error - s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg) - s.Require().NoError(err) - - s.Require().NoError(s.network.WaitForNextBlock()) -} - -func (s *E2ETestSuite) TearDownSuite() { - s.T().Log("tearing down e2e test suite") - s.network.Cleanup() -} - -func (s *E2ETestSuite) TestGenTxCmd() { - val := s.network.Validators[0] - clientCtx := val.ClientCtx - amount := sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(12)) - - tests := []struct { - name string - args []string - expError bool - }{ - { - name: "invalid commission rate returns error", - args: []string{ - fmt.Sprintf("--%s=%s", flags.FlagChainID, s.network.Config.ChainID), - fmt.Sprintf("--%s=1", stakingcli.FlagCommissionRate), - val.Moniker, - amount.String(), - }, - expError: true, - }, - { - name: "valid gentx", - args: []string{ - fmt.Sprintf("--%s=%s", flags.FlagChainID, s.network.Config.ChainID), - val.Moniker, - amount.String(), - }, - expError: false, - }, - { - name: "invalid pubkey", - args: []string{ - fmt.Sprintf("--%s=%s", flags.FlagChainID, s.network.Config.ChainID), - fmt.Sprintf("--%s={\"key\":\"BOIkjkFruMpfOFC9oNPhiJGfmY2pHF/gwHdLDLnrnS0=\"}", stakingcli.FlagPubKey), - val.Moniker, - amount.String(), - }, - expError: true, - }, - { - name: "valid pubkey flag", - args: []string{ - fmt.Sprintf("--%s=%s", flags.FlagChainID, s.network.Config.ChainID), - fmt.Sprintf("--%s={\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"BOIkjkFruMpfOFC9oNPhiJGfmY2pHF/gwHdLDLnrnS0=\"}", stakingcli.FlagPubKey), - val.Moniker, - amount.String(), - }, - expError: false, - }, - } - - for _, tc := range tests { - tc := tc - - dir := s.T().TempDir() - genTxFile := filepath.Join(dir, "myTx") - tc.args = append(tc.args, fmt.Sprintf("--%s=%s", flags.FlagOutputDocument, genTxFile)) - - s.Run(tc.name, func() { - cmd := cli.GenTxCmd( - module.NewBasicManager(), - val.ClientCtx.TxConfig, - banktypes.GenesisBalancesIterator{}, - val.ClientCtx.HomeDir) - - out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) - - if tc.expError { - s.Require().Error(err) - - _, err = os.Open(genTxFile) - s.Require().Error(err) - } else { - s.Require().NoError(err, "test: %s\noutput: %s", tc.name, out.String()) - - // validate generated transaction. - open, err := os.Open(genTxFile) - s.Require().NoError(err) - - all, err := io.ReadAll(open) - s.Require().NoError(err) - - tx, err := val.ClientCtx.TxConfig.TxJSONDecoder()(all) - s.Require().NoError(err) - - msgs := tx.GetMsgs() - s.Require().Len(msgs, 1) - - s.Require().Equal(sdk.MsgTypeURL(&types.MsgCreateValidator{}), sdk.MsgTypeURL(msgs[0])) - s.Require().True(val.Address.Equals(msgs[0].GetSigners()[0])) - s.Require().Equal(amount, msgs[0].(*types.MsgCreateValidator).Value) - s.Require().NoError(tx.ValidateBasic()) - } - }) - } -} diff --git a/tests/e2e/genutil/validate_genesis.go b/tests/e2e/genutil/validate_genesis.go deleted file mode 100644 index 5561b0b9d6..0000000000 --- a/tests/e2e/genutil/validate_genesis.go +++ /dev/null @@ -1,101 +0,0 @@ -package genutil - -import ( - "github.com/cosmos/cosmos-sdk/testutil" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" -) - -// An example exported genesis file from a 0.37 chain. Note that evidence -// parameters only contains `max_age`. -var v037Exported = `{ - "app_hash": "", - "app_state": {}, - "chain_id": "test", - "consensus_params": { - "block": { - "max_bytes": "22020096", - "max_gas": "-1", - "time_iota_ms": "1000" - }, - "evidence": { "max_age": "100000" }, - "validator": { "pub_key_types": ["ed25519"] } - }, - "genesis_time": "2020-09-29T20:16:29.172362037Z", - "validators": [] -}` - -// An example exported genesis file that's 0.40 compatible. -// We added the following app_state: -// -// - x/gov: added votes to test ADR-037 split votes migration. -var v040Valid = `{ - "app_hash": "", - "app_state": { - "gov": { - "starting_proposal_id": "0", - "deposits": [], - "votes": [ - { - "proposal_id": "5", - "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh", - "option": "VOTE_OPTION_YES" - } - ], - "proposals": [], - "deposit_params": { "min_deposit": [], "max_deposit_period": "0s" }, - "voting_params": { "voting_period": "0s" }, - "tally_params": { "quorum": "0", "threshold": "0", "veto_threshold": "0" } - } - }, - "chain_id": "test", - "consensus_params": { - "block": { - "max_bytes": "22020096", - "max_gas": "-1", - "time_iota_ms": "1000" - }, - "evidence": { - "max_age_num_blocks": "100000", - "max_age_duration": "172800000000000", - "max_bytes": "0" - }, - "validator": { "pub_key_types": ["ed25519"] } - }, - "genesis_time": "2020-09-29T20:16:29.172362037Z", - "validators": [] -}` - -func (s *E2ETestSuite) TestValidateGenesis() { - val0 := s.network.Validators[0] - - testCases := []struct { - name string - genesis string - expErr bool - }{ - { - "exported 0.37 genesis file", - v037Exported, - true, - }, - { - "valid 0.40 genesis file", - v040Valid, - false, - }, - } - - for _, tc := range testCases { - tc := tc - s.Run(tc.name, func() { - genesisFile := testutil.WriteToNewTempFile(s.T(), tc.genesis) - _, err := clitestutil.ExecTestCLICmd(val0.ClientCtx, cli.ValidateGenesisCmd(nil), []string{genesisFile.Name()}) - if tc.expErr { - s.Require().Contains(err.Error(), "Make sure that you have correctly migrated all CometBFT consensus params") - } else { - s.Require().NoError(err) - } - }) - } -} diff --git a/tests/e2e/server/export_test.go b/tests/e2e/server/export_test.go index 5b911c904d..11f2a190ab 100644 --- a/tests/e2e/server/export_test.go +++ b/tests/e2e/server/export_test.go @@ -6,23 +6,21 @@ package server_test import ( "bytes" "context" + "encoding/json" "fmt" "io" "os" "path" "testing" - "cosmossdk.io/log" abci "github.com/cometbft/cometbft/abci/types" - cmtjson "github.com/cometbft/cometbft/libs/json" cmtlog "github.com/cometbft/cometbft/libs/log" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - cmttypes "github.com/cometbft/cometbft/types" dbm "github.com/cosmos/cosmos-db" "github.com/spf13/cobra" - "github.com/stretchr/testify/require" + "gotest.tools/v3/assert" - errorsmod "cosmossdk.io/errors" + "cosmossdk.io/log" "cosmossdk.io/simapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" @@ -30,31 +28,29 @@ import ( "github.com/cosmos/cosmos-sdk/server/types" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" ) func TestExportCmd_ConsensusParams(t *testing.T) { tempDir := t.TempDir() - _, ctx, _, cmd := setupApp(t, tempDir) output := &bytes.Buffer{} cmd.SetOut(output) cmd.SetArgs([]string{fmt.Sprintf("--%s=%s", flags.FlagHome, tempDir)}) - require.NoError(t, cmd.ExecuteContext(ctx)) + assert.NilError(t, cmd.ExecuteContext(ctx)) - var exportedGenDoc cmttypes.GenesisDoc - err := cmtjson.Unmarshal(output.Bytes(), &exportedGenDoc) - if err != nil { - t.Fatalf("error unmarshaling exported genesis doc: %s", err) - } + var exportedAppGenesis genutiltypes.AppGenesis + err := json.Unmarshal(output.Bytes(), &exportedAppGenesis) + assert.NilError(t, err) - require.Equal(t, simtestutil.DefaultConsensusParams.Block.MaxBytes, exportedGenDoc.ConsensusParams.Block.MaxBytes) - require.Equal(t, simtestutil.DefaultConsensusParams.Block.MaxGas, exportedGenDoc.ConsensusParams.Block.MaxGas) + assert.DeepEqual(t, simtestutil.DefaultConsensusParams.Block.MaxBytes, exportedAppGenesis.Consensus.Params.Block.MaxBytes) + assert.DeepEqual(t, simtestutil.DefaultConsensusParams.Block.MaxGas, exportedAppGenesis.Consensus.Params.Block.MaxGas) - require.Equal(t, simtestutil.DefaultConsensusParams.Evidence.MaxAgeDuration, exportedGenDoc.ConsensusParams.Evidence.MaxAgeDuration) - require.Equal(t, simtestutil.DefaultConsensusParams.Evidence.MaxAgeNumBlocks, exportedGenDoc.ConsensusParams.Evidence.MaxAgeNumBlocks) + assert.DeepEqual(t, simtestutil.DefaultConsensusParams.Evidence.MaxAgeDuration, exportedAppGenesis.Consensus.Params.Evidence.MaxAgeDuration) + assert.DeepEqual(t, simtestutil.DefaultConsensusParams.Evidence.MaxAgeNumBlocks, exportedAppGenesis.Consensus.Params.Evidence.MaxAgeNumBlocks) - require.Equal(t, simtestutil.DefaultConsensusParams.Validator.PubKeyTypes, exportedGenDoc.ConsensusParams.Validator.PubKeyTypes) + assert.DeepEqual(t, simtestutil.DefaultConsensusParams.Validator.PubKeyTypes, exportedAppGenesis.Consensus.Params.Validator.PubKeyTypes) } func TestExportCmd_HomeDir(t *testing.T) { @@ -63,7 +59,7 @@ func TestExportCmd_HomeDir(t *testing.T) { cmd.SetArgs([]string{fmt.Sprintf("--%s=%s", flags.FlagHome, "foobar")}) err := cmd.ExecuteContext(ctx) - require.EqualError(t, err, "stat foobar/config/genesis.json: no such file or directory") + assert.ErrorContains(t, err, "stat foobar/config/genesis.json: no such file or directory") } func TestExportCmd_Height(t *testing.T) { @@ -109,15 +105,12 @@ func TestExportCmd_Height(t *testing.T) { cmd.SetOut(output) args := append(tc.flags, fmt.Sprintf("--%s=%s", flags.FlagHome, tempDir)) cmd.SetArgs(args) - require.NoError(t, cmd.ExecuteContext(ctx)) + assert.NilError(t, cmd.ExecuteContext(ctx)) - var exportedGenDoc cmttypes.GenesisDoc - err := cmtjson.Unmarshal(output.Bytes(), &exportedGenDoc) - if err != nil { - t.Fatalf("error unmarshaling exported genesis doc: %s", err) - } - - require.Equal(t, tc.expHeight, exportedGenDoc.InitialHeight) + var exportedAppGenesis genutiltypes.AppGenesis + err := json.Unmarshal(output.Bytes(), &exportedAppGenesis) + assert.NilError(t, err) + assert.Equal(t, tc.expHeight, exportedAppGenesis.InitialHeight) }) } } @@ -131,7 +124,7 @@ func TestExportCmd_Output(t *testing.T) { { "should export state to the specified file", []string{ - fmt.Sprintf("--%s=%s", server.FlagOutputDocument, "foobar.json"), + fmt.Sprintf("--%s=%s", flags.FlagOutputDocument, "foobar.json"), }, "foobar.json", }, @@ -146,53 +139,54 @@ func TestExportCmd_Output(t *testing.T) { cmd.SetOut(output) args := append(tc.flags, fmt.Sprintf("--%s=%s", flags.FlagHome, tempDir)) cmd.SetArgs(args) - require.NoError(t, cmd.ExecuteContext(ctx)) + assert.NilError(t, cmd.ExecuteContext(ctx)) - var exportedGenDoc cmttypes.GenesisDoc + var exportedAppGenesis genutiltypes.AppGenesis f, err := os.ReadFile(tc.outputDocument) - if err != nil { - t.Fatalf("error reading exported genesis doc: %s", err) - } - require.NoError(t, cmtjson.Unmarshal(f, &exportedGenDoc)) + assert.NilError(t, err) + assert.NilError(t, json.Unmarshal(f, &exportedAppGenesis)) // Cleanup - if err = os.Remove(tc.outputDocument); err != nil { - t.Fatalf("error removing exported genesis doc: %s", err) - } + assert.NilError(t, os.Remove(tc.outputDocument)) }) } } -func setupApp(t *testing.T, tempDir string) (*simapp.SimApp, context.Context, *cmttypes.GenesisDoc, *cobra.Command) { +func setupApp(t *testing.T, tempDir string) (*simapp.SimApp, context.Context, genutiltypes.AppGenesis, *cobra.Command) { t.Helper() - if err := createConfigFolder(tempDir); err != nil { - t.Fatalf("error creating config folder: %s", err) - } + err := createConfigFolder(tempDir) + assert.NilError(t, err) logger := cmtlog.NewTMLogger(cmtlog.NewSyncWriter(os.Stdout)) db := dbm.NewMemDB() app := simapp.NewSimApp(logger, db, nil, true, simtestutil.NewAppOptionsWithFlagHome(tempDir)) genesisState := simapp.GenesisStateWithSingleValidator(t, app) - stateBytes, err := cmtjson.MarshalIndent(genesisState, "", " ") - require.NoError(t, err) + stateBytes, err := json.MarshalIndent(genesisState, "", " ") + assert.NilError(t, err) serverCtx := server.NewDefaultContext() serverCtx.Config.RootDir = tempDir clientCtx := client.Context{}.WithCodec(app.AppCodec()) - genDoc := &cmttypes.GenesisDoc{} - genDoc.ChainID = "theChainId" - genDoc.Validators = nil - genDoc.AppState = stateBytes + appGenesis := genutiltypes.AppGenesis{ + ChainID: "theChainId", + AppState: stateBytes, + Consensus: &genutiltypes.ConsensusGenesis{ + Validators: nil, + }, + } + + // save genesis file + err = genutil.ExportGenesisFile(&appGenesis, serverCtx.Config.GenesisFile()) + assert.NilError(t, err) - require.NoError(t, saveGenesisFile(genDoc, serverCtx.Config.GenesisFile())) app.InitChain( abci.RequestInitChain{ Validators: []abci.ValidatorUpdate{}, ConsensusParams: simtestutil.DefaultConsensusParams, - AppStateBytes: genDoc.AppState, + AppStateBytes: appGenesis.AppState, }, ) app.Commit() @@ -202,7 +196,6 @@ func setupApp(t *testing.T, tempDir string) (*simapp.SimApp, context.Context, *c var simApp *simapp.SimApp if height != -1 { simApp = simapp.NewSimApp(logger, db, nil, false, appOptions) - if err := simApp.LoadHeight(height); err != nil { return types.ExportedApp{}, err } @@ -217,18 +210,9 @@ func setupApp(t *testing.T, tempDir string) (*simapp.SimApp, context.Context, *c ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) - return app, ctx, genDoc, cmd + return app, ctx, appGenesis, cmd } func createConfigFolder(dir string) error { return os.Mkdir(path.Join(dir, "config"), 0o700) } - -func saveGenesisFile(genDoc *cmttypes.GenesisDoc, dir string) error { - err := genutil.ExportGenesisFile(genDoc, dir) - if err != nil { - return errorsmod.Wrap(err, "error creating file") - } - - return nil -} diff --git a/testutil/network/util.go b/testutil/network/util.go index 7608e770da..81accb7859 100644 --- a/testutil/network/util.go +++ b/testutil/network/util.go @@ -13,7 +13,7 @@ import ( pvm "github.com/cometbft/cometbft/privval" "github.com/cometbft/cometbft/proxy" "github.com/cometbft/cometbft/rpc/client/local" - "github.com/cometbft/cometbft/types" + cmttypes "github.com/cometbft/cometbft/types" cmttime "github.com/cometbft/cometbft/types/time" "github.com/cosmos/cosmos-sdk/server/api" @@ -40,14 +40,21 @@ func startInProcess(cfg Config, val *Validator) error { } app := cfg.AppConstructor(*val) - genDocProvider := node.DefaultGenesisDocProviderFunc(cmtCfg) + appGenesisProvider := func() (*cmttypes.GenesisDoc, error) { + appGenesis, err := genutiltypes.AppGenesisFromFile(cmtCfg.GenesisFile()) + if err != nil { + return nil, err + } + + return appGenesis.ToGenesisDoc() + } tmNode, err := node.NewNode( //resleak:notresource cmtCfg, pvm.LoadOrGenFilePV(cmtCfg.PrivValidatorKeyFile(), cmtCfg.PrivValidatorStateFile()), nodeKey, proxy.NewLocalClientCreator(app), - genDocProvider, + appGenesisProvider, node.DefaultDBProvider, node.DefaultMetricsProvider(cmtCfg.Instrumentation), logger.With("module", val.Moniker), @@ -123,13 +130,13 @@ func collectGenFiles(cfg Config, vals []*Validator, outputDir string) error { initCfg := genutiltypes.NewInitConfig(cfg.ChainID, gentxsDir, vals[i].NodeID, vals[i].PubKey) genFile := cmtCfg.GenesisFile() - genDoc, err := types.GenesisDocFromFile(genFile) + appGenesis, err := genutiltypes.AppGenesisFromFile(genFile) if err != nil { return err } appState, err := genutil.GenAppStateFromConfig(cfg.Codec, cfg.TxConfig, - cmtCfg, initCfg, *genDoc, banktypes.GenesisBalancesIterator{}, genutiltypes.DefaultMessageValidator) + cmtCfg, initCfg, appGenesis, banktypes.GenesisBalancesIterator{}, genutiltypes.DefaultMessageValidator) if err != nil { return err } @@ -168,15 +175,17 @@ func initGenFiles(cfg Config, genAccounts []authtypes.GenesisAccount, genBalance return err } - genDoc := types.GenesisDoc{ - ChainID: cfg.ChainID, - AppState: appGenStateJSON, - Validators: nil, + appGenesis := genutiltypes.AppGenesis{ + ChainID: cfg.ChainID, + AppState: appGenStateJSON, + Consensus: &genutiltypes.ConsensusGenesis{ + Validators: nil, + }, } // generate empty genesis files for each validator and save for i := 0; i < cfg.NumValidators; i++ { - if err := genDoc.SaveAs(genFiles[i]); err != nil { + if err := appGenesis.SaveAs(genFiles[i]); err != nil { return err } } diff --git a/testutil/sims/address_helpers.go b/testutil/sims/address_helpers.go index 0b8ede8203..8157d30586 100644 --- a/testutil/sims/address_helpers.go +++ b/testutil/sims/address_helpers.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/errors" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" ) @@ -156,7 +156,7 @@ func NewPubKeyFromHex(pk string) (res cryptotypes.PubKey) { panic(err) } if len(pkBytes) != ed25519.PubKeySize { - panic(errorsmod.Wrap(errors.ErrInvalidPubKey, "invalid pubkey size")) + panic(errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "invalid pubkey size")) } return &ed25519.PubKey{Key: pkBytes} } diff --git a/testutil/sims/state_helpers.go b/testutil/sims/state_helpers.go index 52e3240650..7143ebfbb0 100644 --- a/testutil/sims/state_helpers.go +++ b/testutil/sims/state_helpers.go @@ -9,8 +9,6 @@ import ( "time" "cosmossdk.io/math" - cmtjson "github.com/cometbft/cometbft/libs/json" - cmttypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" @@ -19,6 +17,7 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" simcli "github.com/cosmos/cosmos-sdk/x/simulation/client/cli" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -211,15 +210,14 @@ func AppStateRandomizedFn( // AppStateFromGenesisFileFn util function to generate the genesis AppState // from a genesis.json file. -func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONCodec, genesisFile string) (cmttypes.GenesisDoc, []simtypes.Account, error) { +func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONCodec, genesisFile string) (genutiltypes.AppGenesis, []simtypes.Account, error) { bytes, err := os.ReadFile(genesisFile) if err != nil { panic(err) } - var genesis cmttypes.GenesisDoc - // NOTE: CometBFT uses a custom JSON decoder for GenesisDoc - if err = cmtjson.Unmarshal(bytes, &genesis); err != nil { + var genesis genutiltypes.AppGenesis + if err = json.Unmarshal(bytes, &genesis); err != nil { return genesis, nil, err } diff --git a/x/auth/helpers/genaccounts.go b/x/auth/helpers/genaccounts.go index f2af64e4c0..817d4f7109 100644 --- a/x/auth/helpers/genaccounts.go +++ b/x/auth/helpers/genaccounts.go @@ -69,7 +69,7 @@ func AddGenesisAccount( return fmt.Errorf("failed to validate new genesis account: %w", err) } - appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genesisFileURL) + appState, appGenesis, err := genutiltypes.GenesisStateFromGenFile(genesisFileURL) if err != nil { return fmt.Errorf("failed to unmarshal genesis state: %w", err) } @@ -132,6 +132,6 @@ func AddGenesisAccount( return fmt.Errorf("failed to marshal application genesis state: %w", err) } - genDoc.AppState = appStateJSON - return genutil.ExportGenesisFile(genDoc, genesisFileURL) + appGenesis.AppState = appStateJSON + return genutil.ExportGenesisFile(appGenesis, genesisFileURL) } diff --git a/x/genutil/README.md b/x/genutil/README.md new file mode 100644 index 0000000000..90db9f4942 --- /dev/null +++ b/x/genutil/README.md @@ -0,0 +1,60 @@ +# `x/genutil` + +## Concepts + +The `genutil` package contains a variaety of genesis utility functionalities for usage within a blockchain application. Namely: + +* Genesis transactions related (gentx) +* Commands for collection and creation of gentxs +* `InitChain` processing of gentxs +* Genesis file validation +* Genesis file migration +* CometBFT related initialization + * Translation of an app genesis to a CometBFT genesis + +## Client + +### CLI + +The genutil commands are available under the `genesis` subcommand. + +#### add-genesis-account + +Add a genesis account to `genesis.json`. Learn more [here](https://docs.cosmos.network/main/run-node/run-node#adding-genesis-accounts). + +#### collect-gentxs + +Collect genesis txs and output a `genesis.json` file. + +```shell +simd genesis collect-gentxs +``` + +This will create a new `genesis.json` file that includes data from all the validators (we sometimes call it the "super genesis file" to distinguish it from single-validator genesis files). + +#### gentx + +Generate a genesis tx carrying a self delegation. + +```shell +simd genesis gentx [key_name] [amount] --chain-id [chain-id] +``` + +This will create the genesis transaction for your new chain. Here `amount` should be at least `1000000000stake`. +If you provide too much or too little, you will encounter an error when starting a node. + +#### migrate + +Migrate genesis to a specified target (SDK) version. + +```shell +simd genesis migrate [target-version] +``` + +#### validate-genesis + +Validates the genesis file at the default location or at the location passed as an argument. + +```shell +simd genesis validate-genesis +``` diff --git a/x/genutil/client/cli/collect.go b/x/genutil/client/cli/collect.go index 5f6018db74..fc001d391f 100644 --- a/x/genutil/client/cli/collect.go +++ b/x/genutil/client/cli/collect.go @@ -4,10 +4,9 @@ import ( "encoding/json" "path/filepath" - cmttypes "github.com/cometbft/cometbft/types" + "cosmossdk.io/errors" "github.com/spf13/cobra" - "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/server" @@ -36,7 +35,7 @@ func CollectGenTxsCmd(genBalIterator types.GenesisBalancesIterator, defaultNodeH return errors.Wrap(err, "failed to initialize node validator files") } - genDoc, err := cmttypes.GenesisDocFromFile(config.GenesisFile()) + appGenesis, err := types.AppGenesisFromFile(config.GenesisFile()) if err != nil { return errors.Wrap(err, "failed to read genesis doc from file") } @@ -47,12 +46,10 @@ func CollectGenTxsCmd(genBalIterator types.GenesisBalancesIterator, defaultNodeH genTxsDir = filepath.Join(config.RootDir, "config", "gentx") } - toPrint := newPrintInfo(config.Moniker, genDoc.ChainID, nodeID, genTxsDir, json.RawMessage("")) - initCfg := types.NewInitConfig(genDoc.ChainID, genTxsDir, nodeID, valPubKey) + toPrint := newPrintInfo(config.Moniker, appGenesis.ChainID, nodeID, genTxsDir, json.RawMessage("")) + initCfg := types.NewInitConfig(appGenesis.ChainID, genTxsDir, nodeID, valPubKey) - appMessage, err := genutil.GenAppStateFromConfig(cdc, - clientCtx.TxConfig, - config, initCfg, *genDoc, genBalIterator, validator) + appMessage, err := genutil.GenAppStateFromConfig(cdc, clientCtx.TxConfig, config, initCfg, appGenesis, genBalIterator, validator) if err != nil { return errors.Wrap(err, "failed to get genesis app state from config") } diff --git a/x/genutil/client/cli/gentx.go b/x/genutil/client/cli/gentx.go index f7a6fe3ec9..c5809cd102 100644 --- a/x/genutil/client/cli/gentx.go +++ b/x/genutil/client/cli/gentx.go @@ -10,7 +10,6 @@ import ( "path/filepath" "cosmossdk.io/errors" - cmttypes "github.com/cometbft/cometbft/types" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -81,13 +80,13 @@ $ %s gentx my-key-name 1000000stake --home=/path/to/home/dir --keyring-backend=o } } - genDoc, err := cmttypes.GenesisDocFromFile(config.GenesisFile()) + appGenesis, err := types.AppGenesisFromFile(config.GenesisFile()) if err != nil { return errors.Wrapf(err, "failed to read genesis doc file %s", config.GenesisFile()) } var genesisState map[string]json.RawMessage - if err = json.Unmarshal(genDoc.AppState, &genesisState); err != nil { + if err = json.Unmarshal(appGenesis.AppState, &genesisState); err != nil { return errors.Wrap(err, "failed to unmarshal genesis state") } @@ -109,7 +108,7 @@ $ %s gentx my-key-name 1000000stake --home=/path/to/home/dir --keyring-backend=o } // set flags for creating a gentx - createValCfg, err := cli.PrepareConfigForTxCreateValidator(cmd.Flags(), moniker, nodeID, genDoc.ChainID, valPubKey) + createValCfg, err := cli.PrepareConfigForTxCreateValidator(cmd.Flags(), moniker, nodeID, appGenesis.ChainID, valPubKey) if err != nil { return errors.Wrap(err, "error creating configuration to create validator msg") } diff --git a/x/genutil/client/cli/init.go b/x/genutil/client/cli/init.go index 4c2a272ab7..592a3a5546 100644 --- a/x/genutil/client/cli/init.go +++ b/x/genutil/client/cli/init.go @@ -12,7 +12,6 @@ import ( "cosmossdk.io/math/unsafe" cfg "github.com/cometbft/cometbft/config" "github.com/cometbft/cometbft/libs/cli" - "github.com/cometbft/cometbft/types" "github.com/cosmos/go-bip39" "github.com/spf13/cobra" @@ -22,7 +21,9 @@ import ( "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/genutil" + "github.com/cosmos/cosmos-sdk/x/genutil/types" ) const ( @@ -134,23 +135,27 @@ func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command { return errorsmod.Wrap(err, "Failed to marshal default genesis state") } - genDoc := &types.GenesisDoc{} + appGenesis := &types.AppGenesis{} if _, err := os.Stat(genFile); err != nil { if !os.IsNotExist(err) { return err } } else { - genDoc, err = types.GenesisDocFromFile(genFile) + appGenesis, err = types.AppGenesisFromFile(genFile) if err != nil { return errorsmod.Wrap(err, "Failed to read genesis doc from file") } } - genDoc.ChainID = chainID - genDoc.Validators = nil - genDoc.AppState = appState + appGenesis.AppName = version.AppName + appGenesis.AppVersion = version.Version + appGenesis.ChainID = chainID + appGenesis.AppState = appState + appGenesis.Consensus = &types.ConsensusGenesis{ + Validators: nil, + } - if err = genutil.ExportGenesisFile(genDoc, genFile); err != nil { + if err = genutil.ExportGenesisFile(appGenesis, genFile); err != nil { return errorsmod.Wrap(err, "Failed to export genesis file") } diff --git a/x/genutil/client/cli/init_test.go b/x/genutil/client/cli/init_test.go index af610bff00..551ccb882a 100644 --- a/x/genutil/client/cli/init_test.go +++ b/x/genutil/client/cli/init_test.go @@ -193,7 +193,7 @@ func TestEmptyState(t *testing.T) { require.Contains(t, out, "genesis_time") require.Contains(t, out, "chain_id") - require.Contains(t, out, "consensus_params") + require.Contains(t, out, "consensus") require.Contains(t, out, "app_hash") require.Contains(t, out, "app_state") } diff --git a/x/genutil/client/cli/migrate.go b/x/genutil/client/cli/migrate.go index dbd3233ac4..6ab54b8e96 100644 --- a/x/genutil/client/cli/migrate.go +++ b/x/genutil/client/cli/migrate.go @@ -6,14 +6,11 @@ import ( "sort" "time" - "cosmossdk.io/errors" - cmtjson "github.com/cometbft/cometbft/libs/json" "github.com/spf13/cobra" "golang.org/x/exp/maps" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" v043 "github.com/cosmos/cosmos-sdk/x/genutil/migrations/v043" v046 "github.com/cosmos/cosmos-sdk/x/genutil/migrations/v046" @@ -64,22 +61,26 @@ $ %s migrate v0.36 /path/to/genesis.json --chain-id=cosmoshub-3 --genesis-time=2 target := args[0] importGenesis := args[1] - genDoc, err := validateGenDoc(importGenesis) + appGenesis, err := types.AppGenesisFromFile(importGenesis) if err != nil { return err } + if err := appGenesis.ValidateAndComplete(); err != nil { + return fmt.Errorf("make sure that you have correctly migrated all CometBFT consensus params. Refer the UPGRADING.md (%s): %w", chainUpgradeGuide, err) + } + // Since some default values are valid values, we just print to // make sure the user didn't forget to update these values. - if genDoc.ConsensusParams.Evidence.MaxBytes == 0 { - fmt.Printf("Warning: consensus_params.evidence.max_bytes is set to 0. If this is"+ + if appGenesis.Consensus.Params.Evidence.MaxBytes == 0 { + fmt.Printf("Warning: consensus.params.evidence.max_bytes is set to 0. If this is"+ " deliberate, feel free to ignore this warning. If not, please have a look at the chain"+ " upgrade guide at %s.\n", chainUpgradeGuide) } var initialState types.AppMap - if err := json.Unmarshal(genDoc.AppState, &initialState); err != nil { - return errors.Wrap(err, "failed to JSON unmarshal initial genesis state") + if err := json.Unmarshal(appGenesis.AppState, &initialState); err != nil { + return fmt.Errorf("failed to JSON unmarshal initial genesis state: %w", err) } migrationFunc := GetMigrationCallback(target) @@ -90,9 +91,9 @@ $ %s migrate v0.36 /path/to/genesis.json --chain-id=cosmoshub-3 --genesis-time=2 // TODO: handler error from migrationFunc call newGenState := migrationFunc(initialState, clientCtx) - genDoc.AppState, err = json.Marshal(newGenState) + appGenesis.AppState, err = json.Marshal(newGenState) if err != nil { - return errors.Wrap(err, "failed to JSON marshal migrated genesis state") + return fmt.Errorf("failed to JSON marshal migrated genesis state: %w", err) } genesisTime, _ := cmd.Flags().GetString(flagGenesisTime) @@ -101,34 +102,39 @@ $ %s migrate v0.36 /path/to/genesis.json --chain-id=cosmoshub-3 --genesis-time=2 err := t.UnmarshalText([]byte(genesisTime)) if err != nil { - return errors.Wrap(err, "failed to unmarshal genesis time") + return fmt.Errorf("failed to unmarshal genesis time: %w", err) } - genDoc.GenesisTime = t + appGenesis.GenesisTime = t } chainID, _ := cmd.Flags().GetString(flags.FlagChainID) if chainID != "" { - genDoc.ChainID = chainID + appGenesis.ChainID = chainID } - bz, err := cmtjson.Marshal(genDoc) + bz, err := json.Marshal(appGenesis) if err != nil { - return errors.Wrap(err, "failed to marshal genesis doc") + return fmt.Errorf("failed to marshal app genesis: %w", err) } - sortedBz, err := sdk.SortJSON(bz) - if err != nil { - return errors.Wrap(err, "failed to sort JSON genesis doc") + outputDocument, _ := cmd.Flags().GetString(flags.FlagOutputDocument) + if outputDocument == "" { + cmd.Println(string(bz)) + return nil + } + + if err = appGenesis.SaveAs(outputDocument); err != nil { + return err } - cmd.Println(string(sortedBz)) return nil }, } - cmd.Flags().String(flagGenesisTime, "", "override genesis_time with this flag") - cmd.Flags().String(flags.FlagChainID, "", "override chain_id with this flag") + cmd.Flags().String(flagGenesisTime, "", "Override genesis_time with this flag") + cmd.Flags().String(flags.FlagChainID, "", "Override chain_id with this flag") + cmd.Flags().String(flags.FlagOutputDocument, "", "Exported state is written to the given file instead of STDOUT") return cmd } diff --git a/x/genutil/client/cli/migrate_test.go b/x/genutil/client/cli/migrate_test.go index fa60f80cd5..9a569a8b6e 100644 --- a/x/genutil/client/cli/migrate_test.go +++ b/x/genutil/client/cli/migrate_test.go @@ -1,10 +1,12 @@ package cli_test import ( + "os" "testing" "github.com/stretchr/testify/require" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/testutil" clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" @@ -16,7 +18,7 @@ func TestGetMigrationCallback(t *testing.T) { } } -func (s *CLITestSuite) TestMigrateGenesis() { +func TestMigrateGenesis(t *testing.T) { testCases := []struct { name string genesis string @@ -29,29 +31,41 @@ func (s *CLITestSuite) TestMigrateGenesis() { "migrate 0.37 to 0.42", v037Exported, "v0.42", - true, "Make sure that you have correctly migrated all CometBFT consensus params", func(_ string) {}, + true, "make sure that you have correctly migrated all CometBFT consensus params", func(_ string) {}, }, { - "migrate 0.42 to 0.43", - v040Valid, - "v0.43", - false, "", - func(jsonOut string) { - // Make sure the json output contains the ADR-037 gov weighted votes. - s.Require().Contains(jsonOut, "\"weight\":\"1.000000000000000000\"") - }, + "invalid target version", + func() string { + bz, err := os.ReadFile("../../types/testdata/app_genesis.json") + require.NoError(t, err) + + return string(bz) + }(), + "v0.10", + true, "unknown migration function for version: v0.10", func(_ string) {}, + }, + { + "invalid target version", + func() string { + bz, err := os.ReadFile("../../types/testdata/cmt_genesis.json") + require.NoError(t, err) + + return string(bz) + }(), + "v0.10", + true, "unknown migration function for version: v0.10", func(_ string) {}, }, } for _, tc := range testCases { tc := tc - s.Run(tc.name, func() { - genesisFile := testutil.WriteToNewTempFile(s.T(), tc.genesis) - jsonOutput, err := clitestutil.ExecTestCLICmd(s.clientCtx, cli.MigrateGenesisCmd(), []string{tc.target, genesisFile.Name()}) + t.Run(tc.name, func(t *testing.T) { + genesisFile := testutil.WriteToNewTempFile(t, tc.genesis) + jsonOutput, err := clitestutil.ExecTestCLICmd(client.Context{}, cli.MigrateGenesisCmd(), []string{tc.target, genesisFile.Name()}) if tc.expErr { - s.Require().Contains(err.Error(), tc.expErrMsg) + require.Contains(t, err.Error(), tc.expErrMsg) } else { - s.Require().NoError(err) + require.NoError(t, err) tc.check(jsonOutput.String()) } }) diff --git a/x/genutil/client/cli/validate_genesis.go b/x/genutil/client/cli/validate_genesis.go index 062e2304da..a565a37f36 100644 --- a/x/genutil/client/cli/validate_genesis.go +++ b/x/genutil/client/cli/validate_genesis.go @@ -4,12 +4,12 @@ import ( "encoding/json" "fmt" - cmttypes "github.com/cometbft/cometbft/types" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/genutil/types" ) const chainUpgradeGuide = "https://github.com/cosmos/cosmos-sdk/blob/main/UPGRADING.md" @@ -17,9 +17,10 @@ const chainUpgradeGuide = "https://github.com/cosmos/cosmos-sdk/blob/main/UPGRAD // ValidateGenesisCmd takes a genesis file, and makes sure that it is valid. func ValidateGenesisCmd(mbm module.BasicManager) *cobra.Command { return &cobra.Command{ - Use: "validate-genesis [file]", - Args: cobra.RangeArgs(0, 1), - Short: "validates the genesis file at the default location or at the location passed as an arg", + Use: "validate [file]", + Aliases: []string{"validate-genesis"}, + Args: cobra.RangeArgs(0, 1), + Short: "Validates the genesis file at the default location or at the location passed as an arg", RunE: func(cmd *cobra.Command, args []string) (err error) { serverCtx := server.GetServerContextFromCmd(cmd) clientCtx := client.GetClientContextFromCmd(cmd) @@ -34,13 +35,17 @@ func ValidateGenesisCmd(mbm module.BasicManager) *cobra.Command { genesis = args[0] } - genDoc, err := validateGenDoc(genesis) + appGenesis, err := types.AppGenesisFromFile(genesis) if err != nil { return err } + if err := appGenesis.ValidateAndComplete(); err != nil { + return fmt.Errorf("make sure that you have correctly migrated all CometBFT consensus params. Refer the UPGRADING.md (%s): %w", chainUpgradeGuide, err) + } + var genState map[string]json.RawMessage - if err = json.Unmarshal(genDoc.AppState, &genState); err != nil { + if err = json.Unmarshal(appGenesis.AppState, &genState); err != nil { return fmt.Errorf("error unmarshalling genesis doc %s: %s", genesis, err.Error()) } @@ -53,19 +58,3 @@ func ValidateGenesisCmd(mbm module.BasicManager) *cobra.Command { }, } } - -// validateGenDoc reads a genesis file and validates that it is a correct -// CometBFT GenesisDoc. This function does not do any cosmos-related -// validation. -func validateGenDoc(importGenesisFile string) (*cmttypes.GenesisDoc, error) { - genDoc, err := cmttypes.GenesisDocFromFile(importGenesisFile) - if err != nil { - return nil, fmt.Errorf("%s. Make sure that"+ - " you have correctly migrated all CometBFT consensus params, please see the"+ - " chain migration guide at %s for more info", - err.Error(), chainUpgradeGuide, - ) - } - - return genDoc, nil -} diff --git a/x/genutil/client/cli/validate_genesis_test.go b/x/genutil/client/cli/validate_genesis_test.go index 90223ea45f..a241bf1f9b 100644 --- a/x/genutil/client/cli/validate_genesis_test.go +++ b/x/genutil/client/cli/validate_genesis_test.go @@ -1,9 +1,14 @@ package cli_test import ( + "os" + "testing" + + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/testutil" clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + "github.com/stretchr/testify/require" ) // An example exported genesis file from a 0.37 chain. Note that evidence @@ -25,48 +30,7 @@ var v037Exported = `{ "validators": [] }` -// An example exported genesis file that's 0.40 compatible. -// We added the following app_state: -// -// - x/gov: added votes to test ADR-037 split votes migration. -var v040Valid = `{ - "app_hash": "", - "app_state": { - "gov": { - "starting_proposal_id": "0", - "deposits": [], - "votes": [ - { - "proposal_id": "5", - "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh", - "option": "VOTE_OPTION_YES" - } - ], - "proposals": [], - "deposit_params": { "min_deposit": [], "max_deposit_period": "0s" }, - "voting_params": { "voting_period": "0s" }, - "tally_params": { "quorum": "0", "threshold": "0", "veto_threshold": "0" } - } - }, - "chain_id": "test", - "consensus_params": { - "block": { - "max_bytes": "22020096", - "max_gas": "-1", - "time_iota_ms": "1000" - }, - "evidence": { - "max_age_num_blocks": "100000", - "max_age_duration": "172800000000000", - "max_bytes": "0" - }, - "validator": { "pub_key_types": ["ed25519"] } - }, - "genesis_time": "2020-09-29T20:16:29.172362037Z", - "validators": [] -}` - -func (s *CLITestSuite) TestValidateGenesis() { +func TestValidateGenesis(t *testing.T) { testCases := []struct { name string genesis string @@ -78,21 +42,27 @@ func (s *CLITestSuite) TestValidateGenesis() { true, }, { - "valid 0.40 genesis file", - v040Valid, + "valid 0.48 genesis file", + func() string { + bz, err := os.ReadFile("../../types/testdata/app_genesis.json") + require.NoError(t, err) + + return string(bz) + }(), false, }, } for _, tc := range testCases { tc := tc - s.Run(tc.name, func() { - genesisFile := testutil.WriteToNewTempFile(s.T(), tc.genesis) - _, err := clitestutil.ExecTestCLICmd(s.clientCtx, cli.ValidateGenesisCmd(nil), []string{genesisFile.Name()}) + + t.Run(tc.name, func(t *testing.T) { + genesisFile := testutil.WriteToNewTempFile(t, tc.genesis) + _, err := clitestutil.ExecTestCLICmd(client.Context{}, cli.ValidateGenesisCmd(nil), []string{genesisFile.Name()}) if tc.expErr { - s.Require().Contains(err.Error(), "Make sure that you have correctly migrated all CometBFT consensus params") + require.Contains(t, err.Error(), "make sure that you have correctly migrated all CometBFT consensus params") } else { - s.Require().NoError(err) + require.NoError(t, err) } }) } diff --git a/x/genutil/collect.go b/x/genutil/collect.go index 8a8f856ea0..de01c07155 100644 --- a/x/genutil/collect.go +++ b/x/genutil/collect.go @@ -11,7 +11,6 @@ import ( "strings" cfg "github.com/cometbft/cometbft/config" - cmttypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" @@ -23,12 +22,12 @@ import ( // GenAppStateFromConfig gets the genesis app state from the config func GenAppStateFromConfig(cdc codec.JSONCodec, txEncodingConfig client.TxEncodingConfig, - config *cfg.Config, initCfg types.InitConfig, genDoc cmttypes.GenesisDoc, genBalIterator types.GenesisBalancesIterator, + config *cfg.Config, initCfg types.InitConfig, genesis *types.AppGenesis, genBalIterator types.GenesisBalancesIterator, validator types.MessageValidator, ) (appState json.RawMessage, err error) { // process genesis transactions, else create default genesis.json appGenTxs, persistentPeers, err := CollectTxs( - cdc, txEncodingConfig.TxJSONDecoder(), config.Moniker, initCfg.GenTxsDir, genDoc, genBalIterator, validator) + cdc, txEncodingConfig.TxJSONDecoder(), config.Moniker, initCfg.GenTxsDir, genesis, genBalIterator, validator) if err != nil { return appState, err } @@ -42,7 +41,7 @@ func GenAppStateFromConfig(cdc codec.JSONCodec, txEncodingConfig client.TxEncodi } // create the app state - appGenesisState, err := types.GenesisStateFromGenDoc(genDoc) + appGenesisState, err := types.GenesisStateFromAppGenesis(genesis) if err != nil { return appState, err } @@ -57,8 +56,8 @@ func GenAppStateFromConfig(cdc codec.JSONCodec, txEncodingConfig client.TxEncodi return appState, err } - genDoc.AppState = appState - err = ExportGenesisFile(&genDoc, config.GenesisFile()) + genesis.AppState = appState + err = ExportGenesisFile(genesis, config.GenesisFile()) return appState, err } @@ -66,13 +65,13 @@ func GenAppStateFromConfig(cdc codec.JSONCodec, txEncodingConfig client.TxEncodi // CollectTxs processes and validates application's genesis Txs and returns // the list of appGenTxs, and persistent peers required to generate genesis.json. func CollectTxs(cdc codec.JSONCodec, txJSONDecoder sdk.TxDecoder, moniker, genTxsDir string, - genDoc cmttypes.GenesisDoc, genBalIterator types.GenesisBalancesIterator, + genesis *types.AppGenesis, genBalIterator types.GenesisBalancesIterator, validator types.MessageValidator, ) (appGenTxs []sdk.Tx, persistentPeers string, err error) { // prepare a map of all balances in genesis state to then validate // against the validators addresses var appState map[string]json.RawMessage - if err := json.Unmarshal(genDoc.AppState, &appState); err != nil { + if err := json.Unmarshal(genesis.AppState, &appState); err != nil { return appGenTxs, persistentPeers, err } diff --git a/x/genutil/collect_test.go b/x/genutil/collect_test.go index ac1a9f66bb..3f0a32a079 100644 --- a/x/genutil/collect_test.go +++ b/x/genutil/collect_test.go @@ -8,15 +8,13 @@ import ( "github.com/cosmos/gogoproto/proto" - cmttypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/cosmos-sdk/codec" cdctypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/types" + sdk "github.com/cosmos/cosmos-sdk/types" bankexported "github.com/cosmos/cosmos-sdk/x/bank/exported" "github.com/cosmos/cosmos-sdk/x/genutil" - gtypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/genutil/types" ) type doNothingUnmarshalJSON struct { @@ -28,7 +26,7 @@ func (dnj *doNothingUnmarshalJSON) UnmarshalJSON(_ []byte, _ proto.Message) erro } type doNothingIterator struct { - gtypes.GenesisBalancesIterator + types.GenesisBalancesIterator } func (dni *doNothingIterator) IterateGenesisBalances(_ codec.JSONCodec, _ map[string]json.RawMessage, _ func(bankexported.GenesisBalance) bool) { @@ -49,7 +47,7 @@ func TestCollectTxsHandlesDirectories(t *testing.T) { t.Fatal(err) } - txDecoder := types.TxDecoder(func(txBytes []byte) (types.Tx, error) { + txDecoder := sdk.TxDecoder(func(txBytes []byte) (sdk.Tx, error) { return nil, nil }) @@ -57,11 +55,11 @@ func TestCollectTxsHandlesDirectories(t *testing.T) { srvCtx := server.NewDefaultContext() _ = srvCtx cdc := codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) - gdoc := cmttypes.GenesisDoc{AppState: []byte("{}")} + genesis := &types.AppGenesis{AppState: []byte("{}")} balItr := new(doNothingIterator) dnc := &doNothingUnmarshalJSON{cdc} - if _, _, err := genutil.CollectTxs(dnc, txDecoder, "foo", testDir, gdoc, balItr, gtypes.DefaultMessageValidator); err != nil { + if _, _, err := genutil.CollectTxs(dnc, txDecoder, "foo", testDir, genesis, balItr, types.DefaultMessageValidator); err != nil { t.Fatal(err) } } diff --git a/x/genutil/doc.go b/x/genutil/doc.go index 43b713b145..daec4032a9 100644 --- a/x/genutil/doc.go +++ b/x/genutil/doc.go @@ -2,9 +2,10 @@ Package genutil contains a variety of genesis utility functionality for usage within a blockchain application. Namely: - Genesis transactions related (gentx) - - commands for collection and creation of gentxs - - initchain processing of gentxs + - Commands for collection and creation of gentxs + - `InitChain` processing of gentxs - Genesis file validation - - CometBFT related initialization + - Genesis file migration + - CometBFT related initialization (Translation of an app genesis to a CometBFT genesis) */ package genutil diff --git a/x/genutil/types/genesis.go b/x/genutil/types/genesis.go new file mode 100644 index 0000000000..ffd601d2b7 --- /dev/null +++ b/x/genutil/types/genesis.go @@ -0,0 +1,211 @@ +package types + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "os" + "time" + + cmtjson "github.com/cometbft/cometbft/libs/json" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" + + "github.com/cosmos/cosmos-sdk/version" +) + +const ( + // MaxChainIDLen is the maximum length of a chain ID. + MaxChainIDLen = cmttypes.MaxChainIDLen +) + +// AppGenesis defines the app's genesis. +type AppGenesis struct { + AppName string `json:"app_name"` + AppVersion string `json:"app_version"` + GenesisTime time.Time `json:"genesis_time"` + ChainID string `json:"chain_id"` + InitialHeight int64 `json:"initial_height"` + AppHash []byte `json:"app_hash"` + AppState json.RawMessage `json:"app_state,omitempty"` + Consensus *ConsensusGenesis `json:"consensus,omitempty"` +} + +// NewAppGenesisWithVersion returns a new AppGenesis with the app name and app version already. +func NewAppGenesisWithVersion(chainID string, appState json.RawMessage) *AppGenesis { + return &AppGenesis{ + AppName: version.AppName, + AppVersion: version.Version, + ChainID: chainID, + AppState: appState, + Consensus: &ConsensusGenesis{ + Validators: nil, + }, + } +} + +// ValidateAndComplete performs validation and completes the AppGenesis. +func (ag *AppGenesis) ValidateAndComplete() error { + if ag.ChainID == "" { + return errors.New("genesis doc must include non-empty chain_id") + } + + if len(ag.ChainID) > MaxChainIDLen { + return fmt.Errorf("chain_id in genesis doc is too long (max: %d)", MaxChainIDLen) + } + + if ag.InitialHeight < 0 { + return fmt.Errorf("initial_height cannot be negative (got %v)", ag.InitialHeight) + } + + if ag.InitialHeight == 0 { + ag.InitialHeight = 1 + } + + if ag.GenesisTime.IsZero() { + ag.GenesisTime = cmttime.Now() + } + + if err := ag.Consensus.ValidateAndComplete(); err != nil { + return err + } + + return nil +} + +// SaveAs is a utility method for saving AppGenesis as a JSON file. +func (ag *AppGenesis) SaveAs(file string) error { + appGenesisBytes, err := json.MarshalIndent(ag, "", " ") + if err != nil { + return err + } + + return os.WriteFile(file, appGenesisBytes, 0o600) +} + +// AppGenesisFromFile reads the AppGenesis from the provided file. +func AppGenesisFromFile(genFile string) (*AppGenesis, error) { + jsonBlob, err := os.ReadFile(genFile) + if err != nil { + return nil, fmt.Errorf("couldn't read AppGenesis file (%s): %w", genFile, err) + } + + var appGenesis AppGenesis + if err := json.Unmarshal(jsonBlob, &appGenesis); err != nil { + // fallback to CometBFT genesis + var ctmGenesis cmttypes.GenesisDoc + if err2 := cmtjson.Unmarshal(jsonBlob, &ctmGenesis); err2 != nil { + return nil, fmt.Errorf("error unmarshalling AppGenesis at %s: %w\n failed fallback to CometBFT GenDoc: %w", genFile, err, err2) + } + + appGenesis = AppGenesis{ + GenesisTime: ctmGenesis.GenesisTime, + ChainID: ctmGenesis.ChainID, + InitialHeight: ctmGenesis.InitialHeight, + AppHash: ctmGenesis.AppHash, + AppState: ctmGenesis.AppState, + Consensus: &ConsensusGenesis{ + Validators: ctmGenesis.Validators, + Params: ctmGenesis.ConsensusParams, + }, + } + } + + return &appGenesis, nil +} + +// -------------------------- +// CometBFT Genesis Handling +// -------------------------- + +// ToGenesisDoc converts the AppGenesis to a CometBFT GenesisDoc. +func (ag *AppGenesis) ToGenesisDoc() (*cmttypes.GenesisDoc, error) { + return &cmttypes.GenesisDoc{ + GenesisTime: ag.GenesisTime, + ChainID: ag.ChainID, + InitialHeight: ag.InitialHeight, + AppHash: ag.AppHash, + AppState: ag.AppState, + Validators: ag.Consensus.Validators, + ConsensusParams: ag.Consensus.Params, + }, nil +} + +// ConsensusGenesis defines the consensus layer's genesis. +// TODO(@julienrbrt) eventually abstract from CometBFT types +type ConsensusGenesis struct { + Validators []cmttypes.GenesisValidator `json:"validators,omitempty"` + Params *cmttypes.ConsensusParams `json:"params,omitempty"` +} + +// NewConsensusGenesis returns a ConsensusGenesis with given values. +// It takes a proto consensus params so it can called from server export command. +func NewConsensusGenesis(params *cmtproto.ConsensusParams, validators []cmttypes.GenesisValidator) *ConsensusGenesis { + return &ConsensusGenesis{ + Params: &cmttypes.ConsensusParams{ + Block: cmttypes.BlockParams{ + MaxBytes: params.Block.MaxBytes, + MaxGas: params.Block.MaxGas, + }, + Evidence: cmttypes.EvidenceParams{ + MaxAgeNumBlocks: params.Evidence.MaxAgeNumBlocks, + MaxAgeDuration: params.Evidence.MaxAgeDuration, + MaxBytes: params.Evidence.MaxBytes, + }, + Validator: cmttypes.ValidatorParams{ + PubKeyTypes: params.Validator.PubKeyTypes, + }, + }, + Validators: validators, + } +} + +func (cs *ConsensusGenesis) MarshalJSON() ([]byte, error) { + type Alias ConsensusGenesis + return cmtjson.Marshal(&Alias{ + Validators: cs.Validators, + Params: cs.Params, + }) +} + +func (cs *ConsensusGenesis) UnmarshalJSON(b []byte) error { + type Alias ConsensusGenesis + + result := Alias{} + if err := cmtjson.Unmarshal(b, &result); err != nil { + return err + } + + cs.Params = result.Params + cs.Validators = result.Validators + + return nil +} + +func (cs *ConsensusGenesis) ValidateAndComplete() error { + if cs == nil { + return fmt.Errorf("consensus genesis cannot be nil") + } + + if cs.Params == nil { + cs.Params = cmttypes.DefaultConsensusParams() + } else if err := cs.Params.ValidateBasic(); err != nil { + return err + } + + for i, v := range cs.Validators { + if v.Power == 0 { + return fmt.Errorf("the genesis file cannot contain validators with no voting power: %v", v) + } + if len(v.Address) > 0 && !bytes.Equal(v.PubKey.Address(), v.Address) { + return fmt.Errorf("incorrect address for validator %v in the genesis file, should be %v", v, v.PubKey.Address()) + } + if len(v.Address) == 0 { + cs.Validators[i].Address = v.PubKey.Address() + } + } + + return nil +} diff --git a/x/genutil/types/genesis_state.go b/x/genutil/types/genesis_state.go index f1b4f6884d..421319adc3 100644 --- a/x/genutil/types/genesis_state.go +++ b/x/genutil/types/genesis_state.go @@ -5,8 +5,6 @@ import ( "fmt" "os" - cmttypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -62,12 +60,12 @@ func SetGenesisStateInAppState( return appState } -// GenesisStateFromGenDoc creates the core parameters for genesis initialization +// GenesisStateFromAppGenesis creates the core parameters for genesis initialization // for the application. // // NOTE: The pubkey input is this machines pubkey. -func GenesisStateFromGenDoc(genDoc cmttypes.GenesisDoc) (genesisState map[string]json.RawMessage, err error) { - if err = json.Unmarshal(genDoc.AppState, &genesisState); err != nil { +func GenesisStateFromAppGenesis(gesnsis *AppGenesis) (genesisState map[string]json.RawMessage, err error) { + if err = json.Unmarshal(gesnsis.AppState, &genesisState); err != nil { return genesisState, err } return genesisState, nil @@ -77,19 +75,18 @@ func GenesisStateFromGenDoc(genDoc cmttypes.GenesisDoc) (genesisState map[string // for the application. // // NOTE: The pubkey input is this machines pubkey. -func GenesisStateFromGenFile(genFile string) (genesisState map[string]json.RawMessage, genDoc *cmttypes.GenesisDoc, err error) { +func GenesisStateFromGenFile(genFile string) (genesisState map[string]json.RawMessage, genesis *AppGenesis, err error) { if _, err := os.Stat(genFile); os.IsNotExist(err) { - return genesisState, genDoc, - fmt.Errorf("%s does not exist, run `init` first", genFile) + return genesisState, genesis, fmt.Errorf("%s does not exist, run `init` first", genFile) } - genDoc, err = cmttypes.GenesisDocFromFile(genFile) + genesis, err = AppGenesisFromFile(genFile) if err != nil { - return genesisState, genDoc, err + return genesisState, genesis, err } - genesisState, err = GenesisStateFromGenDoc(*genDoc) - return genesisState, genDoc, err + genesisState, err = GenesisStateFromAppGenesis(genesis) + return genesisState, genesis, err } // ValidateGenesis validates GenTx transactions diff --git a/x/genutil/types/genesis_test.go b/x/genutil/types/genesis_test.go new file mode 100644 index 0000000000..811c285cee --- /dev/null +++ b/x/genutil/types/genesis_test.go @@ -0,0 +1,69 @@ +package types_test + +import ( + "encoding/json" + "os" + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/golden" + + cmttypes "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/x/genutil/types" +) + +func TestAppGenesis_Marshal(t *testing.T) { + genesis := types.AppGenesis{ + AppName: "simapp", + AppVersion: "0.1.0", + ChainID: "test", + } + + out, err := json.Marshal(&genesis) + assert.NilError(t, err) + assert.Equal(t, string(out), `{"app_name":"simapp","app_version":"0.1.0","genesis_time":"0001-01-01T00:00:00Z","chain_id":"test","initial_height":0,"app_hash":null}`) +} + +func TestAppGenesis_Unmarshal(t *testing.T) { + jsonBlob, err := os.ReadFile("testdata/app_genesis.json") + assert.NilError(t, err) + + var genesis types.AppGenesis + err = json.Unmarshal(jsonBlob, &genesis) + assert.NilError(t, err) + + assert.DeepEqual(t, genesis.ChainID, "demo") + assert.DeepEqual(t, genesis.Consensus.Params.Block.MaxBytes, int64(22020096)) +} + +func TestAppGenesis_ValidGenesis(t *testing.T) { + // validate can read cometbft genesis file + genesis, err := types.AppGenesisFromFile("testdata/cmt_genesis.json") + assert.NilError(t, err) + + assert.DeepEqual(t, genesis.ChainID, "demo") + assert.DeepEqual(t, genesis.Consensus.Validators[0].Name, "test") + + // validate the app genesis can be translated properly to cometbft genesis + cmtGenesis, err := genesis.ToGenesisDoc() + assert.NilError(t, err) + rawCmtGenesis, err := cmttypes.GenesisDocFromFile("testdata/cmt_genesis.json") + assert.NilError(t, err) + assert.DeepEqual(t, cmtGenesis, rawCmtGenesis) + + // validate can properly marshal to app genesis file + rawAppGenesis, err := json.Marshal(&genesis) + assert.NilError(t, err) + golden.Assert(t, string(rawAppGenesis), "app_genesis.json") + + // validate the app genesis can be unmarshalled properly + var appGenesis types.AppGenesis + err = json.Unmarshal(rawAppGenesis, &appGenesis) + assert.NilError(t, err) + assert.DeepEqual(t, appGenesis.Consensus.Params, genesis.Consensus.Params) + + // validate marshalling of app genesis + rawAppGenesis, err = json.Marshal(&appGenesis) + assert.NilError(t, err) + golden.Assert(t, string(rawAppGenesis), "app_genesis.json") +} diff --git a/x/genutil/types/testdata/app_genesis.json b/x/genutil/types/testdata/app_genesis.json new file mode 100644 index 0000000000..450a8f2781 --- /dev/null +++ b/x/genutil/types/testdata/app_genesis.json @@ -0,0 +1 @@ +{"app_name":"","app_version":"","genesis_time":"2023-02-20T11:08:30.588307671Z","chain_id":"demo","initial_height":48,"app_hash":"","app_state":{"auth":{"accounts":[{"@type":"/cosmos.auth.v1beta1.BaseAccount","account_number":"1","address":"cosmos1qmkksxlxqdslq6kkca25m4jn344nx29lytq8f9","pub_key":null,"sequence":"0"},{"@type":"/cosmos.auth.v1beta1.BaseAccount","account_number":"8","address":"cosmos1pnt5523etwtzv6mj7haryfw6w8h5tkcuhd99m8","pub_key":null,"sequence":"0"},{"@type":"/cosmos.auth.v1beta1.ModuleAccount","base_account":{"account_number":"4","address":"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh","pub_key":null,"sequence":"0"},"name":"bonded_tokens_pool","permissions":["burner","staking"]},{"@type":"/cosmos.auth.v1beta1.ModuleAccount","base_account":{"account_number":"5","address":"cosmos1tygms3xhhs3yv487phx3dw4a95jn7t7lpm470r","pub_key":null,"sequence":"0"},"name":"not_bonded_tokens_pool","permissions":["burner","staking"]},{"@type":"/cosmos.auth.v1beta1.ModuleAccount","base_account":{"account_number":"6","address":"cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn","pub_key":null,"sequence":"0"},"name":"gov","permissions":["burner"]},{"@type":"/cosmos.auth.v1beta1.ModuleAccount","base_account":{"account_number":"3","address":"cosmos1jv65s3grqf6v6jl3dp4t6c9t9rk99cd88lyufl","pub_key":null,"sequence":"0"},"name":"distribution","permissions":[]},{"@type":"/cosmos.auth.v1beta1.BaseAccount","account_number":"0","address":"cosmos15jenkldw6348lpgdev3vjzw90zzknxa9a3vg0j","pub_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A3uyZdXedyvYx9VCL6xRjkxtcFpgxjhXFIz9b2mWz+aV"},"sequence":"4"},{"@type":"/cosmos.auth.v1beta1.ModuleAccount","base_account":{"account_number":"7","address":"cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q","pub_key":null,"sequence":"0"},"name":"mint","permissions":["minter"]},{"@type":"/cosmos.auth.v1beta1.ModuleAccount","base_account":{"account_number":"2","address":"cosmos17xpfvakm2amg962yls6f84z3kell8c5lserqta","pub_key":null,"sequence":"0"},"name":"fee_collector","permissions":[]}],"params":{"max_memo_characters":"256","sig_verify_cost_ed25519":"590","sig_verify_cost_secp256k1":"1000","tx_sig_limit":"7","tx_size_cost_per_byte":"10"}},"authz":{"authorization":[]},"bank":{"balances":[{"address":"cosmos1qmkksxlxqdslq6kkca25m4jn344nx29lytq8f9","coins":[{"amount":"5000000000","denom":"stake"}]},{"address":"cosmos1pnt5523etwtzv6mj7haryfw6w8h5tkcuhd99m8","coins":[{"amount":"1000","denom":"stake"}]},{"address":"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh","coins":[{"amount":"1000000","denom":"stake"}]},{"address":"cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn","coins":[{"amount":"10010000","denom":"stake"}]},{"address":"cosmos1jv65s3grqf6v6jl3dp4t6c9t9rk99cd88lyufl","coins":[{"amount":"9635","denom":"stake"}]},{"address":"cosmos15jenkldw6348lpgdev3vjzw90zzknxa9a3vg0j","coins":[{"amount":"4988989000","denom":"stake"}]}],"denom_metadata":[],"params":{"default_send_enabled":true,"send_enabled":[]},"send_enabled":[],"supply":[{"amount":"10000009635","denom":"stake"}]},"capability":{"index":"1","owners":[]},"consensus":null,"crisis":{"constant_fee":{"amount":"1000","denom":"stake"}},"distribution":{"delegator_starting_infos":[{"delegator_address":"cosmos15jenkldw6348lpgdev3vjzw90zzknxa9a3vg0j","starting_info":{"height":"0","previous_period":"1","stake":"1000000.000000000000000000"},"validator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp"}],"delegator_withdraw_infos":[],"fee_pool":{"community_pool":[{"amount":"192.700000000000000000","denom":"stake"}]},"outstanding_rewards":[{"outstanding_rewards":[{"amount":"9442.300000000000000000","denom":"stake"}],"validator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp"}],"params":{"base_proposer_reward":"0.000000000000000000","bonus_proposer_reward":"0.000000000000000000","community_tax":"0.020000000000000000","withdraw_addr_enabled":true},"previous_proposer":"cosmosvalcons16vm0nx49eam4q0xasdnwdzsdl6ymgyjt757sgr","validator_accumulated_commissions":[{"accumulated":{"commission":[{"amount":"944.230000000000000000","denom":"stake"}]},"validator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp"}],"validator_current_rewards":[{"rewards":{"period":"2","rewards":[{"amount":"8498.070000000000000000","denom":"stake"}]},"validator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp"}],"validator_historical_rewards":[{"period":"1","rewards":{"cumulative_reward_ratio":[],"reference_count":2},"validator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp"}],"validator_slash_events":[]},"evidence":{"evidence":[]},"feegrant":{"allowances":[]},"genutil":{"gen_txs":[]},"gov":{"deposit_params":null,"deposits":[{"amount":[{"amount":"10010000","denom":"stake"}],"depositor":"cosmos15jenkldw6348lpgdev3vjzw90zzknxa9a3vg0j","proposal_id":"1"}],"params":{"expedited_min_deposit":[{"amount":"50000000","denom":"stake"}],"expedited_threshold":"0.667000000000000000","expedited_voting_period":"86400s","max_deposit_period":"172800s","min_deposit":[{"amount":"10000000","denom":"stake"}],"min_initial_deposit_ratio":"0.000000000000000000","proposal_cancel_dest":"","proposal_cancel_ratio":"0.500000000000000000","quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000","voting_period":"172800s"},"proposals":[{"deposit_end_time":"2023-02-22T11:11:52.776167376Z","expedited":false,"final_tally_result":{"abstain_count":"0","no_count":"0","no_with_veto_count":"0","yes_count":"0"},"id":"1","messages":[{"@type":"/cosmos.distribution.v1beta1.MsgCommunityPoolSpend","amount":[],"authority":"cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn","recipient":"cosmos1pnt5523etwtzv6mj7haryfw6w8h5tkcuhd99m8"}],"metadata":"ipfs://CID","proposer":"cosmos15jenkldw6348lpgdev3vjzw90zzknxa9a3vg0j","status":"PROPOSAL_STATUS_VOTING_PERIOD","submit_time":"2023-02-20T11:11:52.776167376Z","summary":"test proposal","title":"test proposal","total_deposit":[{"amount":"10010000","denom":"stake"}],"voting_end_time":"2023-02-22T11:12:07.801161984Z","voting_start_time":"2023-02-20T11:12:07.801161984Z"}],"starting_proposal_id":"2","tally_params":null,"votes":[],"voting_params":null},"group":{"group_members":[],"group_policies":[],"group_policy_seq":"0","group_seq":"0","groups":[],"proposal_seq":"0","proposals":[],"votes":[]},"mint":{"minter":{"annual_provisions":"1300010905.175073197786747950","inflation":"0.130000967926594565"},"params":{"blocks_per_year":"6311520","goal_bonded":"0.670000000000000000","inflation_max":"0.200000000000000000","inflation_min":"0.070000000000000000","inflation_rate_change":"0.130000000000000000","mint_denom":"stake"}},"nft":{"classes":[],"entries":[]},"params":null,"slashing":{"missed_blocks":[{"address":"cosmosvalcons16vm0nx49eam4q0xasdnwdzsdl6ymgyjt757sgr","missed_blocks":[]}],"params":{"downtime_jail_duration":"600s","min_signed_per_window":"0.500000000000000000","signed_blocks_window":"100","slash_fraction_double_sign":"0.050000000000000000","slash_fraction_downtime":"0.010000000000000000"},"signing_infos":[{"address":"cosmosvalcons16vm0nx49eam4q0xasdnwdzsdl6ymgyjt757sgr","validator_signing_info":{"address":"cosmosvalcons16vm0nx49eam4q0xasdnwdzsdl6ymgyjt757sgr","index_offset":"46","jailed_until":"1970-01-01T00:00:00Z","missed_blocks_counter":"0","start_height":"0","tombstoned":false}}]},"staking":{"delegations":[{"delegator_address":"cosmos15jenkldw6348lpgdev3vjzw90zzknxa9a3vg0j","shares":"1000000.000000000000000000","validator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp"}],"exported":true,"last_total_power":"1","last_validator_powers":[{"address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp","power":"1"}],"params":{"bond_denom":"stake","historical_entries":10000,"max_entries":7,"max_validators":100,"min_commission_rate":"0.000000000000000000","unbonding_time":"1814400s"},"redelegations":[],"unbonding_delegations":[],"validators":[{"commission":{"commission_rates":{"max_change_rate":"0.010000000000000000","max_rate":"0.200000000000000000","rate":"0.100000000000000000"},"update_time":"2023-02-20T11:08:30.588307671Z"},"consensus_pubkey":{"@type":"/cosmos.crypto.ed25519.PubKey","key":"tMZonPQYoooG/xbFVhHg95pTLxx7aO43/qgHFxDagWM="},"delegator_shares":"1000000.000000000000000000","description":{"details":"","identity":"","moniker":"test","security_contact":"","website":""},"jailed":false,"min_self_delegation":"1","operator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp","status":"BOND_STATUS_BONDED","tokens":"1000000","unbonding_height":"0","unbonding_ids":[],"unbonding_on_hold_ref_count":"0","unbonding_time":"1970-01-01T00:00:00Z"}]},"upgrade":{},"vesting":{}},"consensus":{"validators":[{"address":"D336F99AA5CF77503CDD8366E68A0DFE89B4124B","pub_key":{"type":"tendermint/PubKeyEd25519","value":"tMZonPQYoooG/xbFVhHg95pTLxx7aO43/qgHFxDagWM="},"power":"1","name":"test"}],"params":{"block":{"max_bytes":"22020096","max_gas":"-1"},"evidence":{"max_age_num_blocks":"100000","max_age_duration":"172800000000000","max_bytes":"1048576"},"validator":{"pub_key_types":["ed25519"]},"version":{"app":"0"}}}} \ No newline at end of file diff --git a/x/genutil/types/testdata/cmt_genesis.json b/x/genutil/types/testdata/cmt_genesis.json new file mode 100644 index 0000000000..96275f7180 --- /dev/null +++ b/x/genutil/types/testdata/cmt_genesis.json @@ -0,0 +1 @@ +{"app_hash":"","app_state":{"auth":{"accounts":[{"@type":"/cosmos.auth.v1beta1.BaseAccount","account_number":"1","address":"cosmos1qmkksxlxqdslq6kkca25m4jn344nx29lytq8f9","pub_key":null,"sequence":"0"},{"@type":"/cosmos.auth.v1beta1.BaseAccount","account_number":"8","address":"cosmos1pnt5523etwtzv6mj7haryfw6w8h5tkcuhd99m8","pub_key":null,"sequence":"0"},{"@type":"/cosmos.auth.v1beta1.ModuleAccount","base_account":{"account_number":"4","address":"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh","pub_key":null,"sequence":"0"},"name":"bonded_tokens_pool","permissions":["burner","staking"]},{"@type":"/cosmos.auth.v1beta1.ModuleAccount","base_account":{"account_number":"5","address":"cosmos1tygms3xhhs3yv487phx3dw4a95jn7t7lpm470r","pub_key":null,"sequence":"0"},"name":"not_bonded_tokens_pool","permissions":["burner","staking"]},{"@type":"/cosmos.auth.v1beta1.ModuleAccount","base_account":{"account_number":"6","address":"cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn","pub_key":null,"sequence":"0"},"name":"gov","permissions":["burner"]},{"@type":"/cosmos.auth.v1beta1.ModuleAccount","base_account":{"account_number":"3","address":"cosmos1jv65s3grqf6v6jl3dp4t6c9t9rk99cd88lyufl","pub_key":null,"sequence":"0"},"name":"distribution","permissions":[]},{"@type":"/cosmos.auth.v1beta1.BaseAccount","account_number":"0","address":"cosmos15jenkldw6348lpgdev3vjzw90zzknxa9a3vg0j","pub_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A3uyZdXedyvYx9VCL6xRjkxtcFpgxjhXFIz9b2mWz+aV"},"sequence":"4"},{"@type":"/cosmos.auth.v1beta1.ModuleAccount","base_account":{"account_number":"7","address":"cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q","pub_key":null,"sequence":"0"},"name":"mint","permissions":["minter"]},{"@type":"/cosmos.auth.v1beta1.ModuleAccount","base_account":{"account_number":"2","address":"cosmos17xpfvakm2amg962yls6f84z3kell8c5lserqta","pub_key":null,"sequence":"0"},"name":"fee_collector","permissions":[]}],"params":{"max_memo_characters":"256","sig_verify_cost_ed25519":"590","sig_verify_cost_secp256k1":"1000","tx_sig_limit":"7","tx_size_cost_per_byte":"10"}},"authz":{"authorization":[]},"bank":{"balances":[{"address":"cosmos1qmkksxlxqdslq6kkca25m4jn344nx29lytq8f9","coins":[{"amount":"5000000000","denom":"stake"}]},{"address":"cosmos1pnt5523etwtzv6mj7haryfw6w8h5tkcuhd99m8","coins":[{"amount":"1000","denom":"stake"}]},{"address":"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh","coins":[{"amount":"1000000","denom":"stake"}]},{"address":"cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn","coins":[{"amount":"10010000","denom":"stake"}]},{"address":"cosmos1jv65s3grqf6v6jl3dp4t6c9t9rk99cd88lyufl","coins":[{"amount":"9635","denom":"stake"}]},{"address":"cosmos15jenkldw6348lpgdev3vjzw90zzknxa9a3vg0j","coins":[{"amount":"4988989000","denom":"stake"}]}],"denom_metadata":[],"params":{"default_send_enabled":true,"send_enabled":[]},"send_enabled":[],"supply":[{"amount":"10000009635","denom":"stake"}]},"capability":{"index":"1","owners":[]},"consensus":null,"crisis":{"constant_fee":{"amount":"1000","denom":"stake"}},"distribution":{"delegator_starting_infos":[{"delegator_address":"cosmos15jenkldw6348lpgdev3vjzw90zzknxa9a3vg0j","starting_info":{"height":"0","previous_period":"1","stake":"1000000.000000000000000000"},"validator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp"}],"delegator_withdraw_infos":[],"fee_pool":{"community_pool":[{"amount":"192.700000000000000000","denom":"stake"}]},"outstanding_rewards":[{"outstanding_rewards":[{"amount":"9442.300000000000000000","denom":"stake"}],"validator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp"}],"params":{"base_proposer_reward":"0.000000000000000000","bonus_proposer_reward":"0.000000000000000000","community_tax":"0.020000000000000000","withdraw_addr_enabled":true},"previous_proposer":"cosmosvalcons16vm0nx49eam4q0xasdnwdzsdl6ymgyjt757sgr","validator_accumulated_commissions":[{"accumulated":{"commission":[{"amount":"944.230000000000000000","denom":"stake"}]},"validator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp"}],"validator_current_rewards":[{"rewards":{"period":"2","rewards":[{"amount":"8498.070000000000000000","denom":"stake"}]},"validator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp"}],"validator_historical_rewards":[{"period":"1","rewards":{"cumulative_reward_ratio":[],"reference_count":2},"validator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp"}],"validator_slash_events":[]},"evidence":{"evidence":[]},"feegrant":{"allowances":[]},"genutil":{"gen_txs":[]},"gov":{"deposit_params":null,"deposits":[{"amount":[{"amount":"10010000","denom":"stake"}],"depositor":"cosmos15jenkldw6348lpgdev3vjzw90zzknxa9a3vg0j","proposal_id":"1"}],"params":{"expedited_min_deposit":[{"amount":"50000000","denom":"stake"}],"expedited_threshold":"0.667000000000000000","expedited_voting_period":"86400s","max_deposit_period":"172800s","min_deposit":[{"amount":"10000000","denom":"stake"}],"min_initial_deposit_ratio":"0.000000000000000000","proposal_cancel_dest":"","proposal_cancel_ratio":"0.500000000000000000","quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000","voting_period":"172800s"},"proposals":[{"deposit_end_time":"2023-02-22T11:11:52.776167376Z","expedited":false,"final_tally_result":{"abstain_count":"0","no_count":"0","no_with_veto_count":"0","yes_count":"0"},"id":"1","messages":[{"@type":"/cosmos.distribution.v1beta1.MsgCommunityPoolSpend","amount":[],"authority":"cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn","recipient":"cosmos1pnt5523etwtzv6mj7haryfw6w8h5tkcuhd99m8"}],"metadata":"ipfs://CID","proposer":"cosmos15jenkldw6348lpgdev3vjzw90zzknxa9a3vg0j","status":"PROPOSAL_STATUS_VOTING_PERIOD","submit_time":"2023-02-20T11:11:52.776167376Z","summary":"test proposal","title":"test proposal","total_deposit":[{"amount":"10010000","denom":"stake"}],"voting_end_time":"2023-02-22T11:12:07.801161984Z","voting_start_time":"2023-02-20T11:12:07.801161984Z"}],"starting_proposal_id":"2","tally_params":null,"votes":[],"voting_params":null},"group":{"group_members":[],"group_policies":[],"group_policy_seq":"0","group_seq":"0","groups":[],"proposal_seq":"0","proposals":[],"votes":[]},"mint":{"minter":{"annual_provisions":"1300010905.175073197786747950","inflation":"0.130000967926594565"},"params":{"blocks_per_year":"6311520","goal_bonded":"0.670000000000000000","inflation_max":"0.200000000000000000","inflation_min":"0.070000000000000000","inflation_rate_change":"0.130000000000000000","mint_denom":"stake"}},"nft":{"classes":[],"entries":[]},"params":null,"slashing":{"missed_blocks":[{"address":"cosmosvalcons16vm0nx49eam4q0xasdnwdzsdl6ymgyjt757sgr","missed_blocks":[]}],"params":{"downtime_jail_duration":"600s","min_signed_per_window":"0.500000000000000000","signed_blocks_window":"100","slash_fraction_double_sign":"0.050000000000000000","slash_fraction_downtime":"0.010000000000000000"},"signing_infos":[{"address":"cosmosvalcons16vm0nx49eam4q0xasdnwdzsdl6ymgyjt757sgr","validator_signing_info":{"address":"cosmosvalcons16vm0nx49eam4q0xasdnwdzsdl6ymgyjt757sgr","index_offset":"46","jailed_until":"1970-01-01T00:00:00Z","missed_blocks_counter":"0","start_height":"0","tombstoned":false}}]},"staking":{"delegations":[{"delegator_address":"cosmos15jenkldw6348lpgdev3vjzw90zzknxa9a3vg0j","shares":"1000000.000000000000000000","validator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp"}],"exported":true,"last_total_power":"1","last_validator_powers":[{"address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp","power":"1"}],"params":{"bond_denom":"stake","historical_entries":10000,"max_entries":7,"max_validators":100,"min_commission_rate":"0.000000000000000000","unbonding_time":"1814400s"},"redelegations":[],"unbonding_delegations":[],"validators":[{"commission":{"commission_rates":{"max_change_rate":"0.010000000000000000","max_rate":"0.200000000000000000","rate":"0.100000000000000000"},"update_time":"2023-02-20T11:08:30.588307671Z"},"consensus_pubkey":{"@type":"/cosmos.crypto.ed25519.PubKey","key":"tMZonPQYoooG/xbFVhHg95pTLxx7aO43/qgHFxDagWM="},"delegator_shares":"1000000.000000000000000000","description":{"details":"","identity":"","moniker":"test","security_contact":"","website":""},"jailed":false,"min_self_delegation":"1","operator_address":"cosmosvaloper15jenkldw6348lpgdev3vjzw90zzknxa9c9carp","status":"BOND_STATUS_BONDED","tokens":"1000000","unbonding_height":"0","unbonding_ids":[],"unbonding_on_hold_ref_count":"0","unbonding_time":"1970-01-01T00:00:00Z"}]},"upgrade":{},"vesting":{}},"chain_id":"demo","consensus_params":{"block":{"max_bytes":"22020096","max_gas":"-1"},"evidence":{"max_age_duration":"172800000000000","max_age_num_blocks":"100000","max_bytes":"1048576"},"validator":{"pub_key_types":["ed25519"]},"version":{"app":"0"}},"genesis_time":"2023-02-20T11:08:30.588307671Z","initial_height":"48","validators":[{"address":"D336F99AA5CF77503CDD8366E68A0DFE89B4124B","name":"test","power":"1","pub_key":{"type":"tendermint/PubKeyEd25519","value":"tMZonPQYoooG/xbFVhHg95pTLxx7aO43/qgHFxDagWM="}}]} \ No newline at end of file diff --git a/x/genutil/utils.go b/x/genutil/utils.go index 6c7617bda4..880aa7bdc1 100644 --- a/x/genutil/utils.go +++ b/x/genutil/utils.go @@ -16,36 +16,31 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/x/genutil/types" ) // ExportGenesisFile creates and writes the genesis configuration to disk. An // error is returned if building or writing the configuration to file fails. -func ExportGenesisFile(genDoc *cmttypes.GenesisDoc, genFile string) error { - if err := genDoc.ValidateAndComplete(); err != nil { +func ExportGenesisFile(genesis *types.AppGenesis, genFile string) error { + if err := genesis.ValidateAndComplete(); err != nil { return err } - return genDoc.SaveAs(genFile) + return genesis.SaveAs(genFile) } // ExportGenesisFileWithTime creates and writes the genesis configuration to disk. // An error is returned if building or writing the configuration to file fails. -func ExportGenesisFileWithTime( - genFile, chainID string, validators []cmttypes.GenesisValidator, - appState json.RawMessage, genTime time.Time, -) error { - genDoc := cmttypes.GenesisDoc{ - GenesisTime: genTime, - ChainID: chainID, - Validators: validators, - AppState: appState, - } +func ExportGenesisFileWithTime(genFile, chainID string, validators []cmttypes.GenesisValidator, appState json.RawMessage, genTime time.Time) error { + appGenesis := types.NewAppGenesisWithVersion(chainID, appState) + appGenesis.GenesisTime = genTime + appGenesis.Consensus.Validators = validators - if err := genDoc.ValidateAndComplete(); err != nil { + if err := appGenesis.ValidateAndComplete(); err != nil { return err } - return genDoc.SaveAs(genFile) + return appGenesis.SaveAs(genFile) } // InitializeNodeValidatorFiles creates private validator and p2p configuration files.