feat: add application genesis (#15031)

This commit is contained in:
Julien Robert 2023-02-22 11:29:03 +01:00 committed by GitHub
parent 57653f8e60
commit 832517befc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 593 additions and 633 deletions

View File

@ -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.

View File

@ -15,4 +15,4 @@ message GenesisState {
(amino.field_name) = "gentxs",
(amino.dont_omitempty) = true
];
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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{

View File

@ -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

View File

@ -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
}

View File

@ -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))
}

View File

@ -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())
}
})
}
}

View File

@ -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())
}
})
}
}

View File

@ -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)
}
})
}
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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}
}

View File

@ -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
}

View File

@ -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)
}

60
x/genutil/README.md Normal file
View File

@ -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
```

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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
}

View File

@ -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())
}
})

View File

@ -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
}

View File

@ -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)
}
})
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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

211
x/genutil/types/genesis.go Normal file
View File

@ -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
}

View File

@ -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

View File

@ -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")
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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.