diff --git a/CHANGELOG.md b/CHANGELOG.md index 43413678..d430930a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ **Features:** +- Make it easy to initialize contracts in genesis file with new CLI commands[\#326](https://github.com/CosmWasm/wasmd/issues/326) - Upgrade to WasmVM v0.13.0 [\#358](https://github.com/CosmWasm/wasmd/pull/358) - Upgrade to cosmos-sdk v0.40.0-rc6 [\#354](https://github.com/CosmWasm/wasmd/pull/354) - Upgrade to cosmos-sdk v0.40.0-rc5 [\#344](https://github.com/CosmWasm/wasmd/issues/344) diff --git a/app/app.go b/app/app.go index 643138fb..73fd5fc5 100644 --- a/app/app.go +++ b/app/app.go @@ -411,7 +411,7 @@ func NewWasmApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b distr.NewAppModule(appCodec, app.distrKeeper, app.accountKeeper, app.bankKeeper, app.stakingKeeper), staking.NewAppModule(appCodec, app.stakingKeeper, app.accountKeeper, app.bankKeeper), upgrade.NewAppModule(app.upgradeKeeper), - wasm.NewAppModule(&app.wasmKeeper), + wasm.NewAppModule(&app.wasmKeeper, app.stakingKeeper), evidence.NewAppModule(app.evidenceKeeper), ibc.NewAppModule(app.ibcKeeper), params.NewAppModule(app.paramsKeeper), @@ -433,6 +433,8 @@ func NewWasmApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b // NOTE: Capability module must occur first so that it can initialize any capabilities // so that other modules that want to create or claim capabilities afterwards in InitChain // can do so safely. + // wasm module should be a the end as it can call other modules functionality direct or via message dispatching during + // genesis phase. For example bank transfer, auth account check, staking, ... app.mm.SetOrderInitGenesis( capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName, @@ -461,7 +463,7 @@ func NewWasmApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b distr.NewAppModule(appCodec, app.distrKeeper, app.accountKeeper, app.bankKeeper, app.stakingKeeper), slashing.NewAppModule(appCodec, app.slashingKeeper, app.accountKeeper, app.bankKeeper, app.stakingKeeper), params.NewAppModule(app.paramsKeeper), - wasm.NewAppModule(&app.wasmKeeper), + wasm.NewAppModule(&app.wasmKeeper, app.stakingKeeper), evidence.NewAppModule(app.evidenceKeeper), ibc.NewAppModule(app.ibcKeeper), transferModule, diff --git a/cmd/wasmd/genwasm.go b/cmd/wasmd/genwasm.go new file mode 100644 index 00000000..87f05cdf --- /dev/null +++ b/cmd/wasmd/genwasm.go @@ -0,0 +1,26 @@ +package main + +import ( + wasmcli "github.com/CosmWasm/wasmd/x/wasm/client/cli" + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" +) + +func AddGenesisWasmMsgCmd(defaultNodeHome string) *cobra.Command { + txCmd := &cobra.Command{ + Use: "add-wasm-genesis-message", + Short: "Wasm genesis subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + txCmd.AddCommand( + wasmcli.GenesisStoreCodeCmd(defaultNodeHome), + wasmcli.GenesisInstantiateContractCmd(defaultNodeHome), + wasmcli.GenesisExecuteContractCmd(defaultNodeHome), + wasmcli.GenesisListContractsCmd(defaultNodeHome), + wasmcli.GenesisListCodesCmd(defaultNodeHome), + ) + return txCmd + +} diff --git a/cmd/wasmd/root.go b/cmd/wasmd/root.go index 82f4fdd4..fa0c932e 100644 --- a/cmd/wasmd/root.go +++ b/cmd/wasmd/root.go @@ -84,6 +84,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig app.EncodingConfig) { genutilcli.GenTxCmd(app.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome), genutilcli.ValidateGenesisCmd(app.ModuleBasics), AddGenesisAccountCmd(app.DefaultNodeHome), + AddGenesisWasmMsgCmd(app.DefaultNodeHome), tmcli.NewCompletionCmd(rootCmd, true), // testnetCmd(app.ModuleBasics, banktypes.GenesisBalancesIterator{}), debug.Cmd(), diff --git a/contrib/local/00-genesis.sh b/contrib/local/00-genesis.sh new file mode 100755 index 00000000..3c23b8d0 --- /dev/null +++ b/contrib/local/00-genesis.sh @@ -0,0 +1,28 @@ +#!/bin/bash +set -o errexit -o nounset -o pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +BASE_ACCOUNT=$(wasmd keys show validator -a) + +echo "-----------------------" +echo "## Genesis CosmWasm contract" +wasmd add-wasm-genesis-message store "$DIR/../../x/wasm/internal/keeper/testdata/hackatom.wasm" --instantiate-everybody true --builder=foo/bar:latest --run-as validator + +echo "-----------------------" +echo "## Genesis CosmWasm instance" +INIT="{\"verifier\":\"$(wasmd keys show validator -a)\", \"beneficiary\":\"$(wasmd keys show fred -a)\"}" +wasmd add-wasm-genesis-message instantiate-contract 1 "$INIT" --run-as validator --label=foobar --amount=100ustake --admin $BASE_ACCOUNT + +echo "-----------------------" +echo "## Genesis CosmWasm execute" +FIRST_CONTRACT_ADDR=wasm18vd8fpwxzck93qlwghaj6arh4p7c5n89k7fvsl +MSG='{"release":{}}' +wasmd add-wasm-genesis-message execute $FIRST_CONTRACT_ADDR "$MSG" --run-as validator --amount=1ustake + +echo "-----------------------" +echo "## List Genesis CosmWasm codes" +wasmd add-wasm-genesis-message list-codes + +echo "-----------------------" +echo "## List Genesis CosmWasm contracts" +wasmd add-wasm-genesis-message list-contracts \ No newline at end of file diff --git a/doc/proto.md b/doc/proto.md index d3e3877d..6d83a116 100644 --- a/doc/proto.md +++ b/doc/proto.md @@ -7,6 +7,7 @@ - [Code](#cosmwasm.wasm.v1beta1.Code) - [Contract](#cosmwasm.wasm.v1beta1.Contract) - [GenesisState](#cosmwasm.wasm.v1beta1.GenesisState) + - [GenesisState.GenMsgs](#cosmwasm.wasm.v1beta1.GenesisState.GenMsgs) - [Sequence](#cosmwasm.wasm.v1beta1.Sequence) - [x/wasm/internal/types/msg.proto](#x/wasm/internal/types/msg.proto) @@ -116,6 +117,25 @@ GenesisState - genesis state of x/wasm | codes | [Code](#cosmwasm.wasm.v1beta1.Code) | repeated | | | contracts | [Contract](#cosmwasm.wasm.v1beta1.Contract) | repeated | | | sequences | [Sequence](#cosmwasm.wasm.v1beta1.Sequence) | repeated | | +| gen_msgs | [GenesisState.GenMsgs](#cosmwasm.wasm.v1beta1.GenesisState.GenMsgs) | repeated | | + + + + + + + + +### GenesisState.GenMsgs +GenMsgs define the messages that can be executed during genesis phase. +The intention is to have more human readable data that is auditable. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| store_code | [MsgStoreCode](#cosmwasm.wasm.v1beta1.MsgStoreCode) | | | +| instantiate_contract | [MsgInstantiateContract](#cosmwasm.wasm.v1beta1.MsgInstantiateContract) | | | +| execute_contract | [MsgExecuteContract](#cosmwasm.wasm.v1beta1.MsgExecuteContract) | | | diff --git a/x/wasm/client/cli/genesis_msg.go b/x/wasm/client/cli/genesis_msg.go new file mode 100644 index 00000000..7431b9a4 --- /dev/null +++ b/x/wasm/client/cli/genesis_msg.go @@ -0,0 +1,489 @@ +package cli + +import ( + "bufio" + "bytes" + "crypto/sha256" + "encoding/binary" + "encoding/json" + "errors" + "fmt" + + "github.com/CosmWasm/wasmd/x/wasm/internal/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/spf13/cobra" + "github.com/tendermint/tendermint/crypto" + tmtypes "github.com/tendermint/tendermint/types" +) + +// GenesisStoreCodeCmd cli command to add a `MsgStoreCode` to the wasm section of the genesis +// that is executed on block 0. +func GenesisStoreCodeCmd(defaultNodeHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "store [wasm file] --source [source] --builder [builder] --run-as [owner_address_or_key_name]\",", + Short: "Upload a wasm binary", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + senderAddr, err := getActorAddress(cmd) + if err != nil { + return err + } + + msg, err := parseStoreCodeArgs(args[0], senderAddr, cmd.Flags()) + if err != nil { + return err + } + if err = msg.ValidateBasic(); err != nil { + return err + } + + return alterModuleState(cmd, func(s *types.GenesisState, _ map[string]json.RawMessage) error { + s.GenMsgs = append(s.GenMsgs, types.GenesisState_GenMsgs{ + Sum: &types.GenesisState_GenMsgs_StoreCode{StoreCode: &msg}, + }) + return nil + }) + }, + } + cmd.Flags().String(flagSource, "", "A valid URI reference to the contract's source code, optional") + cmd.Flags().String(flagBuilder, "", "A valid docker tag for the build system, optional") + cmd.Flags().String(flagRunAs, "", "The address that is stored as code creator") + cmd.Flags().String(flagInstantiateByEverybody, "", "Everybody can instantiate a contract from the code, optional") + cmd.Flags().String(flagInstantiateByAddress, "", "Only this address can instantiate a contract instance from the code, optional") + + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// GenesisInstantiateContractCmd cli command to add a `MsgInstantiateContract` to the wasm section of the genesis +// that is executed on block 0. +func GenesisInstantiateContractCmd(defaultNodeHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "instantiate-contract [code_id_int64] [json_encoded_init_args] --label [text] --run-as [address] --admin [address,optional] --amount [coins,optional]", + Short: "Instantiate a wasm contract", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + senderAddr, err := getActorAddress(cmd) + if err != nil { + return err + } + + msg, err := parseInstantiateArgs(args[0], args[1], senderAddr, cmd.Flags()) + if err != nil { + return err + } + if err = msg.ValidateBasic(); err != nil { + return err + } + + return alterModuleState(cmd, func(s *types.GenesisState, a map[string]json.RawMessage) error { + // simple sanity check that sender has some balance although it may be consumed by a previous message already + switch ok, err := hasAccountBalance(cmd, a, senderAddr, msg.InitFunds); { + case err != nil: + return err + case !ok: + return errors.New("sender has not enough account balance") + } + + // does code id exists? + codeInfos, err := getAllCodes(s) + if err != nil { + return err + } + var codeInfo *codeMeta + for i := range codeInfos { + if codeInfos[i].CodeID == msg.CodeID { + codeInfo = &codeInfos[i] + break + } + } + if codeInfo == nil { + return fmt.Errorf("unknown code id: %d", msg.CodeID) + } + // permissions correct? + if !codeInfo.Info.InstantiateConfig.Allowed(senderAddr) { + return fmt.Errorf("permissions were not granted for %s", senderAddr) + } + s.GenMsgs = append(s.GenMsgs, types.GenesisState_GenMsgs{ + Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &msg}, + }) + return nil + }) + }, + } + cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation") + cmd.Flags().String(flagLabel, "", "A human-readable name for this contract in lists") + cmd.Flags().String(flagAdmin, "", "Address of an admin") + cmd.Flags().String(flagRunAs, "", "The address that pays the init funds. It is the creator of the contract.") + + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// GenesisInstantiateContractCmd cli command to add a `MsgExecuteContract` to the wasm section of the genesis +// that is executed on block 0. +func GenesisExecuteContractCmd(defaultNodeHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "execute [contract_addr_bech32] [json_encoded_send_args] --run-as [address] --amount [coins,optional]", + Short: "Execute a command on a wasm contract", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + senderAddr, err := getActorAddress(cmd) + if err != nil { + return err + } + + msg, err := parseExecuteArgs(args[0], args[1], senderAddr, cmd.Flags()) + if err != nil { + return err + } + if err = msg.ValidateBasic(); err != nil { + return err + } + + return alterModuleState(cmd, func(s *types.GenesisState, a map[string]json.RawMessage) error { + // simple sanity check that sender has some balance although it may be consumed by a previous message already + switch ok, err := hasAccountBalance(cmd, a, senderAddr, msg.SentFunds); { + case err != nil: + return err + case !ok: + return errors.New("sender has not enough account balance") + } + + // - does contract address exists? + if !hasContract(s, msg.Contract) { + return fmt.Errorf("unknown contract: %s", msg.Contract) + } + s.GenMsgs = append(s.GenMsgs, types.GenesisState_GenMsgs{ + Sum: &types.GenesisState_GenMsgs_ExecuteContract{ExecuteContract: &msg}, + }) + return nil + }) + }, + } + cmd.Flags().String(flagAmount, "", "Coins to send to the contract along with command") + cmd.Flags().String(flagRunAs, "", "The address that pays the funds.") + + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// GenesisListCodesCmd cli command to list all codes stored in the genesis wasm.code section +// as well as from messages that are queued in the wasm.genMsgs section. +func GenesisListCodesCmd(defaultNodeHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "list-codes ", + Short: "Lists all codes from genesis code dump and queued messages", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + g, err := readGenesis(cmd) + if err != nil { + return err + } + all, err := getAllCodes(g.wasmModuleState) + if err != nil { + return err + } + return printJsonOutput(cmd, all) + + }, + } + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// GenesisListContractsCmd cli command to list all contracts stored in the genesis wasm.contract section +// as well as from messages that are queued in the wasm.genMsgs section. +func GenesisListContractsCmd(defaultNodeHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "list-contracts ", + Short: "Lists all contracts from genesis contract dump and queued messages", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + g, err := readGenesis(cmd) + if err != nil { + return err + } + state := g.wasmModuleState + all := getAllContracts(state) + return printJsonOutput(cmd, all) + }, + } + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// clientCtx marshaller works only with proto or bytes so we marshal the output ourself +func printJsonOutput(cmd *cobra.Command, obj interface{}) error { + clientCtx := client.GetClientContextFromCmd(cmd) + bz, err := json.MarshalIndent(obj, "", " ") + if err != nil { + return err + } + return clientCtx.PrintString(string(bz)) +} + +type codeMeta struct { + CodeID uint64 `json:"code_id"` + Info types.CodeInfo `json:"info"` +} + +func getAllCodes(state *types.GenesisState) ([]codeMeta, error) { + var all []codeMeta + for _, c := range state.Codes { + all = append(all, codeMeta{ + CodeID: c.CodeID, + Info: c.CodeInfo, + }) + } + // add inflight + seq := codeSeqValue(state) + for _, m := range state.GenMsgs { + if msg := m.GetStoreCode(); msg != nil { + var accessConfig types.AccessConfig + if msg.InstantiatePermission != nil { + accessConfig = *msg.InstantiatePermission + } else { + // default + creator, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return nil, fmt.Errorf("sender: %s", err) + } + accessConfig = state.Params.InstantiateDefaultPermission.With(creator) + } + hash := sha256.Sum256(msg.WASMByteCode) + all = append(all, codeMeta{ + CodeID: seq, + Info: types.CodeInfo{ + CodeHash: hash[:], + Creator: msg.Sender, + Source: msg.Source, + Builder: msg.Builder, + InstantiateConfig: accessConfig, + }, + }) + seq++ + } + } + return all, nil +} + +type contractMeta struct { + ContractAddress string `json:"contract_address"` + Info types.ContractInfo `json:"info"` +} + +func getAllContracts(state *types.GenesisState) []contractMeta { + var all []contractMeta + for _, c := range state.Contracts { + all = append(all, contractMeta{ + ContractAddress: c.ContractAddress, + Info: c.ContractInfo, + }) + } + // add inflight + seq := contractSeqValue(state) + for _, m := range state.GenMsgs { + if msg := m.GetInstantiateContract(); msg != nil { + all = append(all, contractMeta{ + ContractAddress: contractAddress(msg.CodeID, seq).String(), + Info: types.ContractInfo{ + CodeID: msg.CodeID, + Creator: msg.Sender, + Admin: msg.Admin, + Label: msg.Label, + }, + }) + seq++ + } + } + return all +} + +func hasAccountBalance(cmd *cobra.Command, a map[string]json.RawMessage, sender sdk.AccAddress, coins sdk.Coins) (bool, error) { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return false, err + } + cdc := clientCtx.JSONMarshaler + var genBalIterator banktypes.GenesisBalancesIterator + err = genutil.ValidateAccountInGenesis(a, genBalIterator, sender, coins, cdc) + if err != nil { + return false, err + } + return true, nil +} + +func hasContract(state *types.GenesisState, contractAddr string) bool { + for _, c := range state.Contracts { + if c.ContractAddress == contractAddr { + return true + } + } + seq := contractSeqValue(state) + for _, m := range state.GenMsgs { + if msg := m.GetInstantiateContract(); msg != nil { + if contractAddress(msg.CodeID, seq).String() == contractAddr { + return true + } + seq++ + } + } + return false +} + +// genesisData contains raw and unmarshalled data from the genesis file +type genesisData struct { + genesisFile string + genDoc *tmtypes.GenesisDoc + appState map[string]json.RawMessage + wasmModuleState *types.GenesisState +} + +func readGenesis(cmd *cobra.Command) (*genesisData, error) { + clientCtx := client.GetClientContextFromCmd(cmd) + serverCtx := server.GetServerContextFromCmd(cmd) + config := serverCtx.Config + config.SetRoot(clientCtx.HomeDir) + + genFile := config.GenesisFile() + appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal genesis state: %w", err) + } + var wasmGenesisState types.GenesisState + if appState[types.ModuleName] != nil { + clientCtx := client.GetClientContextFromCmd(cmd) + clientCtx.JSONMarshaler.MustUnmarshalJSON(appState[types.ModuleName], &wasmGenesisState) + } + + return &genesisData{ + genesisFile: genFile, + genDoc: genDoc, + appState: appState, + wasmModuleState: &wasmGenesisState, + }, nil +} + +// alterModuleState loads the genesis from the default or set home dir, +// unmarshalls the wasm module section into the object representation +// calls the callback function to modify it +// and marshals the modified state back into the genesis file +func alterModuleState(cmd *cobra.Command, callback func(s *types.GenesisState, a map[string]json.RawMessage) error) error { + g, err := readGenesis(cmd) + if err != nil { + return err + } + if err := callback(g.wasmModuleState, g.appState); err != nil { + return err + } + // and store update + if err := g.wasmModuleState.ValidateBasic(); err != nil { + return err + } + clientCtx := client.GetClientContextFromCmd(cmd) + wasmGenStateBz, err := clientCtx.JSONMarshaler.MarshalJSON(g.wasmModuleState) + if err != nil { + return sdkerrors.Wrap(err, "marshal wasm genesis state") + } + + g.appState[types.ModuleName] = wasmGenStateBz + appStateJSON, err := json.Marshal(g.appState) + if err != nil { + return sdkerrors.Wrap(err, "marshal application genesis state") + } + + g.genDoc.AppState = appStateJSON + return genutil.ExportGenesisFile(g.genDoc, g.genesisFile) +} + +// contractSeqValue reads the contract sequence from the genesis or +// returns default start value used in the keeper +func contractSeqValue(state *types.GenesisState) uint64 { + var seq uint64 = 1 + for _, s := range state.Sequences { + if bytes.Equal(s.IDKey, types.KeyLastInstanceID) { + seq = s.Value + break + } + } + return seq +} + +// codeSeqValue reads the code sequence from the genesis or +// returns default start value used in the keeper +func codeSeqValue(state *types.GenesisState) uint64 { + var seq uint64 = 1 + for _, s := range state.Sequences { + if bytes.Equal(s.IDKey, types.KeyLastCodeID) { + seq = s.Value + break + } + } + return seq +} + +// getActorAddress returns the account address for the `--run-as` flag. +// The flag value can either be an address already or a key name where the +// address is read from the keyring instead. +func getActorAddress(cmd *cobra.Command) (sdk.AccAddress, error) { + actorArg, err := cmd.Flags().GetString(flagRunAs) + if err != nil { + return nil, fmt.Errorf("run-as: %s", err.Error()) + } + if len(actorArg) == 0 { + return nil, errors.New("run-as address is required") + } + + actorAddr, err := sdk.AccAddressFromBech32(actorArg) + if err == nil { + return actorAddr, nil + } + inBuf := bufio.NewReader(cmd.InOrStdin()) + keyringBackend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) + + homeDir := client.GetClientContextFromCmd(cmd).HomeDir + // attempt to lookup address from Keybase if no address was provided + kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, homeDir, inBuf) + if err != nil { + return nil, err + } + + info, err := kb.Key(actorArg) + if err != nil { + return nil, fmt.Errorf("failed to get address from Keybase: %w", err) + } + return info.GetAddress(), nil +} + +// contractAddress builds a contract address. copied from keeper +func contractAddress(codeID, instanceID uint64) sdk.AccAddress { + // NOTE: It is possible to get a duplicate address if either codeID or instanceID + // overflow 32 bits. This is highly improbable, but something that could be refactored. + contractID := codeID<<32 + instanceID + return addrFromUint64(contractID) +} + +// addrFromUint64 is a helper for address generation, copied from keeper +func addrFromUint64(id uint64) sdk.AccAddress { + addr := make([]byte, 20) + addr[0] = 'C' + binary.PutUvarint(addr[1:], id) + return sdk.AccAddress(crypto.AddressHash(addr)) +} diff --git a/x/wasm/client/cli/genesis_msg_test.go b/x/wasm/client/cli/genesis_msg_test.go new file mode 100644 index 00000000..ab1d4fc9 --- /dev/null +++ b/x/wasm/client/cli/genesis_msg_test.go @@ -0,0 +1,571 @@ +package cli + +import ( + "context" + "encoding/json" + "io/ioutil" + "os" + "path" + "testing" + + "github.com/CosmWasm/wasmd/x/wasm/internal/keeper" + "github.com/CosmWasm/wasmd/x/wasm/internal/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" +) + +var wasmIdent = []byte("\x00\x61\x73\x6D") + +var myWellFundedAccount = keeper.RandomBech32AccountAddress(nil) + +const defaultTestKeyName = "my-key-name" + +func TestGenesisStoreCodeCmd(t *testing.T) { + minimalWasmGenesis := types.GenesisState{ + Params: types.DefaultParams(), + } + anyValidWasmFile, err := ioutil.TempFile(t.TempDir(), "wasm") + require.NoError(t, err) + anyValidWasmFile.Write(wasmIdent) + require.NoError(t, anyValidWasmFile.Close()) + + specs := map[string]struct { + srcGenesis types.GenesisState + mutator func(cmd *cobra.Command) + expError bool + }{ + "all good with actor address": { + srcGenesis: minimalWasmGenesis, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{anyValidWasmFile.Name()}) + flagSet := cmd.Flags() + flagSet.Set("source", "https://foo.bar") + flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t)) + }, + }, + "all good with key name": { + srcGenesis: minimalWasmGenesis, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{anyValidWasmFile.Name()}) + flagSet := cmd.Flags() + flagSet.Set("run-as", defaultTestKeyName) + }, + }, + "with unknown actor key name should fail": { + srcGenesis: minimalWasmGenesis, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{anyValidWasmFile.Name()}) + flagSet := cmd.Flags() + flagSet.Set("run-as", "unknown key") + }, + expError: true, + }, + "without actor should fail": { + srcGenesis: minimalWasmGenesis, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{anyValidWasmFile.Name()}) + flagSet := cmd.Flags() + flagSet.Set("source", "https://foo.bar") + }, + expError: true, + }, + "invalid msg data should fail": { + srcGenesis: minimalWasmGenesis, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{anyValidWasmFile.Name()}) + flagSet := cmd.Flags() + flagSet.Set("source", "not an url") + flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t)) + }, + expError: true, + }, + } + for msg, spec := range specs { + t.Run(msg, func(t *testing.T) { + homeDir := setupGenesis(t, spec.srcGenesis) + + // when + cmd := GenesisStoreCodeCmd(homeDir) + spec.mutator(cmd) + err := executeCmdWithContext(t, homeDir, cmd) + if spec.expError { + require.Error(t, err) + return + } + require.NoError(t, err) + // then + moduleState := loadModuleState(t, homeDir) + assert.Len(t, moduleState.GenMsgs, 1) + }) + } +} + +func TestInstantiateContractCmd(t *testing.T) { + minimalWasmGenesis := types.GenesisState{ + Params: types.DefaultParams(), + } + anyValidWasmFile, err := ioutil.TempFile(t.TempDir(), "wasm") + require.NoError(t, err) + anyValidWasmFile.Write(wasmIdent) + require.NoError(t, anyValidWasmFile.Close()) + + specs := map[string]struct { + srcGenesis types.GenesisState + mutator func(cmd *cobra.Command) + expMsgCount int + expError bool + }{ + "all good with code id in genesis codes": { + srcGenesis: types.GenesisState{ + Params: types.DefaultParams(), + Codes: []types.Code{ + { + CodeID: 1, + CodeInfo: types.CodeInfo{ + CodeHash: []byte("a-valid-code-hash"), + Creator: keeper.RandomBech32AccountAddress(t), + InstantiateConfig: types.AccessConfig{ + Permission: types.AccessTypeEverybody, + }, + }, + CodeBytes: wasmIdent, + }, + }, + }, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{"1", `{}`}) + flagSet := cmd.Flags() + flagSet.Set("label", "testing") + flagSet.Set("run-as", myWellFundedAccount) + }, + expMsgCount: 1, + }, + "all good with code id from genesis store messages without initial sequence": { + srcGenesis: types.GenesisState{ + Params: types.DefaultParams(), + GenMsgs: []types.GenesisState_GenMsgs{ + {Sum: &types.GenesisState_GenMsgs_StoreCode{StoreCode: types.MsgStoreCodeFixture()}}, + }, + }, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{"1", `{}`}) + flagSet := cmd.Flags() + flagSet.Set("label", "testing") + flagSet.Set("run-as", myWellFundedAccount) + }, + expMsgCount: 2, + }, + "all good with code id from genesis store messages and sequence set": { + srcGenesis: types.GenesisState{ + Params: types.DefaultParams(), + GenMsgs: []types.GenesisState_GenMsgs{ + {Sum: &types.GenesisState_GenMsgs_StoreCode{StoreCode: types.MsgStoreCodeFixture()}}, + }, + Sequences: []types.Sequence{ + {IDKey: types.KeyLastCodeID, Value: 100}, + }, + }, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{"100", `{}`}) + flagSet := cmd.Flags() + flagSet.Set("label", "testing") + flagSet.Set("run-as", myWellFundedAccount) + }, + expMsgCount: 2, + }, + "fails with codeID not existing in codes": { + srcGenesis: minimalWasmGenesis, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{"2", `{}`}) + flagSet := cmd.Flags() + flagSet.Set("label", "testing") + flagSet.Set("run-as", myWellFundedAccount) + }, + expError: true, + }, + "fails when instantiation permissions not granted": { + srcGenesis: types.GenesisState{ + Params: types.DefaultParams(), + GenMsgs: []types.GenesisState_GenMsgs{ + {Sum: &types.GenesisState_GenMsgs_StoreCode{StoreCode: types.MsgStoreCodeFixture(func(code *types.MsgStoreCode) { + code.InstantiatePermission = &types.AllowNobody + })}}, + }, + }, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{"1", `{}`}) + flagSet := cmd.Flags() + flagSet.Set("label", "testing") + flagSet.Set("run-as", myWellFundedAccount) + }, + expError: true, + }, + "fails without a sender balance": { + srcGenesis: types.GenesisState{ + Params: types.DefaultParams(), + Codes: []types.Code{ + { + CodeID: 1, + CodeInfo: types.CodeInfo{ + CodeHash: []byte("a-valid-code-hash"), + Creator: keeper.RandomBech32AccountAddress(t), + InstantiateConfig: types.AccessConfig{ + Permission: types.AccessTypeEverybody, + }, + }, + CodeBytes: wasmIdent, + }, + }, + }, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{"1", `{}`}) + flagSet := cmd.Flags() + flagSet.Set("label", "testing") + flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t)) + }, + expError: true, + }, + } + for msg, spec := range specs { + t.Run(msg, func(t *testing.T) { + homeDir := setupGenesis(t, spec.srcGenesis) + + // when + cmd := GenesisInstantiateContractCmd(homeDir) + spec.mutator(cmd) + err := executeCmdWithContext(t, homeDir, cmd) + if spec.expError { + require.Error(t, err) + return + } + require.NoError(t, err) + // then + moduleState := loadModuleState(t, homeDir) + assert.Len(t, moduleState.GenMsgs, spec.expMsgCount) + }) + } +} + +func TestExecuteContractCmd(t *testing.T) { + const firstContractAddress = "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5" + minimalWasmGenesis := types.GenesisState{ + Params: types.DefaultParams(), + } + anyValidWasmFile, err := ioutil.TempFile(t.TempDir(), "wasm") + require.NoError(t, err) + anyValidWasmFile.Write(wasmIdent) + require.NoError(t, anyValidWasmFile.Close()) + + specs := map[string]struct { + srcGenesis types.GenesisState + mutator func(cmd *cobra.Command) + expMsgCount int + expError bool + }{ + "all good with contract in genesis contracts": { + srcGenesis: types.GenesisState{ + Params: types.DefaultParams(), + Codes: []types.Code{ + { + CodeID: 1, + CodeInfo: types.CodeInfoFixture(), + CodeBytes: wasmIdent, + }, + }, + Contracts: []types.Contract{ + { + ContractAddress: firstContractAddress, + ContractInfo: types.ContractInfoFixture(func(info *types.ContractInfo) { + info.Created = nil + }), + ContractState: []types.Model{}, + }, + }, + }, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{firstContractAddress, `{}`}) + flagSet := cmd.Flags() + flagSet.Set("run-as", myWellFundedAccount) + }, + expMsgCount: 1, + }, + "all good with contract from genesis store messages without initial sequence": { + srcGenesis: types.GenesisState{ + Params: types.DefaultParams(), + Codes: []types.Code{ + { + CodeID: 1, + CodeInfo: types.CodeInfoFixture(), + CodeBytes: wasmIdent, + }, + }, + GenMsgs: []types.GenesisState_GenMsgs{ + {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: types.MsgInstantiateContractFixture()}}, + }, + }, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{firstContractAddress, `{}`}) + flagSet := cmd.Flags() + flagSet.Set("run-as", myWellFundedAccount) + }, + expMsgCount: 2, + }, + "all good with contract from genesis store messages and contract sequence set": { + srcGenesis: types.GenesisState{ + Params: types.DefaultParams(), + Codes: []types.Code{ + { + CodeID: 1, + CodeInfo: types.CodeInfoFixture(), + CodeBytes: wasmIdent, + }, + }, + GenMsgs: []types.GenesisState_GenMsgs{ + {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: types.MsgInstantiateContractFixture()}}, + }, + Sequences: []types.Sequence{ + {IDKey: types.KeyLastInstanceID, Value: 100}, + }, + }, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{"cosmos1weh0k0l6t6v4jkmkde8e90tzkw2c59g42ccl62", `{}`}) + flagSet := cmd.Flags() + flagSet.Set("run-as", myWellFundedAccount) + }, + expMsgCount: 2, + }, + "fails with unknown contract address": { + srcGenesis: minimalWasmGenesis, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{keeper.RandomBech32AccountAddress(t), `{}`}) + flagSet := cmd.Flags() + flagSet.Set("run-as", myWellFundedAccount) + }, + expError: true, + }, + "fails without enough sender balance": { + srcGenesis: types.GenesisState{ + Params: types.DefaultParams(), + Codes: []types.Code{ + { + CodeID: 1, + CodeInfo: types.CodeInfoFixture(), + CodeBytes: wasmIdent, + }, + }, + Contracts: []types.Contract{ + { + ContractAddress: firstContractAddress, + ContractInfo: types.ContractInfoFixture(func(info *types.ContractInfo) { + info.Created = nil + }), + ContractState: []types.Model{}, + }, + }, + }, + mutator: func(cmd *cobra.Command) { + cmd.SetArgs([]string{firstContractAddress, `{}`}) + flagSet := cmd.Flags() + flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t)) + }, + expError: true, + }, + } + for msg, spec := range specs { + t.Run(msg, func(t *testing.T) { + homeDir := setupGenesis(t, spec.srcGenesis) + cmd := GenesisExecuteContractCmd(homeDir) + spec.mutator(cmd) + + // when + err := executeCmdWithContext(t, homeDir, cmd) + if spec.expError { + require.Error(t, err) + return + } + require.NoError(t, err) + // then + moduleState := loadModuleState(t, homeDir) + assert.Len(t, moduleState.GenMsgs, spec.expMsgCount) + }) + } +} +func TestGetAllContracts(t *testing.T) { + specs := map[string]struct { + src types.GenesisState + exp []contractMeta + }{ + "read from contracts state": { + src: types.GenesisState{ + Contracts: []types.Contract{ + { + ContractAddress: "first-contract", + ContractInfo: types.ContractInfo{Label: "first"}, + }, + { + ContractAddress: "second-contract", + ContractInfo: types.ContractInfo{Label: "second"}, + }, + }, + }, + exp: []contractMeta{ + { + ContractAddress: "first-contract", + Info: types.ContractInfo{Label: "first"}, + }, + { + ContractAddress: "second-contract", + Info: types.ContractInfo{Label: "second"}, + }, + }, + }, + "read from message state": { + src: types.GenesisState{ + GenMsgs: []types.GenesisState_GenMsgs{ + {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &types.MsgInstantiateContract{Label: "first"}}}, + {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &types.MsgInstantiateContract{Label: "second"}}}, + }, + }, + exp: []contractMeta{ + { + ContractAddress: contractAddress(0, 1).String(), + Info: types.ContractInfo{Label: "first"}, + }, + { + ContractAddress: contractAddress(0, 2).String(), + Info: types.ContractInfo{Label: "second"}, + }, + }, + }, + "read from message state with contract sequence": { + src: types.GenesisState{ + Sequences: []types.Sequence{ + {IDKey: types.KeyLastInstanceID, Value: 100}, + }, + GenMsgs: []types.GenesisState_GenMsgs{ + {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &types.MsgInstantiateContract{Label: "hundred"}}}, + }, + }, + exp: []contractMeta{ + { + ContractAddress: contractAddress(0, 100).String(), + Info: types.ContractInfo{Label: "hundred"}, + }, + }, + }, + "read from contract and message state with contract sequence": { + src: types.GenesisState{ + Contracts: []types.Contract{ + { + ContractAddress: "first-contract", + ContractInfo: types.ContractInfo{Label: "first"}, + }, + }, + Sequences: []types.Sequence{ + {IDKey: types.KeyLastInstanceID, Value: 100}, + }, + GenMsgs: []types.GenesisState_GenMsgs{ + {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &types.MsgInstantiateContract{Label: "hundred"}}}, + }, + }, + exp: []contractMeta{ + { + ContractAddress: "first-contract", + Info: types.ContractInfo{Label: "first"}, + }, + { + ContractAddress: contractAddress(0, 100).String(), + Info: types.ContractInfo{Label: "hundred"}, + }, + }, + }, + } + for msg, spec := range specs { + t.Run(msg, func(t *testing.T) { + got := getAllContracts(&spec.src) + assert.Equal(t, spec.exp, got) + }) + } + +} + +func setupGenesis(t *testing.T, wasmGenesis types.GenesisState) string { + appCodec := keeper.MakeEncodingConfig(t).Marshaler + homeDir := t.TempDir() + + require.NoError(t, os.Mkdir(path.Join(homeDir, "config"), 0700)) + genFilename := path.Join(homeDir, "config", "genesis.json") + appState := make(map[string]json.RawMessage) + appState[types.ModuleName] = appCodec.MustMarshalJSON(&wasmGenesis) + + bankGenesis := banktypes.DefaultGenesisState() + bankGenesis.Balances = append(bankGenesis.Balances, banktypes.Balance{ + // add a balance for the default sender account + Address: myWellFundedAccount, + Coins: sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10000000000))), + }) + appState[banktypes.ModuleName] = appCodec.MustMarshalJSON(bankGenesis) + appState[stakingtypes.ModuleName] = appCodec.MustMarshalJSON(stakingtypes.DefaultGenesisState()) + + appStateBz, err := json.Marshal(appState) + require.NoError(t, err) + genDoc := tmtypes.GenesisDoc{ + ChainID: "testing", + AppState: appStateBz, + } + err = genutil.ExportGenesisFile(&genDoc, genFilename) + require.NoError(t, err) + + return homeDir +} + +func executeCmdWithContext(t *testing.T, homeDir string, cmd *cobra.Command) error { + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(homeDir) + require.NoError(t, err) + appCodec := keeper.MakeEncodingConfig(t).Marshaler + serverCtx := server.NewContext(viper.New(), cfg, logger) + clientCtx := client.Context{}.WithJSONMarshaler(appCodec).WithHomeDir(homeDir) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) + flagSet := cmd.Flags() + flagSet.Set("home", homeDir) + flagSet.Set(flags.FlagKeyringBackend, keyring.BackendTest) + + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, homeDir, mockIn) + require.NoError(t, err) + _, err = kb.NewAccount(defaultTestKeyName, testutil.TestMnemonic, "", sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + return cmd.ExecuteContext(ctx) +} + +func loadModuleState(t *testing.T, homeDir string) types.GenesisState { + genFilename := path.Join(homeDir, "config", "genesis.json") + appState, _, err := genutiltypes.GenesisStateFromGenFile(genFilename) + require.NoError(t, err) + require.Contains(t, appState, types.ModuleName) + + appCodec := keeper.MakeEncodingConfig(t).Marshaler + var moduleState types.GenesisState + require.NoError(t, appCodec.UnmarshalJSON(appState[types.ModuleName], &moduleState)) + return moduleState +} diff --git a/x/wasm/client/cli/gov_tx.go b/x/wasm/client/cli/gov_tx.go index 7902007b..3a7214af 100644 --- a/x/wasm/client/cli/gov_tx.go +++ b/x/wasm/client/cli/gov_tx.go @@ -1,6 +1,8 @@ package cli import ( + "fmt" + "github.com/CosmWasm/wasmd/x/wasm/internal/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/tx" @@ -9,7 +11,6 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/viper" ) func ProposalStoreCodeCmd() *cobra.Command { @@ -23,27 +24,44 @@ func ProposalStoreCodeCmd() *cobra.Command { return err } - src, err := parseStoreCodeArgs(args, clientCtx) + src, err := parseStoreCodeArgs(args[0], clientCtx.FromAddress, cmd.Flags()) if err != nil { return err } - if len(viper.GetString(flagRunAs)) == 0 { + runAs, err := cmd.Flags().GetString(flagRunAs) + if err != nil { + return fmt.Errorf("run-as: %s", err) + } + if len(runAs) == 0 { return errors.New("run-as address is required") } + proposalTitle, err := cmd.Flags().GetString(cli.FlagTitle) + if err != nil { + return fmt.Errorf("proposal title: %s", err) + } + proposalDescr, err := cmd.Flags().GetString(cli.FlagDescription) + if err != nil { + return fmt.Errorf("proposal description: %s", err) + } + depositArg, err := cmd.Flags().GetString(cli.FlagDeposit) + if err != nil { + return err + } + deposit, err := sdk.ParseCoinsNormalized(depositArg) + if err != nil { + return err + } + content := types.StoreCodeProposal{ - Title: viper.GetString(cli.FlagTitle), - Description: viper.GetString(cli.FlagDescription), - RunAs: viper.GetString(flagRunAs), + Title: proposalTitle, + Description: proposalDescr, + RunAs: runAs, WASMByteCode: src.WASMByteCode, Source: src.Source, Builder: src.Builder, InstantiatePermission: src.InstantiatePermission, } - deposit, err := sdk.ParseCoinsNormalized(viper.GetString(cli.FlagDeposit)) - if err != nil { - return err - } msg, err := govtypes.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress()) if err != nil { return err @@ -83,17 +101,39 @@ func ProposalInstantiateContractCmd() *cobra.Command { return err } - src, err := parseInstantiateArgs(args, clientCtx) + src, err := parseInstantiateArgs(args[0], args[1], clientCtx.FromAddress, cmd.Flags()) if err != nil { return err } - if len(viper.GetString(flagRunAs)) == 0 { - return errors.New("creator address is required") + + runAs, err := cmd.Flags().GetString(flagRunAs) + if err != nil { + return fmt.Errorf("run-as: %s", err) } + if len(runAs) == 0 { + return errors.New("run-as address is required") + } + proposalTitle, err := cmd.Flags().GetString(cli.FlagTitle) + if err != nil { + return fmt.Errorf("proposal title: %s", err) + } + proposalDescr, err := cmd.Flags().GetString(cli.FlagDescription) + if err != nil { + return fmt.Errorf("proposal description: %s", err) + } + depositArg, err := cmd.Flags().GetString(cli.FlagDeposit) + if err != nil { + return err + } + deposit, err := sdk.ParseCoinsNormalized(depositArg) + if err != nil { + return err + } + content := types.InstantiateContractProposal{ - Title: viper.GetString(cli.FlagTitle), - Description: viper.GetString(cli.FlagDescription), - RunAs: viper.GetString(flagRunAs), + Title: proposalTitle, + Description: proposalDescr, + RunAs: runAs, Admin: src.Admin, CodeID: src.CodeID, Label: src.Label, @@ -101,11 +141,6 @@ func ProposalInstantiateContractCmd() *cobra.Command { InitFunds: src.InitFunds, } - deposit, err := sdk.ParseCoinsNormalized(viper.GetString(cli.FlagDeposit)) - if err != nil { - return err - } - msg, err := govtypes.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress()) if err != nil { return err @@ -148,22 +183,37 @@ func ProposalMigrateContractCmd() *cobra.Command { return err } - if len(viper.GetString(flagRunAs)) == 0 { + runAs, err := cmd.Flags().GetString(flagRunAs) + if err != nil { + return fmt.Errorf("run-as: %s", err) + } + if len(runAs) == 0 { return errors.New("run-as address is required") } + proposalTitle, err := cmd.Flags().GetString(cli.FlagTitle) + if err != nil { + return fmt.Errorf("proposal title: %s", err) + } + proposalDescr, err := cmd.Flags().GetString(cli.FlagDescription) + if err != nil { + return fmt.Errorf("proposal description: %s", err) + } + depositArg, err := cmd.Flags().GetString(cli.FlagDeposit) + if err != nil { + return err + } + deposit, err := sdk.ParseCoinsNormalized(depositArg) + if err != nil { + return err + } content := types.MigrateContractProposal{ - Title: viper.GetString(cli.FlagTitle), - Description: viper.GetString(cli.FlagDescription), + Title: proposalTitle, + Description: proposalDescr, Contract: src.Contract, CodeID: src.CodeID, MigrateMsg: src.MigrateMsg, - RunAs: viper.GetString(flagRunAs), - } - - deposit, err := sdk.ParseCoinsNormalized(viper.GetString(cli.FlagDeposit)) - if err != nil { - return err + RunAs: runAs, } msg, err := govtypes.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress()) @@ -205,18 +255,30 @@ func ProposalUpdateContractAdminCmd() *cobra.Command { return err } - content := types.UpdateAdminProposal{ - Title: viper.GetString(cli.FlagTitle), - Description: viper.GetString(cli.FlagDescription), - Contract: src.Contract, - NewAdmin: src.NewAdmin, + proposalTitle, err := cmd.Flags().GetString(cli.FlagTitle) + if err != nil { + return fmt.Errorf("proposal title: %s", err) } - - deposit, err := sdk.ParseCoinsNormalized(viper.GetString(cli.FlagDeposit)) + proposalDescr, err := cmd.Flags().GetString(cli.FlagDescription) + if err != nil { + return fmt.Errorf("proposal description: %s", err) + } + depositArg, err := cmd.Flags().GetString(cli.FlagDeposit) + if err != nil { + return fmt.Errorf("deposit: %s", err) + } + deposit, err := sdk.ParseCoinsNormalized(depositArg) if err != nil { return err } + content := types.UpdateAdminProposal{ + Title: proposalTitle, + Description: proposalDescr, + Contract: src.Contract, + NewAdmin: src.NewAdmin, + } + msg, err := govtypes.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress()) if err != nil { return err @@ -249,17 +311,29 @@ func ProposalClearContractAdminCmd() *cobra.Command { return err } - content := types.ClearAdminProposal{ - Title: viper.GetString(cli.FlagTitle), - Description: viper.GetString(cli.FlagDescription), - Contract: args[0], + proposalTitle, err := cmd.Flags().GetString(cli.FlagTitle) + if err != nil { + return fmt.Errorf("proposal title: %s", err) } - - deposit, err := sdk.ParseCoinsNormalized(viper.GetString(cli.FlagDeposit)) + proposalDescr, err := cmd.Flags().GetString(cli.FlagDescription) + if err != nil { + return fmt.Errorf("proposal description: %s", err) + } + depositArg, err := cmd.Flags().GetString(cli.FlagDeposit) + if err != nil { + return fmt.Errorf("deposit: %s", err) + } + deposit, err := sdk.ParseCoinsNormalized(depositArg) if err != nil { return err } + content := types.ClearAdminProposal{ + Title: proposalTitle, + Description: proposalDescr, + Contract: args[0], + } + msg, err := govtypes.NewMsgSubmitProposal(&content, deposit, clientCtx.GetFromAddress()) if err != nil { return err diff --git a/x/wasm/client/cli/query.go b/x/wasm/client/cli/query.go index 7035a6f9..4a1faed0 100644 --- a/x/wasm/client/cli/query.go +++ b/x/wasm/client/cli/query.go @@ -47,7 +47,6 @@ func GetCmdListCode() *cobra.Command { Long: "List all wasm bytecode on the chain", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err @@ -83,7 +82,6 @@ func GetCmdListContractByCode() *cobra.Command { Long: "List wasm all bytecode on the chain for given code id", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err @@ -125,7 +123,6 @@ func GetCmdQueryCode() *cobra.Command { Long: "Downloads wasm bytecode for given code id", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err @@ -218,7 +215,6 @@ func GetCmdGetContractStateAll() *cobra.Command { Long: "Prints out all internal state of a contract given its address", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err @@ -260,7 +256,6 @@ func GetCmdGetContractStateRaw() *cobra.Command { Long: "Prints out internal state for of a contract given its address", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err @@ -302,7 +297,6 @@ func GetCmdGetContractStateSmart() *cobra.Command { Long: "Calls contract with given address with query data and prints the returned result", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err diff --git a/x/wasm/client/cli/tx.go b/x/wasm/client/cli/tx.go index 659482b2..55a9b3fc 100644 --- a/x/wasm/client/cli/tx.go +++ b/x/wasm/client/cli/tx.go @@ -1,6 +1,7 @@ package cli import ( + "errors" "fmt" "io/ioutil" "strconv" @@ -13,7 +14,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/spf13/cobra" - "github.com/spf13/viper" + flag "github.com/spf13/pflag" ) const ( @@ -56,8 +57,7 @@ func StoreCodeCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) - - msg, err := parseStoreCodeArgs(args, clientCtx) + msg, err := parseStoreCodeArgs(args[0], clientCtx.GetFromAddress(), cmd.Flags()) if err != nil { return err } @@ -76,8 +76,8 @@ func StoreCodeCmd() *cobra.Command { return cmd } -func parseStoreCodeArgs(args []string, cliCtx client.Context) (types.MsgStoreCode, error) { - wasm, err := ioutil.ReadFile(args[0]) +func parseStoreCodeArgs(file string, sender sdk.AccAddress, flags *flag.FlagSet) (types.MsgStoreCode, error) { + wasm, err := ioutil.ReadFile(file) if err != nil { return types.MsgStoreCode{}, err } @@ -94,23 +94,48 @@ func parseStoreCodeArgs(args []string, cliCtx client.Context) (types.MsgStoreCod } var perm *types.AccessConfig - if onlyAddrStr := viper.GetString(flagInstantiateByAddress); onlyAddrStr != "" { + onlyAddrStr, err := flags.GetString(flagInstantiateByAddress) + if err != nil { + return types.MsgStoreCode{}, fmt.Errorf("instantiate by address: %s", err) + } + if onlyAddrStr != "" { allowedAddr, err := sdk.AccAddressFromBech32(onlyAddrStr) if err != nil { return types.MsgStoreCode{}, sdkerrors.Wrap(err, flagInstantiateByAddress) } x := types.AccessTypeOnlyAddress.With(allowedAddr) perm = &x - } else if everybody := viper.GetBool(flagInstantiateByEverybody); everybody { - perm = &types.AllowEverybody + } else { + everybodyStr, err := flags.GetString(flagInstantiateByEverybody) + if err != nil { + return types.MsgStoreCode{}, fmt.Errorf("instantiate by everybody: %s", err) + } + if everybodyStr != "" { + ok, err := strconv.ParseBool(everybodyStr) + if err != nil { + return types.MsgStoreCode{}, fmt.Errorf("boolean value expected for instantiate by everybody: %s", err) + } + if ok { + perm = &types.AllowEverybody + } + } } // build and sign the transaction, then broadcast to Tendermint + source, err := flags.GetString(flagSource) + if err != nil { + return types.MsgStoreCode{}, fmt.Errorf("source: %s", err) + } + builder, err := flags.GetString(flagBuilder) + if err != nil { + return types.MsgStoreCode{}, fmt.Errorf("builder: %s", err) + } + msg := types.MsgStoreCode{ - Sender: cliCtx.GetFromAddress().String(), + Sender: sender.String(), WASMByteCode: wasm, - Source: viper.GetString(flagSource), - Builder: viper.GetString(flagBuilder), + Source: source, + Builder: builder, InstantiatePermission: perm, } return msg, nil @@ -126,7 +151,7 @@ func InstantiateContractCmd() *cobra.Command { clientCtx, err := client.GetClientTxContext(cmd) - msg, err := parseInstantiateArgs(args, clientCtx) + msg, err := parseInstantiateArgs(args[0], args[1], clientCtx.GetFromAddress(), cmd.Flags()) if err != nil { return err } @@ -144,29 +169,36 @@ func InstantiateContractCmd() *cobra.Command { return cmd } -func parseInstantiateArgs(args []string, cliCtx client.Context) (types.MsgInstantiateContract, error) { +func parseInstantiateArgs(rawCodeID, initMsg string, sender sdk.AccAddress, flags *flag.FlagSet) (types.MsgInstantiateContract, error) { // get the id of the code to instantiate - codeID, err := strconv.ParseUint(args[0], 10, 64) + codeID, err := strconv.ParseUint(rawCodeID, 10, 64) if err != nil { return types.MsgInstantiateContract{}, err } - amounstStr := viper.GetString(flagAmount) - amount, err := sdk.ParseCoinsNormalized(amounstStr) + amountStr, err := flags.GetString(flagAmount) if err != nil { - return types.MsgInstantiateContract{}, err + return types.MsgInstantiateContract{}, fmt.Errorf("amount: %s", err) + } + amount, err := sdk.ParseCoinsNormalized(amountStr) + if err != nil { + return types.MsgInstantiateContract{}, fmt.Errorf("amount: %s", err) + } + label, err := flags.GetString(flagLabel) + if err != nil { + return types.MsgInstantiateContract{}, fmt.Errorf("label: %s", err) } - - label := viper.GetString(flagLabel) if label == "" { - return types.MsgInstantiateContract{}, fmt.Errorf("Label is required on all contracts") + return types.MsgInstantiateContract{}, errors.New("label is required on all contracts") + } + adminStr, err := flags.GetString(flagAdmin) + if err != nil { + return types.MsgInstantiateContract{}, fmt.Errorf("admin: %s", err) } - initMsg := args[1] - adminStr := viper.GetString(flagAdmin) // build and sign the transaction, then broadcast to Tendermint msg := types.MsgInstantiateContract{ - Sender: cliCtx.GetFromAddress().String(), + Sender: sender.String(), CodeID: codeID, Label: label, InitFunds: amount, @@ -179,29 +211,16 @@ func parseInstantiateArgs(args []string, cliCtx client.Context) (types.MsgInstan // ExecuteContractCmd will instantiate a contract from previously uploaded code. func ExecuteContractCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "execute [contract_addr_bech32] [json_encoded_send_args]", + Use: "execute [contract_addr_bech32] [json_encoded_send_args] --amount [coins,optional]", Short: "Execute a command on a wasm contract", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - // get the id of the code to instantiate - amounstStr := viper.GetString(flagAmount) - amount, err := sdk.ParseCoinsNormalized(amounstStr) + msg, err := parseExecuteArgs(args[0], args[1], clientCtx.GetFromAddress(), cmd.Flags()) if err != nil { return err } - - execMsg := args[1] - - // build and sign the transaction, then broadcast to Tendermint - msg := types.MsgExecuteContract{ - Sender: clientCtx.GetFromAddress().String(), - Contract: args[0], - SentFunds: amount, - Msg: []byte(execMsg), - } if err := msg.ValidateBasic(); err != nil { return err } @@ -213,3 +232,22 @@ func ExecuteContractCmd() *cobra.Command { flags.AddTxFlagsToCmd(cmd) return cmd } + +func parseExecuteArgs(contractAddr string, execMsg string, sender sdk.AccAddress, flags *flag.FlagSet) (types.MsgExecuteContract, error) { + amountStr, err := flags.GetString(flagAmount) + if err != nil { + return types.MsgExecuteContract{}, fmt.Errorf("amount: %s", err) + } + + amount, err := sdk.ParseCoinsNormalized(amountStr) + if err != nil { + return types.MsgExecuteContract{}, err + } + + return types.MsgExecuteContract{ + Sender: sender.String(), + Contract: contractAddr, + SentFunds: amount, + Msg: []byte(execMsg), + }, nil +} diff --git a/x/wasm/client/proposal_handler_test.go b/x/wasm/client/proposal_handler_test.go index a2319ae8..df9a4c27 100644 --- a/x/wasm/client/proposal_handler_test.go +++ b/x/wasm/client/proposal_handler_test.go @@ -30,7 +30,7 @@ func TestGovRestHandlers(t *testing.T) { "fees": []dict{{"denom": "ustake", "amount": "1000000"}}, } ) - encodingConfig := keeper.MakeEncodingConfig() + encodingConfig := keeper.MakeEncodingConfig(t) clientCtx := client.Context{}. WithJSONMarshaler(encodingConfig.Marshaler). WithTxConfig(encodingConfig.TxConfig). diff --git a/x/wasm/genesis_test.go b/x/wasm/genesis_test.go index e3fbb815..8bb6f9cd 100644 --- a/x/wasm/genesis_test.go +++ b/x/wasm/genesis_test.go @@ -79,7 +79,7 @@ func TestInitGenesis(t *testing.T) { q2 := newData.module.LegacyQuerierHandler(nil) // initialize new app with genstate - InitGenesis(newData.ctx, &newData.keeper, *genState) + InitGenesis(newData.ctx, &newData.keeper, *genState, newData.stakingKeeper, newData.module.Route().Handler()) // run same checks again on newdata, to make sure it was reinitialized correctly assertCodeList(t, q2, newData.ctx, 1) diff --git a/x/wasm/internal/keeper/genesis.go b/x/wasm/internal/keeper/genesis.go index 23e42091..809f91ed 100644 --- a/x/wasm/internal/keeper/genesis.go +++ b/x/wasm/internal/keeper/genesis.go @@ -4,21 +4,25 @@ import ( "github.com/CosmWasm/wasmd/x/wasm/internal/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - // authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - // "github.com/CosmWasm/wasmd/x/wasm/internal/types" + abci "github.com/tendermint/tendermint/abci/types" ) +// ValidatorSetSource is a subset of the staking keeper +type ValidatorSetSource interface { + ApplyAndReturnValidatorSetUpdates(sdk.Context) (updates []abci.ValidatorUpdate, err error) +} + // InitGenesis sets supply information for genesis. // // CONTRACT: all types of accounts must have been already initialized/created -func InitGenesis(ctx sdk.Context, keeper *Keeper, data types.GenesisState) error { +func InitGenesis(ctx sdk.Context, keeper *Keeper, data types.GenesisState, stakingKeeper ValidatorSetSource, msgHandler sdk.Handler) ([]abci.ValidatorUpdate, error) { keeper.setParams(ctx, data.Params) var maxCodeID uint64 for i, code := range data.Codes { err := keeper.importCode(ctx, code.CodeID, code.CodeInfo, code.CodeBytes) if err != nil { - return sdkerrors.Wrapf(err, "code %d with id: %d", i, code.CodeID) + return nil, sdkerrors.Wrapf(err, "code %d with id: %d", i, code.CodeID) } if code.CodeID > maxCodeID { maxCodeID = code.CodeID @@ -29,11 +33,11 @@ func InitGenesis(ctx sdk.Context, keeper *Keeper, data types.GenesisState) error for i, contract := range data.Contracts { contractAddr, err := sdk.AccAddressFromBech32(contract.ContractAddress) if err != nil { - return sdkerrors.Wrapf(err, "address in contract number %d", i) + return nil, sdkerrors.Wrapf(err, "address in contract number %d", i) } err = keeper.importContract(ctx, contractAddr, &contract.ContractInfo, contract.ContractState) if err != nil { - return sdkerrors.Wrapf(err, "contract number %d", i) + return nil, sdkerrors.Wrapf(err, "contract number %d", i) } maxContractID = i + 1 // not ideal but max(contractID) is not persisted otherwise } @@ -41,19 +45,32 @@ func InitGenesis(ctx sdk.Context, keeper *Keeper, data types.GenesisState) error for i, seq := range data.Sequences { err := keeper.importAutoIncrementID(ctx, seq.IDKey, seq.Value) if err != nil { - return sdkerrors.Wrapf(err, "sequence number %d", i) + return nil, sdkerrors.Wrapf(err, "sequence number %d", i) } } // sanity check seq values if keeper.peekAutoIncrementID(ctx, types.KeyLastCodeID) <= maxCodeID { - return sdkerrors.Wrapf(types.ErrInvalid, "seq %s must be greater %d ", string(types.KeyLastCodeID), maxCodeID) + return nil, sdkerrors.Wrapf(types.ErrInvalid, "seq %s must be greater %d ", string(types.KeyLastCodeID), maxCodeID) } if keeper.peekAutoIncrementID(ctx, types.KeyLastInstanceID) <= uint64(maxContractID) { - return sdkerrors.Wrapf(types.ErrInvalid, "seq %s must be greater %d ", string(types.KeyLastInstanceID), maxContractID) + return nil, sdkerrors.Wrapf(types.ErrInvalid, "seq %s must be greater %d ", string(types.KeyLastInstanceID), maxContractID) } - return nil + if len(data.GenMsgs) == 0 { + return nil, nil + } + for _, genTx := range data.GenMsgs { + msg := genTx.AsMsg() + if msg == nil { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "unknown message") + } + _, err := msgHandler(ctx, msg) + if err != nil { + return nil, sdkerrors.Wrap(err, "genesis") + } + } + return stakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) } // ExportGenesis returns a GenesisState for a given context and keeper. diff --git a/x/wasm/internal/keeper/genesis_test.go b/x/wasm/internal/keeper/genesis_test.go index a04f401a..a900bbbf 100644 --- a/x/wasm/internal/keeper/genesis_test.go +++ b/x/wasm/internal/keeper/genesis_test.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "encoding/base64" "encoding/json" + "errors" "fmt" "io/ioutil" "math/rand" @@ -24,7 +25,9 @@ import ( fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/proto/tendermint/crypto" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" ) @@ -96,7 +99,7 @@ func TestGenesisExportImport(t *testing.T) { var importState wasmTypes.GenesisState err = json.Unmarshal(exportedGenesis, &importState) require.NoError(t, err) - InitGenesis(dstCtx, dstKeeper, importState) + InitGenesis(dstCtx, dstKeeper, importState, &StakingKeeperMock{}, TestHandler(dstKeeper)) // compare whole DB for j := range srcStoreKeys { @@ -126,14 +129,16 @@ func TestGenesisExportImport(t *testing.T) { } } -func TestFailFastImport(t *testing.T) { +func TestGenesisInit(t *testing.T) { wasmCode, err := ioutil.ReadFile("./testdata/hackatom.wasm") require.NoError(t, err) myCodeInfo := wasmTypes.CodeInfoFixture(wasmTypes.WithSHA256CodeHash(wasmCode)) specs := map[string]struct { - src types.GenesisState - expSuccess bool + src types.GenesisState + stakingMock StakingKeeperMock + msgHandlerMock MockMsgHandler + expSuccess bool }{ "happy path: code info correct": { src: types.GenesisState{ @@ -355,19 +360,51 @@ func TestFailFastImport(t *testing.T) { Params: types.DefaultParams(), }, }, + "validator set update called for any genesis messages": { + src: wasmTypes.GenesisState{ + GenMsgs: []types.GenesisState_GenMsgs{ + {Sum: &types.GenesisState_GenMsgs_StoreCode{ + StoreCode: types.MsgStoreCodeFixture(), + }}, + }, + Params: types.DefaultParams(), + }, + stakingMock: StakingKeeperMock{expCalls: 1, validatorUpdate: []abci.ValidatorUpdate{ + {PubKey: crypto.PublicKey{Sum: &crypto.PublicKey_Ed25519{ + Ed25519: []byte("a valid key")}}, + Power: 100, + }, + }}, + msgHandlerMock: MockMsgHandler{expCalls: 1, expMsg: types.MsgStoreCodeFixture()}, + expSuccess: true, + }, + "validator set update not called on genesis msg handler errors": { + src: wasmTypes.GenesisState{ + GenMsgs: []types.GenesisState_GenMsgs{ + {Sum: &types.GenesisState_GenMsgs_StoreCode{ + StoreCode: types.MsgStoreCodeFixture(), + }}, + }, + Params: types.DefaultParams(), + }, + msgHandlerMock: MockMsgHandler{expCalls: 1, err: errors.New("test error response")}, + stakingMock: StakingKeeperMock{expCalls: 0}, + }, } - for msg, spec := range specs { t.Run(msg, func(t *testing.T) { keeper, ctx, _ := setupKeeper(t) require.NoError(t, types.ValidateGenesis(spec.src)) - got := InitGenesis(ctx, keeper, spec.src) - if spec.expSuccess { - require.NoError(t, got) + gotValidatorSet, gotErr := InitGenesis(ctx, keeper, spec.src, &spec.stakingMock, spec.msgHandlerMock.Handle) + if !spec.expSuccess { + require.Error(t, gotErr) return } - require.Error(t, got) + require.NoError(t, gotErr) + spec.msgHandlerMock.verifyCalls(t) + spec.stakingMock.verifyCalls(t) + assert.Equal(t, spec.stakingMock.validatorUpdate, gotValidatorSet) }) } } @@ -433,7 +470,7 @@ func TestImportContractWithCodeHistoryReset(t *testing.T) { ctx = ctx.WithBlockHeight(0).WithGasMeter(sdk.NewInfiniteGasMeter()) // when - err = InitGenesis(ctx, keeper, importState) + _, err = InitGenesis(ctx, keeper, importState, &StakingKeeperMock{}, TestHandler(keeper)) require.NoError(t, err) // verify wasm code @@ -482,6 +519,76 @@ func TestImportContractWithCodeHistoryReset(t *testing.T) { assert.Equal(t, expHistory, keeper.GetContractHistory(ctx, contractAddr)) } +func TestSupportedGenMsgTypes(t *testing.T) { + wasmCode, err := ioutil.ReadFile("./testdata/hackatom.wasm") + require.NoError(t, err) + var ( + myAddress sdk.AccAddress = bytes.Repeat([]byte{1}, sdk.AddrLen) + verifierAddress sdk.AccAddress = bytes.Repeat([]byte{2}, sdk.AddrLen) + beneficiaryAddress sdk.AccAddress = bytes.Repeat([]byte{3}, sdk.AddrLen) + ) + const denom = "stake" + importState := types.GenesisState{ + Params: types.DefaultParams(), + GenMsgs: []types.GenesisState_GenMsgs{ + { + Sum: &types.GenesisState_GenMsgs_StoreCode{ + StoreCode: &types.MsgStoreCode{ + Sender: myAddress.String(), + WASMByteCode: wasmCode, + }, + }, + }, + { + Sum: &types.GenesisState_GenMsgs_InstantiateContract{ + InstantiateContract: &types.MsgInstantiateContract{ + Sender: myAddress.String(), + CodeID: 1, + Label: "testing", + InitMsg: HackatomExampleInitMsg{ + Verifier: verifierAddress, + Beneficiary: beneficiaryAddress, + }.GetBytes(t), + InitFunds: sdk.NewCoins(sdk.NewCoin(denom, sdk.NewInt(10))), + }, + }, + }, + { + Sum: &types.GenesisState_GenMsgs_ExecuteContract{ + ExecuteContract: &types.MsgExecuteContract{ + Sender: verifierAddress.String(), + Contract: contractAddress(1, 1).String(), + Msg: []byte(`{"release":{}}`), + }, + }, + }, + }, + } + require.NoError(t, importState.ValidateBasic()) + ctx, keepers := CreateDefaultTestInput(t) + keeper := keepers.WasmKeeper + ctx = ctx.WithBlockHeight(0).WithGasMeter(sdk.NewInfiniteGasMeter()) + fundAccounts(t, ctx, keepers.AccountKeeper, keepers.BankKeeper, myAddress, sdk.NewCoins(sdk.NewCoin(denom, sdk.NewInt(100)))) + // when + _, err = InitGenesis(ctx, keeper, importState, &StakingKeeperMock{}, TestHandler(keeper)) + require.NoError(t, err) + + // verify code stored + gotWasmCode, err := keeper.GetByteCode(ctx, 1) + require.NoError(t, err) + assert.Equal(t, wasmCode, gotWasmCode) + codeInfo := keeper.GetCodeInfo(ctx, 1) + require.NotNil(t, codeInfo) + + // verify contract instantiated + cInfo := keeper.GetContractInfo(ctx, contractAddress(1, 1)) + require.NotNil(t, cInfo) + + // verify contract executed + gotBalance := keepers.BankKeeper.GetBalance(ctx, beneficiaryAddress, denom) + assert.Equal(t, sdk.NewCoin(denom, sdk.NewInt(10)), gotBalance) +} + func setupKeeper(t *testing.T) (*Keeper, sdk.Context, []sdk.StoreKey) { t.Helper() tempDir, err := ioutil.TempDir("", "wasm") @@ -505,10 +612,46 @@ func setupKeeper(t *testing.T) (*Keeper, sdk.Context, []sdk.StoreKey) { Time: time.Date(2020, time.April, 22, 12, 0, 0, 0, time.UTC), }, false, log.NewNopLogger()) - encodingConfig := MakeEncodingConfig() + encodingConfig := MakeEncodingConfig(t) wasmConfig := wasmTypes.DefaultWasmConfig() pk := paramskeeper.NewKeeper(encodingConfig.Marshaler, encodingConfig.Amino, keyParams, tkeyParams) srcKeeper := NewKeeper(encodingConfig.Marshaler, keyWasm, pk.Subspace(wasmTypes.DefaultParamspace), authkeeper.AccountKeeper{}, nil, stakingkeeper.Keeper{}, distributionkeeper.Keeper{}, nil, tempDir, wasmConfig, "", nil, nil) return &srcKeeper, ctx, []sdk.StoreKey{keyWasm, keyParams} } + +type StakingKeeperMock struct { + err error + validatorUpdate []abci.ValidatorUpdate + expCalls int + gotCalls int +} + +func (s *StakingKeeperMock) ApplyAndReturnValidatorSetUpdates(_ sdk.Context) ([]abci.ValidatorUpdate, error) { + s.gotCalls++ + return s.validatorUpdate, s.err +} + +func (s *StakingKeeperMock) verifyCalls(t *testing.T) { + assert.Equal(t, s.expCalls, s.gotCalls, "number calls") +} + +type MockMsgHandler struct { + result *sdk.Result + err error + expCalls int + gotCalls int + expMsg sdk.Msg + gotMsg sdk.Msg +} + +func (m *MockMsgHandler) Handle(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + m.gotCalls++ + m.gotMsg = msg + return m.result, m.err +} + +func (m *MockMsgHandler) verifyCalls(t *testing.T) { + assert.Equal(t, m.expMsg, m.gotMsg, "message param") + assert.Equal(t, m.expCalls, m.gotCalls, "number calls") +} diff --git a/x/wasm/internal/keeper/keeper.go b/x/wasm/internal/keeper/keeper.go index 40e9c19a..17fe84c9 100644 --- a/x/wasm/internal/keeper/keeper.go +++ b/x/wasm/internal/keeper/keeper.go @@ -628,14 +628,17 @@ func (k Keeper) generateContractAddress(ctx sdk.Context, codeID uint64) sdk.AccA return contractAddress(codeID, instanceID) } +// contractAddress builds an sdk account address for a contract. +// Intentionally kept private as this is module internal logic. func contractAddress(codeID, instanceID uint64) sdk.AccAddress { // NOTE: It is possible to get a duplicate address if either codeID or instanceID // overflow 32 bits. This is highly improbable, but something that could be refactored. contractID := codeID<<32 + instanceID return addrFromUint64(contractID) - } +// GetNextCodeID reads the next sequence id used for storing wasm code. +// Read only operation. func (k Keeper) GetNextCodeID(ctx sdk.Context) uint64 { store := ctx.KVStore(k.storeKey) bz := store.Get(types.KeyLastCodeID) diff --git a/x/wasm/internal/keeper/reflect_test.go b/x/wasm/internal/keeper/reflect_test.go index bded56b3..1587e11d 100644 --- a/x/wasm/internal/keeper/reflect_test.go +++ b/x/wasm/internal/keeper/reflect_test.go @@ -72,7 +72,7 @@ func mustParse(t *testing.T, data []byte, res interface{}) { const MaskFeatures = "staking,mask" func TestMaskReflectContractSend(t *testing.T) { - cdc := MakeTestCodec() + cdc := MakeTestCodec(t) ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(cdc), nil) accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper @@ -154,7 +154,7 @@ func TestMaskReflectContractSend(t *testing.T) { } func TestMaskReflectCustomMsg(t *testing.T) { - cdc := MakeTestCodec() + cdc := MakeTestCodec(t) ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(cdc), maskPlugins()) accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper @@ -248,7 +248,7 @@ func TestMaskReflectCustomMsg(t *testing.T) { } func TestMaskReflectCustomQuery(t *testing.T) { - cdc := MakeTestCodec() + cdc := MakeTestCodec(t) ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(cdc), maskPlugins()) accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper @@ -302,7 +302,7 @@ type maskState struct { } func TestMaskReflectWasmQueries(t *testing.T) { - ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(MakeTestCodec()), nil) + ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(MakeTestCodec(t)), nil) accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) @@ -373,7 +373,7 @@ func TestMaskReflectWasmQueries(t *testing.T) { } func TestWasmRawQueryWithNil(t *testing.T) { - ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(MakeTestCodec()), nil) + ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(MakeTestCodec(t)), nil) accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) diff --git a/x/wasm/internal/keeper/test_common.go b/x/wasm/internal/keeper/test_common.go index bd79efd1..03308ca2 100644 --- a/x/wasm/internal/keeper/test_common.go +++ b/x/wasm/internal/keeper/test_common.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "os" "testing" "time" @@ -84,10 +83,11 @@ var ModuleBasics = module.NewBasicManager( transfer.AppModuleBasic{}, ) -func MakeTestCodec() codec.Marshaler { - return MakeEncodingConfig().Marshaler +func MakeTestCodec(t *testing.T) codec.Marshaler { + return MakeEncodingConfig(t).Marshaler } -func MakeEncodingConfig() params2.EncodingConfig { + +func MakeEncodingConfig(_ *testing.T) params2.EncodingConfig { amino := codec.NewLegacyAmino() interfaceRegistry := codectypes.NewInterfaceRegistry() marshaler := codec.NewProtoCodec(interfaceRegistry) @@ -123,11 +123,14 @@ type TestKeepers struct { WasmKeeper *Keeper } +// CreateDefaultTestInput common settings for CreateTestInput +func CreateDefaultTestInput(t *testing.T) (sdk.Context, TestKeepers) { + return CreateTestInput(t, false, "staking", nil, nil) +} + // encoders can be nil to accept the defaults, or set it to override some of the message handlers (like default) func CreateTestInput(t *testing.T, isCheckTx bool, supportedFeatures string, encoders *MessageEncoders, queriers *QueryPlugins) (sdk.Context, TestKeepers) { - tempDir, err := ioutil.TempDir("", "wasm") - require.NoError(t, err) - t.Cleanup(func() { os.RemoveAll(tempDir) }) + tempDir := t.TempDir() keyWasm := sdk.NewKVStoreKey(types.StoreKey) keyAcc := sdk.NewKVStoreKey(authtypes.StoreKey) @@ -154,7 +157,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, supportedFeatures string, enc Height: 1234567, Time: time.Date(2020, time.April, 22, 12, 0, 0, 0, time.UTC), }, isCheckTx, log.NewNopLogger()) - encodingConfig := MakeEncodingConfig() + encodingConfig := MakeEncodingConfig(t) appCodec, legacyAmino := encodingConfig.Marshaler, encodingConfig.Amino paramsKeeper := paramskeeper.NewKeeper(appCodec, legacyAmino, keyParams, tkeyParams) @@ -216,7 +219,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, supportedFeatures string, enc // set some funds ot pay out validatores, based on code from: // https://github.com/cosmos/cosmos-sdk/blob/fea231556aee4d549d7551a6190389c4328194eb/x/distribution/keeper/keeper_test.go#L50-L57 distrAcc := distKeeper.GetDistributionAccount(ctx) - err = bankKeeper.SetBalances(ctx, distrAcc.GetAddress(), sdk.NewCoins( + err := bankKeeper.SetBalances(ctx, distrAcc.GetAddress(), sdk.NewCoins( sdk.NewCoin("stake", sdk.NewInt(2000000)), )) require.NoError(t, err) @@ -282,14 +285,13 @@ func CreateTestInput(t *testing.T, isCheckTx bool, supportedFeatures string, enc func TestHandler(k *Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) - switch msg := msg.(type) { + case *types.MsgStoreCode: + return handleStoreCode(ctx, k, msg) case *types.MsgInstantiateContract: return handleInstantiate(ctx, k, msg) - case *types.MsgExecuteContract: return handleExecute(ctx, k, msg) - default: errMsg := fmt.Sprintf("unrecognized wasm message type: %T", msg) return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg) @@ -297,15 +299,34 @@ func TestHandler(k *Keeper) sdk.Handler { } } +func handleStoreCode(ctx sdk.Context, k *Keeper, msg *types.MsgStoreCode) (*sdk.Result, error) { + senderAddr, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return nil, sdkerrors.Wrap(err, "sender") + } + codeID, err := k.Create(ctx, senderAddr, msg.WASMByteCode, msg.Source, msg.Builder, msg.InstantiatePermission) + if err != nil { + return nil, err + } + + return &sdk.Result{ + Data: []byte(fmt.Sprintf("%d", codeID)), + Events: ctx.EventManager().ABCIEvents(), + }, nil +} + func handleInstantiate(ctx sdk.Context, k *Keeper, msg *types.MsgInstantiateContract) (*sdk.Result, error) { senderAddr, err := sdk.AccAddressFromBech32(msg.Sender) if err != nil { return nil, sdkerrors.Wrap(err, "sender") } - adminAddr, err := sdk.AccAddressFromBech32(msg.Admin) - if err != nil { - return nil, sdkerrors.Wrap(err, "admin") + var adminAddr sdk.AccAddress + if msg.Admin != "" { + if adminAddr, err = sdk.AccAddressFromBech32(msg.Admin); err != nil { + return nil, sdkerrors.Wrap(err, "admin") + } } + contractAddr, err := k.Instantiate(ctx, msg.CodeID, senderAddr, adminAddr, msg.InitMsg, msg.Label, msg.InitFunds) if err != nil { return nil, err diff --git a/x/wasm/internal/types/genesis.go b/x/wasm/internal/types/genesis.go index 8eef81e9..5aa9ba02 100644 --- a/x/wasm/internal/types/genesis.go +++ b/x/wasm/internal/types/genesis.go @@ -32,6 +32,11 @@ func (s GenesisState) ValidateBasic() error { return sdkerrors.Wrapf(err, "sequence: %d", i) } } + for i := range s.GenMsgs { + if err := s.GenMsgs[i].ValidateBasic(); err != nil { + return sdkerrors.Wrapf(err, "gen message: %d", i) + } + } return nil } @@ -67,6 +72,28 @@ func (c Contract) ValidateBasic() error { return nil } +// AsMsg returns the underlying cosmos-sdk message instance. Null when can not be mapped to a known type. +func (m GenesisState_GenMsgs) AsMsg() sdk.Msg { + if msg := m.GetStoreCode(); msg != nil { + return msg + } + if msg := m.GetInstantiateContract(); msg != nil { + return msg + } + if msg := m.GetExecuteContract(); msg != nil { + return msg + } + return nil +} + +func (m GenesisState_GenMsgs) ValidateBasic() error { + msg := m.AsMsg() + if msg == nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "unknown message") + } + return msg.ValidateBasic() +} + // ValidateGenesis performs basic validation of supply genesis data returning an // error for any failed validation criteria. func ValidateGenesis(data GenesisState) error { diff --git a/x/wasm/internal/types/genesis.pb.go b/x/wasm/internal/types/genesis.pb.go index ebf9fe6a..172d0061 100644 --- a/x/wasm/internal/types/genesis.pb.go +++ b/x/wasm/internal/types/genesis.pb.go @@ -25,10 +25,11 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // GenesisState - genesis state of x/wasm type GenesisState struct { - Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` - Codes []Code `protobuf:"bytes,2,rep,name=codes,proto3" json:"codes,omitempty"` - Contracts []Contract `protobuf:"bytes,3,rep,name=contracts,proto3" json:"contracts,omitempty"` - Sequences []Sequence `protobuf:"bytes,4,rep,name=sequences,proto3" json:"sequences,omitempty"` + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + Codes []Code `protobuf:"bytes,2,rep,name=codes,proto3" json:"codes,omitempty"` + Contracts []Contract `protobuf:"bytes,3,rep,name=contracts,proto3" json:"contracts,omitempty"` + Sequences []Sequence `protobuf:"bytes,4,rep,name=sequences,proto3" json:"sequences,omitempty"` + GenMsgs []GenesisState_GenMsgs `protobuf:"bytes,5,rep,name=gen_msgs,json=genMsgs,proto3" json:"gen_msgs,omitempty"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -92,6 +93,115 @@ func (m *GenesisState) GetSequences() []Sequence { return nil } +func (m *GenesisState) GetGenMsgs() []GenesisState_GenMsgs { + if m != nil { + return m.GenMsgs + } + return nil +} + +// GenMsgs define the messages that can be executed during genesis phase. +// The intention is to have more human readable data that is auditable. +type GenesisState_GenMsgs struct { + // sum is a single message + // + // Types that are valid to be assigned to Sum: + // *GenesisState_GenMsgs_StoreCode + // *GenesisState_GenMsgs_InstantiateContract + // *GenesisState_GenMsgs_ExecuteContract + Sum isGenesisState_GenMsgs_Sum `protobuf_oneof:"sum"` +} + +func (m *GenesisState_GenMsgs) Reset() { *m = GenesisState_GenMsgs{} } +func (m *GenesisState_GenMsgs) String() string { return proto.CompactTextString(m) } +func (*GenesisState_GenMsgs) ProtoMessage() {} +func (*GenesisState_GenMsgs) Descriptor() ([]byte, []int) { + return fileDescriptor_52f9f2715025dba8, []int{0, 0} +} +func (m *GenesisState_GenMsgs) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState_GenMsgs) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState_GenMsgs.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState_GenMsgs) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState_GenMsgs.Merge(m, src) +} +func (m *GenesisState_GenMsgs) XXX_Size() int { + return m.Size() +} +func (m *GenesisState_GenMsgs) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState_GenMsgs.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState_GenMsgs proto.InternalMessageInfo + +type isGenesisState_GenMsgs_Sum interface { + isGenesisState_GenMsgs_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type GenesisState_GenMsgs_StoreCode struct { + StoreCode *MsgStoreCode `protobuf:"bytes,1,opt,name=store_code,json=storeCode,proto3,oneof" json:"store_code,omitempty"` +} +type GenesisState_GenMsgs_InstantiateContract struct { + InstantiateContract *MsgInstantiateContract `protobuf:"bytes,2,opt,name=instantiate_contract,json=instantiateContract,proto3,oneof" json:"instantiate_contract,omitempty"` +} +type GenesisState_GenMsgs_ExecuteContract struct { + ExecuteContract *MsgExecuteContract `protobuf:"bytes,3,opt,name=execute_contract,json=executeContract,proto3,oneof" json:"execute_contract,omitempty"` +} + +func (*GenesisState_GenMsgs_StoreCode) isGenesisState_GenMsgs_Sum() {} +func (*GenesisState_GenMsgs_InstantiateContract) isGenesisState_GenMsgs_Sum() {} +func (*GenesisState_GenMsgs_ExecuteContract) isGenesisState_GenMsgs_Sum() {} + +func (m *GenesisState_GenMsgs) GetSum() isGenesisState_GenMsgs_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *GenesisState_GenMsgs) GetStoreCode() *MsgStoreCode { + if x, ok := m.GetSum().(*GenesisState_GenMsgs_StoreCode); ok { + return x.StoreCode + } + return nil +} + +func (m *GenesisState_GenMsgs) GetInstantiateContract() *MsgInstantiateContract { + if x, ok := m.GetSum().(*GenesisState_GenMsgs_InstantiateContract); ok { + return x.InstantiateContract + } + return nil +} + +func (m *GenesisState_GenMsgs) GetExecuteContract() *MsgExecuteContract { + if x, ok := m.GetSum().(*GenesisState_GenMsgs_ExecuteContract); ok { + return x.ExecuteContract + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*GenesisState_GenMsgs) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*GenesisState_GenMsgs_StoreCode)(nil), + (*GenesisState_GenMsgs_InstantiateContract)(nil), + (*GenesisState_GenMsgs_ExecuteContract)(nil), + } +} + // Code struct encompasses CodeInfo and CodeBytes type Code struct { CodeID uint64 `protobuf:"varint,1,opt,name=code_id,json=codeId,proto3" json:"code_id,omitempty"` @@ -269,6 +379,7 @@ func (m *Sequence) GetValue() uint64 { func init() { proto.RegisterType((*GenesisState)(nil), "cosmwasm.wasm.v1beta1.GenesisState") + proto.RegisterType((*GenesisState_GenMsgs)(nil), "cosmwasm.wasm.v1beta1.GenesisState.GenMsgs") proto.RegisterType((*Code)(nil), "cosmwasm.wasm.v1beta1.Code") proto.RegisterType((*Contract)(nil), "cosmwasm.wasm.v1beta1.Contract") proto.RegisterType((*Sequence)(nil), "cosmwasm.wasm.v1beta1.Sequence") @@ -279,39 +390,47 @@ func init() { } var fileDescriptor_52f9f2715025dba8 = []byte{ - // 510 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x93, 0xc1, 0x6f, 0xd3, 0x3e, - 0x14, 0xc7, 0x9b, 0x2e, 0xcd, 0xaf, 0xf5, 0xfa, 0x63, 0xc8, 0x0c, 0x11, 0x6d, 0x2c, 0x29, 0xed, - 0xa5, 0x48, 0x28, 0x61, 0xe3, 0xc8, 0x89, 0x6c, 0x12, 0xca, 0x26, 0x10, 0xca, 0x0e, 0x48, 0xbb, - 0x54, 0x6e, 0xfc, 0x56, 0x22, 0x9a, 0xb8, 0xc4, 0xee, 0x58, 0xfe, 0x09, 0xc4, 0x9f, 0xb5, 0x1b, - 0x3d, 0x72, 0xaa, 0x50, 0x7a, 0xe3, 0xaf, 0x40, 0x76, 0xd2, 0x2c, 0x48, 0xeb, 0xb8, 0xa4, 0xf5, - 0xf3, 0xf7, 0x7d, 0xfc, 0xde, 0xf7, 0xd9, 0x68, 0x70, 0xed, 0x7e, 0x25, 0x3c, 0x76, 0xa3, 0x44, - 0x40, 0x9a, 0x90, 0xa9, 0x2b, 0xb2, 0x19, 0x70, 0x77, 0x02, 0x09, 0xf0, 0x88, 0x3b, 0xb3, 0x94, - 0x09, 0x86, 0x1f, 0x87, 0x8c, 0xc7, 0x52, 0xe6, 0xa8, 0xcf, 0xd5, 0xe1, 0x18, 0x04, 0x39, 0xdc, - 0xdb, 0x9d, 0xb0, 0x09, 0x53, 0x0a, 0x57, 0xfe, 0x2b, 0xc4, 0x7b, 0xcf, 0xee, 0x26, 0xaa, 0x6f, - 0x21, 0xe9, 0xff, 0x68, 0xa2, 0xee, 0xdb, 0xe2, 0x84, 0x73, 0x41, 0x04, 0xe0, 0xd7, 0xc8, 0x98, - 0x91, 0x94, 0xc4, 0xdc, 0xd4, 0x7a, 0xda, 0x70, 0xfb, 0xe8, 0xc0, 0xb9, 0xf3, 0x44, 0xe7, 0x83, - 0x12, 0x79, 0xfa, 0xcd, 0xd2, 0x6e, 0x04, 0x65, 0x0a, 0x3e, 0x45, 0xad, 0x90, 0x51, 0xe0, 0x66, - 0xb3, 0xb7, 0x35, 0xdc, 0x3e, 0xda, 0xdf, 0x90, 0x7b, 0xcc, 0x28, 0x78, 0x4f, 0x64, 0xe6, 0xef, - 0xa5, 0xbd, 0xa3, 0x32, 0x5e, 0xb0, 0x38, 0x12, 0x10, 0xcf, 0x44, 0x16, 0x14, 0x08, 0x7c, 0x81, - 0x3a, 0x21, 0x4b, 0x44, 0x4a, 0x42, 0xc1, 0xcd, 0x2d, 0xc5, 0xb3, 0x37, 0xf2, 0x0a, 0x9d, 0xb7, - 0x5f, 0x32, 0x1f, 0x55, 0x99, 0x35, 0xee, 0x2d, 0x4e, 0xb2, 0x39, 0x7c, 0x99, 0x43, 0x12, 0x02, - 0x37, 0xf5, 0x7b, 0xd9, 0xe7, 0xa5, 0xee, 0x96, 0x5d, 0x65, 0xd6, 0xd9, 0x55, 0xb0, 0xff, 0x4d, - 0x43, 0xba, 0x6c, 0x10, 0x0f, 0xd0, 0x7f, 0xb2, 0x93, 0x51, 0x44, 0x95, 0x95, 0xba, 0x87, 0xf2, - 0xa5, 0x6d, 0xc8, 0x2d, 0xff, 0x24, 0x30, 0xe4, 0x96, 0x4f, 0xb1, 0x27, 0xbb, 0x94, 0xa2, 0xe4, - 0x92, 0x99, 0x4d, 0xe5, 0xb8, 0x7d, 0x8f, 0x6b, 0x7e, 0x72, 0xc9, 0x4a, 0xcf, 0xdb, 0x61, 0xb9, - 0xc6, 0x07, 0x08, 0x29, 0xc6, 0x38, 0x13, 0x20, 0xad, 0xd2, 0x86, 0xdd, 0x40, 0x51, 0x3d, 0x19, - 0xe8, 0x2f, 0x34, 0xd4, 0x5e, 0x3b, 0x84, 0x9f, 0xa3, 0x87, 0x6b, 0x1b, 0x46, 0x84, 0xd2, 0x14, - 0x78, 0x31, 0xe8, 0x4e, 0xb0, 0xb3, 0x8e, 0xbf, 0x29, 0xc2, 0xf8, 0x3d, 0xfa, 0xbf, 0x92, 0xd6, - 0xca, 0x1b, 0xfc, 0x63, 0x08, 0xb5, 0x12, 0xbb, 0x61, 0x2d, 0x86, 0x7d, 0xf4, 0xa0, 0xe2, 0x71, - 0x79, 0xd7, 0xca, 0xa9, 0x3e, 0xdd, 0x00, 0x7c, 0xc7, 0x28, 0x4c, 0x4b, 0x52, 0x55, 0x89, 0xba, - 0xa4, 0x7d, 0x0f, 0xb5, 0xd7, 0x73, 0xc1, 0x3d, 0x64, 0x44, 0x74, 0xf4, 0x19, 0x32, 0xd5, 0x47, - 0xd7, 0xeb, 0xe4, 0x4b, 0xbb, 0xe5, 0x9f, 0x9c, 0x41, 0x16, 0xb4, 0x22, 0x7a, 0x06, 0x19, 0xde, - 0x45, 0xad, 0x2b, 0x32, 0x9d, 0x83, 0x6a, 0x40, 0x0f, 0x8a, 0x85, 0x77, 0x7a, 0x93, 0x5b, 0xda, - 0x22, 0xb7, 0xb4, 0x5f, 0xb9, 0xa5, 0x7d, 0x5f, 0x59, 0x8d, 0xc5, 0xca, 0x6a, 0xfc, 0x5c, 0x59, - 0x8d, 0x8b, 0x97, 0x93, 0x48, 0x7c, 0x9a, 0x8f, 0x9d, 0x90, 0xc5, 0xee, 0x31, 0xe3, 0xf1, 0x47, - 0xf9, 0x86, 0x64, 0x69, 0xd4, 0xbd, 0x2e, 0x7f, 0xff, 0x7e, 0x51, 0x63, 0x43, 0x3d, 0xa6, 0x57, - 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x56, 0x37, 0xff, 0x93, 0xc3, 0x03, 0x00, 0x00, + // 639 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xc1, 0x6e, 0xd3, 0x40, + 0x10, 0x86, 0xe3, 0x26, 0x4e, 0x93, 0x6d, 0xa0, 0xd5, 0xb6, 0x88, 0x28, 0xa5, 0x71, 0x49, 0x2e, + 0xad, 0x80, 0x98, 0x96, 0x23, 0x27, 0xdc, 0x22, 0x9a, 0x56, 0x45, 0xc8, 0x95, 0x40, 0xea, 0x25, + 0x72, 0xec, 0xa9, 0xb1, 0xa8, 0xbd, 0x21, 0xb3, 0x29, 0xf5, 0x4b, 0x20, 0xc4, 0x53, 0xf5, 0xd8, + 0x23, 0xa7, 0x08, 0xa5, 0x37, 0x9e, 0x02, 0xed, 0x7a, 0xed, 0xba, 0x22, 0x2e, 0x17, 0x27, 0x3b, + 0xfe, 0xff, 0xcf, 0x33, 0xb3, 0xb3, 0x4b, 0xba, 0x97, 0xe6, 0x37, 0x07, 0x43, 0x33, 0x88, 0x38, + 0x8c, 0x23, 0xe7, 0xdc, 0xe4, 0xf1, 0x08, 0xd0, 0xf4, 0x21, 0x02, 0x0c, 0xb0, 0x37, 0x1a, 0x33, + 0xce, 0xe8, 0x23, 0x97, 0x61, 0x28, 0x64, 0x3d, 0xf9, 0xb8, 0xd8, 0x19, 0x02, 0x77, 0x76, 0x5a, + 0x6b, 0x3e, 0xf3, 0x99, 0x54, 0x98, 0xe2, 0x5f, 0x22, 0x6e, 0x3d, 0x9d, 0x4f, 0x94, 0x4f, 0x25, + 0x31, 0xe6, 0x4b, 0x42, 0xf4, 0x13, 0x41, 0xe7, 0x4a, 0x27, 0x8d, 0x77, 0x49, 0x0a, 0x27, 0xdc, + 0xe1, 0x40, 0x5f, 0x93, 0xea, 0xc8, 0x19, 0x3b, 0x21, 0x36, 0xb5, 0x4d, 0x6d, 0x6b, 0x69, 0x77, + 0xa3, 0x37, 0x37, 0xa5, 0xde, 0x07, 0x29, 0xb2, 0x2a, 0x57, 0x53, 0xa3, 0x64, 0x2b, 0x0b, 0x3d, + 0x24, 0xba, 0xcb, 0x3c, 0xc0, 0xe6, 0xc2, 0x66, 0x79, 0x6b, 0x69, 0x77, 0xbd, 0xc0, 0xbb, 0xc7, + 0x3c, 0xb0, 0x1e, 0x0b, 0xe7, 0x9f, 0xa9, 0xb1, 0x2c, 0x1d, 0xcf, 0x59, 0x18, 0x70, 0x08, 0x47, + 0x3c, 0xb6, 0x13, 0x04, 0x3d, 0x25, 0x75, 0x97, 0x45, 0x7c, 0xec, 0xb8, 0x1c, 0x9b, 0x65, 0xc9, + 0x33, 0x0a, 0x79, 0x89, 0xce, 0x5a, 0x57, 0xcc, 0xd5, 0xcc, 0x99, 0xe3, 0xde, 0xe2, 0x04, 0x1b, + 0xe1, 0xeb, 0x04, 0x22, 0x17, 0xb0, 0x59, 0xb9, 0x97, 0x7d, 0xa2, 0x74, 0xb7, 0xec, 0xcc, 0x99, + 0x67, 0x67, 0x41, 0x3a, 0x24, 0x35, 0x1f, 0xa2, 0x41, 0x88, 0x3e, 0x36, 0x75, 0x89, 0x7e, 0x56, + 0x80, 0xce, 0xf7, 0x5d, 0x2c, 0x8e, 0xd1, 0x47, 0xab, 0xa5, 0x3e, 0x43, 0x53, 0x48, 0xee, 0x2b, + 0x8b, 0x7e, 0x22, 0x6a, 0xfd, 0x5c, 0x20, 0x8b, 0xca, 0x40, 0xf7, 0x09, 0x41, 0xce, 0xc6, 0x30, + 0x10, 0x6d, 0x53, 0x9b, 0xd6, 0x2d, 0xf8, 0xe2, 0x31, 0xfa, 0x27, 0x42, 0x2b, 0x36, 0xe0, 0xa0, + 0x64, 0xd7, 0x31, 0x5d, 0xd0, 0x21, 0x59, 0x0b, 0x22, 0xe4, 0x4e, 0xc4, 0x03, 0x87, 0x0b, 0x56, + 0xd2, 0xaa, 0xe6, 0x82, 0xe4, 0xbd, 0x28, 0xe6, 0xf5, 0x6f, 0x5d, 0xe9, 0x36, 0x1c, 0x94, 0xec, + 0xd5, 0xe0, 0xdf, 0x30, 0xfd, 0x48, 0x56, 0xe0, 0x12, 0xdc, 0x49, 0x9e, 0x5f, 0x96, 0xfc, 0xed, + 0x62, 0xfe, 0xdb, 0xc4, 0x91, 0x63, 0x2f, 0xc3, 0xdd, 0x90, 0xa5, 0x93, 0x32, 0x4e, 0xc2, 0xce, + 0x77, 0x8d, 0x54, 0x64, 0x2d, 0x5d, 0xb2, 0x28, 0x7a, 0x31, 0x08, 0x3c, 0xd9, 0x8e, 0x8a, 0x45, + 0x66, 0x53, 0xa3, 0x2a, 0x5e, 0xf5, 0xf7, 0xed, 0xaa, 0x78, 0xd5, 0xf7, 0xa8, 0x25, 0xc6, 0x4b, + 0x88, 0xa2, 0x33, 0xa6, 0xaa, 0x34, 0xee, 0x19, 0xd7, 0x7e, 0x74, 0xc6, 0xd4, 0xb0, 0xd7, 0x5c, + 0xb5, 0xa6, 0x1b, 0x84, 0x48, 0xc6, 0x30, 0xe6, 0x80, 0xb2, 0x94, 0x86, 0x2d, 0xa9, 0x96, 0x08, + 0x74, 0xae, 0x35, 0x52, 0xcb, 0x8a, 0xdf, 0x26, 0x2b, 0x69, 0xd1, 0x03, 0xc7, 0xf3, 0xc6, 0x80, + 0xc9, 0x09, 0xab, 0xdb, 0xcb, 0x69, 0xfc, 0x4d, 0x12, 0xa6, 0xef, 0xc9, 0x83, 0x4c, 0x9a, 0x4b, + 0xaf, 0xfb, 0x9f, 0xe9, 0xcf, 0xa5, 0xd8, 0x70, 0x73, 0x31, 0xda, 0x27, 0x0f, 0x33, 0x1e, 0x8a, + 0x61, 0x53, 0xc7, 0xe9, 0x49, 0x51, 0xd7, 0x99, 0x07, 0xe7, 0x8a, 0x94, 0x65, 0x22, 0xa7, 0xb4, + 0x63, 0x91, 0x5a, 0x7a, 0x20, 0xe8, 0x26, 0xa9, 0x06, 0xde, 0xe0, 0x0b, 0xc4, 0xb2, 0x8e, 0x86, + 0x55, 0x9f, 0x4d, 0x0d, 0xbd, 0xbf, 0x7f, 0x04, 0xb1, 0xad, 0x07, 0xde, 0x11, 0xc4, 0x74, 0x8d, + 0xe8, 0x17, 0xce, 0xf9, 0x04, 0x64, 0x01, 0x15, 0x3b, 0x59, 0x58, 0x87, 0x57, 0xb3, 0xb6, 0x76, + 0x3d, 0x6b, 0x6b, 0xbf, 0x67, 0x6d, 0xed, 0xc7, 0x4d, 0xbb, 0x74, 0x7d, 0xd3, 0x2e, 0xfd, 0xba, + 0x69, 0x97, 0x4e, 0x5f, 0xfa, 0x01, 0xff, 0x3c, 0x19, 0xf6, 0x5c, 0x16, 0x9a, 0x7b, 0x0c, 0xc3, + 0x4f, 0xe2, 0xea, 0x12, 0xa9, 0x79, 0xe6, 0xa5, 0xfa, 0xbd, 0x7b, 0x91, 0x0d, 0xab, 0xf2, 0x16, + 0x7b, 0xf5, 0x37, 0x00, 0x00, 0xff, 0xff, 0xc2, 0xba, 0x9d, 0x7b, 0x5d, 0x05, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -334,6 +453,20 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.GenMsgs) > 0 { + for iNdEx := len(m.GenMsgs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.GenMsgs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } if len(m.Sequences) > 0 { for iNdEx := len(m.Sequences) - 1; iNdEx >= 0; iNdEx-- { { @@ -389,6 +522,101 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *GenesisState_GenMsgs) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState_GenMsgs) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState_GenMsgs) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sum != nil { + { + size := m.Sum.Size() + i -= size + if _, err := m.Sum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + return len(dAtA) - i, nil +} + +func (m *GenesisState_GenMsgs_StoreCode) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState_GenMsgs_StoreCode) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.StoreCode != nil { + { + size, err := m.StoreCode.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *GenesisState_GenMsgs_InstantiateContract) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState_GenMsgs_InstantiateContract) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.InstantiateContract != nil { + { + size, err := m.InstantiateContract.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *GenesisState_GenMsgs_ExecuteContract) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState_GenMsgs_ExecuteContract) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.ExecuteContract != nil { + { + size, err := m.ExecuteContract.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + return len(dAtA) - i, nil +} func (m *Code) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -560,9 +788,63 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) } } + if len(m.GenMsgs) > 0 { + for _, e := range m.GenMsgs { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } return n } +func (m *GenesisState_GenMsgs) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sum != nil { + n += m.Sum.Size() + } + return n +} + +func (m *GenesisState_GenMsgs_StoreCode) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.StoreCode != nil { + l = m.StoreCode.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + return n +} +func (m *GenesisState_GenMsgs_InstantiateContract) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.InstantiateContract != nil { + l = m.InstantiateContract.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + return n +} +func (m *GenesisState_GenMsgs_ExecuteContract) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ExecuteContract != nil { + l = m.ExecuteContract.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + return n +} func (m *Code) Size() (n int) { if m == nil { return 0 @@ -788,6 +1070,198 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GenMsgs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.GenMsgs = append(m.GenMsgs, GenesisState_GenMsgs{}) + if err := m.GenMsgs[len(m.GenMsgs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GenesisState_GenMsgs) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenMsgs: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenMsgs: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StoreCode", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &MsgStoreCode{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &GenesisState_GenMsgs_StoreCode{v} + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InstantiateContract", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &MsgInstantiateContract{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &GenesisState_GenMsgs_InstantiateContract{v} + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecuteContract", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &MsgExecuteContract{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &GenesisState_GenMsgs_ExecuteContract{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/x/wasm/internal/types/genesis.proto b/x/wasm/internal/types/genesis.proto index 7a6dd185..806a5b09 100644 --- a/x/wasm/internal/types/genesis.proto +++ b/x/wasm/internal/types/genesis.proto @@ -3,6 +3,7 @@ package cosmwasm.wasm.v1beta1; import "gogoproto/gogo.proto"; import "x/wasm/internal/types/types.proto"; +import "x/wasm/internal/types/msg.proto"; option go_package = "github.com/CosmWasm/wasmd/x/wasmd/internal/types"; @@ -12,6 +13,18 @@ message GenesisState { repeated Code codes = 2 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "codes,omitempty"]; repeated Contract contracts = 3 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "contracts,omitempty"]; repeated Sequence sequences = 4 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "sequences,omitempty"]; + repeated GenMsgs gen_msgs = 5 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "gen_msgs,omitempty"]; + + // GenMsgs define the messages that can be executed during genesis phase in order. + // The intention is to have more human readable data that is auditable. + message GenMsgs { + // sum is a single message + oneof sum { + MsgStoreCode store_code = 1; + MsgInstantiateContract instantiate_contract = 2; + MsgExecuteContract execute_contract = 3; + } + } } // Code struct encompasses CodeInfo and CodeBytes diff --git a/x/wasm/internal/types/genesis_test.go b/x/wasm/internal/types/genesis_test.go index f75da315..d8da55c5 100644 --- a/x/wasm/internal/types/genesis_test.go +++ b/x/wasm/internal/types/genesis_test.go @@ -39,6 +39,30 @@ func TestValidateGenesisState(t *testing.T) { }, expError: true, }, + "genesis store code message invalid": { + srcMutator: func(s *GenesisState) { + s.GenMsgs[0].GetStoreCode().WASMByteCode = nil + }, + expError: true, + }, + "genesis instantiate contract message invalid": { + srcMutator: func(s *GenesisState) { + s.GenMsgs[1].GetInstantiateContract().CodeID = 0 + }, + expError: true, + }, + "genesis execute contract message invalid": { + srcMutator: func(s *GenesisState) { + s.GenMsgs[2].GetExecuteContract().Sender = "invalid" + }, + expError: true, + }, + "genesis invalid message type": { + srcMutator: func(s *GenesisState) { + s.GenMsgs[0].Sum = nil + }, + expError: true, + }, } for msg, spec := range specs { t.Run(msg, func(t *testing.T) { diff --git a/x/wasm/internal/types/test_fixtures.go b/x/wasm/internal/types/test_fixtures.go index 827c3b0f..66f0f711 100644 --- a/x/wasm/internal/types/test_fixtures.go +++ b/x/wasm/internal/types/test_fixtures.go @@ -14,6 +14,7 @@ func GenesisFixture(mutators ...func(*GenesisState)) GenesisState { numCodes = 2 numContracts = 2 numSequences = 2 + numMsg = 3 ) fixture := GenesisState{ @@ -34,6 +35,11 @@ func GenesisFixture(mutators ...func(*GenesisState)) GenesisState { Value: uint64(i), } } + fixture.GenMsgs = []GenesisState_GenMsgs{ + {Sum: &GenesisState_GenMsgs_StoreCode{StoreCode: MsgStoreCodeFixture()}}, + {Sum: &GenesisState_GenMsgs_InstantiateContract{InstantiateContract: MsgInstantiateContractFixture()}}, + {Sum: &GenesisState_GenMsgs_ExecuteContract{ExecuteContract: MsgExecuteContractFixture()}}, + } for _, m := range mutators { m(&fixture) } @@ -120,6 +126,61 @@ func WithSHA256CodeHash(wasmCode []byte) func(info *CodeInfo) { } } +func MsgStoreCodeFixture(mutators ...func(*MsgStoreCode)) *MsgStoreCode { + var wasmIdent = []byte("\x00\x61\x73\x6D") + const anyAddress = "cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpjnp7du" + r := &MsgStoreCode{ + Sender: anyAddress, + WASMByteCode: wasmIdent, + Source: "https://example.com/code", + Builder: "foo/bar:latest", + InstantiatePermission: &AllowEverybody, + } + for _, m := range mutators { + m(r) + } + return r +} + +func MsgInstantiateContractFixture(mutators ...func(*MsgInstantiateContract)) *MsgInstantiateContract { + const anyAddress = "cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpjnp7du" + r := &MsgInstantiateContract{ + Sender: anyAddress, + Admin: anyAddress, + CodeID: 1, + Label: "testing", + InitMsg: []byte(`{"foo":"bar"}`), + InitFunds: sdk.Coins{{ + Denom: "stake", + Amount: sdk.NewInt(1), + }}, + } + for _, m := range mutators { + m(r) + } + return r +} + +func MsgExecuteContractFixture(mutators ...func(*MsgExecuteContract)) *MsgExecuteContract { + const ( + anyAddress = "cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpjnp7du" + firstContractAddress = "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5" + ) + r := &MsgExecuteContract{ + Sender: anyAddress, + Contract: firstContractAddress, + Msg: []byte(`{"do":"something"}`), + SentFunds: sdk.Coins{{ + Denom: "stake", + Amount: sdk.NewInt(1), + }}, + } + for _, m := range mutators { + m(r) + } + return r +} + func StoreCodeProposalFixture(mutators ...func(*StoreCodeProposal)) *StoreCodeProposal { const anyAddress = "cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpjnp7du" p := &StoreCodeProposal{ diff --git a/x/wasm/module.go b/x/wasm/module.go index feb01dee..5a4aee15 100644 --- a/x/wasm/module.go +++ b/x/wasm/module.go @@ -94,14 +94,16 @@ func (b AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) // AppModule implements an application module for the wasm module. type AppModule struct { AppModuleBasic - keeper *Keeper + keeper *Keeper + validatorSetSource keeper.ValidatorSetSource } // NewAppModule creates a new AppModule object -func NewAppModule(keeper *Keeper) AppModule { +func NewAppModule(keeper *Keeper, validatorSetSource keeper.ValidatorSetSource) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - keeper: keeper, + AppModuleBasic: AppModuleBasic{}, + keeper: keeper, + validatorSetSource: validatorSetSource, } } @@ -131,10 +133,11 @@ func (AppModule) QuerierRoute() string { func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate { var genesisState GenesisState cdc.MustUnmarshalJSON(data, &genesisState) - if err := InitGenesis(ctx, am.keeper, genesisState); err != nil { + validators, err := InitGenesis(ctx, am.keeper, genesisState, am.validatorSetSource, am.Route().Handler()) + if err != nil { panic(err) } - return []abci.ValidatorUpdate{} + return validators } // ExportGenesis returns the exported genesis state as raw bytes for the wasm diff --git a/x/wasm/module_test.go b/x/wasm/module_test.go index 591725a4..68fbe56f 100644 --- a/x/wasm/module_test.go +++ b/x/wasm/module_test.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/dvsekhvalnov/jose2go/base64url" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -22,23 +23,24 @@ import ( ) type testData struct { - module module.AppModule - ctx sdk.Context - acctKeeper authkeeper.AccountKeeper - keeper Keeper - bankKeeper bankkeeper.Keeper + module module.AppModule + ctx sdk.Context + acctKeeper authkeeper.AccountKeeper + keeper Keeper + bankKeeper bankkeeper.Keeper + stakingKeeper stakingkeeper.Keeper } // returns a cleanup function, which must be defered on func setupTest(t *testing.T) testData { ctx, keepers := CreateTestInput(t, false, "staking", nil, nil) - acctKeeper, wasmKeeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper data := testData{ - module: NewAppModule(wasmKeeper), - ctx: ctx, - acctKeeper: acctKeeper, - keeper: *wasmKeeper, - bankKeeper: bankKeeper, + module: NewAppModule(keepers.WasmKeeper, keepers.StakingKeeper), + ctx: ctx, + acctKeeper: keepers.AccountKeeper, + keeper: *keepers.WasmKeeper, + bankKeeper: keepers.BankKeeper, + stakingKeeper: keepers.StakingKeeper, } return data }