mirror of https://github.com/certusone/wasmd.git
Polish gen msg code and tests
This commit is contained in:
parent
5882bf36cd
commit
83d22a9352
|
@ -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)
|
||||
|
|
|
@ -16,12 +16,16 @@ import (
|
|||
"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]\",",
|
||||
|
@ -41,7 +45,7 @@ func GenesisStoreCodeCmd(defaultNodeHome string) *cobra.Command {
|
|||
return err
|
||||
}
|
||||
|
||||
return alterModuleState(cmd, func(s *types.GenesisState) error {
|
||||
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},
|
||||
})
|
||||
|
@ -61,6 +65,8 @@ func GenesisStoreCodeCmd(defaultNodeHome string) *cobra.Command {
|
|||
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]",
|
||||
|
@ -80,16 +86,21 @@ func GenesisInstantiateContractCmd(defaultNodeHome string) *cobra.Command {
|
|||
return err
|
||||
}
|
||||
|
||||
// todo: check conditions to fail fast
|
||||
// - does actor account exists?
|
||||
// - enough balance to succeed for init coins
|
||||
return alterModuleState(cmd, func(s *types.GenesisState) error {
|
||||
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
|
||||
var codeInfo *codeMeta
|
||||
for i := range codeInfos {
|
||||
if codeInfos[i].CodeID == msg.CodeID {
|
||||
codeInfo = &codeInfos[i]
|
||||
|
@ -121,6 +132,8 @@ func GenesisInstantiateContractCmd(defaultNodeHome string) *cobra.Command {
|
|||
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]",
|
||||
|
@ -140,12 +153,15 @@ func GenesisExecuteContractCmd(defaultNodeHome string) *cobra.Command {
|
|||
return err
|
||||
}
|
||||
|
||||
// todo: check conditions to fail fast
|
||||
// - does actor account exists?
|
||||
// - enough balance to succeed for exec coins
|
||||
// - does contract address exists?
|
||||
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")
|
||||
}
|
||||
|
||||
return alterModuleState(cmd, func(s *types.GenesisState) error {
|
||||
// - does contract address exists?
|
||||
if !hasContract(s, msg.Contract) {
|
||||
return fmt.Errorf("unknown contract: %s", msg.Contract)
|
||||
|
@ -166,80 +182,23 @@ func GenesisExecuteContractCmd(defaultNodeHome string) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
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 {
|
||||
type ContractMeta struct {
|
||||
ContractAddress string `json:"contract_address"`
|
||||
Info types.ContractInfo `json:"info"`
|
||||
}
|
||||
var all []ContractMeta
|
||||
err := readModuleState(cmd, func(state *types.GenesisState) error {
|
||||
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 nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
bz, err := json.MarshalIndent(all, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return clientCtx.PrintString(string(bz))
|
||||
|
||||
},
|
||||
}
|
||||
cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory")
|
||||
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 {
|
||||
var all []CodeMeta
|
||||
err := readModuleState(cmd, func(state *types.GenesisState) (err error) {
|
||||
all, err = getAllCodes(state)
|
||||
return
|
||||
})
|
||||
g, err := readGenesis(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
bz, err := json.MarshalIndent(all, "", " ")
|
||||
all, err := getAllCodes(g.wasmModuleState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return clientCtx.PrintString(string(bz))
|
||||
return printJsonOutput(cmd, all)
|
||||
|
||||
},
|
||||
}
|
||||
|
@ -248,15 +207,47 @@ func GenesisListCodesCmd(defaultNodeHome string) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
type CodeMeta struct {
|
||||
// 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
|
||||
func getAllCodes(state *types.GenesisState) ([]codeMeta, error) {
|
||||
var all []codeMeta
|
||||
for _, c := range state.Codes {
|
||||
all = append(all, CodeMeta{
|
||||
all = append(all, codeMeta{
|
||||
CodeID: c.CodeID,
|
||||
Info: c.CodeInfo,
|
||||
})
|
||||
|
@ -277,7 +268,7 @@ func getAllCodes(state *types.GenesisState) ([]CodeMeta, error) {
|
|||
accessConfig = state.Params.InstantiateDefaultPermission.With(creator)
|
||||
}
|
||||
hash := sha256.Sum256(msg.WASMByteCode)
|
||||
all = append(all, CodeMeta{
|
||||
all = append(all, codeMeta{
|
||||
CodeID: seq,
|
||||
Info: types.CodeInfo{
|
||||
CodeHash: hash[:],
|
||||
|
@ -293,6 +284,52 @@ func getAllCodes(state *types.GenesisState) ([]CodeMeta, error) {
|
|||
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 {
|
||||
|
@ -311,31 +348,15 @@ func hasContract(state *types.GenesisState, contractAddr string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// readModuleState read only version of alterModuleState
|
||||
func readModuleState(cmd *cobra.Command, callback func(s *types.GenesisState) error) error {
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
serverCtx := server.GetServerContextFromCmd(cmd)
|
||||
config := serverCtx.Config
|
||||
config.SetRoot(clientCtx.HomeDir)
|
||||
|
||||
genFile := config.GenesisFile()
|
||||
appState, _, err := genutiltypes.GenesisStateFromGenFile(genFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal genesis state: %w", err)
|
||||
}
|
||||
|
||||
var wasmGenesisState types.GenesisState
|
||||
if appState[types.ModuleName] != nil {
|
||||
clientCtx.JSONMarshaler.MustUnmarshalJSON(appState[types.ModuleName], &wasmGenesisState)
|
||||
}
|
||||
return callback(&wasmGenesisState)
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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) error) error {
|
||||
func readGenesis(cmd *cobra.Command) (*genesisData, error) {
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
serverCtx := server.GetServerContextFromCmd(cmd)
|
||||
config := serverCtx.Config
|
||||
|
@ -344,34 +365,56 @@ func alterModuleState(cmd *cobra.Command, callback func(s *types.GenesisState) e
|
|||
genFile := config.GenesisFile()
|
||||
appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal genesis state: %w", err)
|
||||
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)
|
||||
}
|
||||
if err := callback(&wasmGenesisState); err != nil {
|
||||
|
||||
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 := wasmGenesisState.ValidateBasic(); err != nil {
|
||||
if err := callback(g.wasmModuleState, g.appState); err != nil {
|
||||
return err
|
||||
}
|
||||
wasmGenStateBz, err := clientCtx.JSONMarshaler.MarshalJSON(&wasmGenesisState)
|
||||
// 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")
|
||||
}
|
||||
|
||||
appState[types.ModuleName] = wasmGenStateBz
|
||||
appStateJSON, err := json.Marshal(appState)
|
||||
g.appState[types.ModuleName] = wasmGenStateBz
|
||||
appStateJSON, err := json.Marshal(g.appState)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(err, "marshal application genesis state")
|
||||
}
|
||||
|
||||
genDoc.AppState = appStateJSON
|
||||
return genutil.ExportGenesisFile(genDoc, genFile)
|
||||
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 {
|
||||
|
@ -383,6 +426,8 @@ func contractSeqValue(state *types.GenesisState) uint64 {
|
|||
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 {
|
||||
|
@ -394,6 +439,9 @@ func codeSeqValue(state *types.GenesisState) uint64 {
|
|||
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 {
|
||||
|
@ -424,6 +472,7 @@ func getActorAddress(cmd *cobra.Command) (sdk.AccAddress, error) {
|
|||
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.
|
||||
|
@ -431,6 +480,7 @@ func contractAddress(codeID, instanceID uint64) sdk.AccAddress {
|
|||
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'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package cli_test
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -8,7 +8,6 @@ import (
|
|||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/CosmWasm/wasmd/x/wasm/client/cli"
|
||||
"github.com/CosmWasm/wasmd/x/wasm/internal/keeper"
|
||||
"github.com/CosmWasm/wasmd/x/wasm/internal/types"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
|
@ -18,9 +17,11 @@ import (
|
|||
"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"
|
||||
|
@ -31,6 +32,8 @@ import (
|
|||
|
||||
var wasmIdent = []byte("\x00\x61\x73\x6D")
|
||||
|
||||
var myWellFundedAccount = keeper.RandomBech32AccountAddress(nil)
|
||||
|
||||
const defaultTestKeyName = "my-key-name"
|
||||
|
||||
func TestGenesisStoreCodeCmd(t *testing.T) {
|
||||
|
@ -47,7 +50,6 @@ func TestGenesisStoreCodeCmd(t *testing.T) {
|
|||
mutator func(cmd *cobra.Command)
|
||||
expError bool
|
||||
}{
|
||||
|
||||
"all good with actor address": {
|
||||
srcGenesis: minimalWasmGenesis,
|
||||
mutator: func(cmd *cobra.Command) {
|
||||
|
@ -99,7 +101,7 @@ func TestGenesisStoreCodeCmd(t *testing.T) {
|
|||
homeDir := setupGenesis(t, spec.srcGenesis)
|
||||
|
||||
// when
|
||||
cmd := cli.GenesisStoreCodeCmd(homeDir)
|
||||
cmd := GenesisStoreCodeCmd(homeDir)
|
||||
spec.mutator(cmd)
|
||||
err := executeCmdWithContext(t, homeDir, cmd)
|
||||
if spec.expError {
|
||||
|
@ -150,7 +152,7 @@ func TestInstantiateContractCmd(t *testing.T) {
|
|||
cmd.SetArgs([]string{"1", `{}`})
|
||||
flagSet := cmd.Flags()
|
||||
flagSet.Set("label", "testing")
|
||||
flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t))
|
||||
flagSet.Set("run-as", myWellFundedAccount)
|
||||
},
|
||||
expMsgCount: 1,
|
||||
},
|
||||
|
@ -165,7 +167,7 @@ func TestInstantiateContractCmd(t *testing.T) {
|
|||
cmd.SetArgs([]string{"1", `{}`})
|
||||
flagSet := cmd.Flags()
|
||||
flagSet.Set("label", "testing")
|
||||
flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t))
|
||||
flagSet.Set("run-as", myWellFundedAccount)
|
||||
},
|
||||
expMsgCount: 2,
|
||||
},
|
||||
|
@ -183,7 +185,7 @@ func TestInstantiateContractCmd(t *testing.T) {
|
|||
cmd.SetArgs([]string{"100", `{}`})
|
||||
flagSet := cmd.Flags()
|
||||
flagSet.Set("label", "testing")
|
||||
flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t))
|
||||
flagSet.Set("run-as", myWellFundedAccount)
|
||||
},
|
||||
expMsgCount: 2,
|
||||
},
|
||||
|
@ -193,7 +195,7 @@ func TestInstantiateContractCmd(t *testing.T) {
|
|||
cmd.SetArgs([]string{"2", `{}`})
|
||||
flagSet := cmd.Flags()
|
||||
flagSet.Set("label", "testing")
|
||||
flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t))
|
||||
flagSet.Set("run-as", myWellFundedAccount)
|
||||
},
|
||||
expError: true,
|
||||
},
|
||||
|
@ -206,6 +208,31 @@ func TestInstantiateContractCmd(t *testing.T) {
|
|||
})}},
|
||||
},
|
||||
},
|
||||
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()
|
||||
|
@ -220,7 +247,7 @@ func TestInstantiateContractCmd(t *testing.T) {
|
|||
homeDir := setupGenesis(t, spec.srcGenesis)
|
||||
|
||||
// when
|
||||
cmd := cli.GenesisInstantiateContractCmd(homeDir)
|
||||
cmd := GenesisInstantiateContractCmd(homeDir)
|
||||
spec.mutator(cmd)
|
||||
err := executeCmdWithContext(t, homeDir, cmd)
|
||||
if spec.expError {
|
||||
|
@ -274,7 +301,7 @@ func TestExecuteContractCmd(t *testing.T) {
|
|||
mutator: func(cmd *cobra.Command) {
|
||||
cmd.SetArgs([]string{firstContractAddress, `{}`})
|
||||
flagSet := cmd.Flags()
|
||||
flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t))
|
||||
flagSet.Set("run-as", myWellFundedAccount)
|
||||
},
|
||||
expMsgCount: 1,
|
||||
},
|
||||
|
@ -295,7 +322,7 @@ func TestExecuteContractCmd(t *testing.T) {
|
|||
mutator: func(cmd *cobra.Command) {
|
||||
cmd.SetArgs([]string{firstContractAddress, `{}`})
|
||||
flagSet := cmd.Flags()
|
||||
flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t))
|
||||
flagSet.Set("run-as", myWellFundedAccount)
|
||||
},
|
||||
expMsgCount: 2,
|
||||
},
|
||||
|
@ -319,7 +346,7 @@ func TestExecuteContractCmd(t *testing.T) {
|
|||
mutator: func(cmd *cobra.Command) {
|
||||
cmd.SetArgs([]string{"cosmos1weh0k0l6t6v4jkmkde8e90tzkw2c59g42ccl62", `{}`})
|
||||
flagSet := cmd.Flags()
|
||||
flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t))
|
||||
flagSet.Set("run-as", myWellFundedAccount)
|
||||
},
|
||||
expMsgCount: 2,
|
||||
},
|
||||
|
@ -328,6 +355,33 @@ func TestExecuteContractCmd(t *testing.T) {
|
|||
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,
|
||||
|
@ -336,10 +390,10 @@ func TestExecuteContractCmd(t *testing.T) {
|
|||
for msg, spec := range specs {
|
||||
t.Run(msg, func(t *testing.T) {
|
||||
homeDir := setupGenesis(t, spec.srcGenesis)
|
||||
cmd := GenesisExecuteContractCmd(homeDir)
|
||||
spec.mutator(cmd)
|
||||
|
||||
// when
|
||||
cmd := cli.GenesisExecuteContractCmd(homeDir)
|
||||
spec.mutator(cmd)
|
||||
err := executeCmdWithContext(t, homeDir, cmd)
|
||||
if spec.expError {
|
||||
require.Error(t, err)
|
||||
|
@ -352,6 +406,104 @@ func TestExecuteContractCmd(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
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
|
||||
|
@ -362,6 +514,15 @@ func setupGenesis(t *testing.T, wasmGenesis types.GenesisState) string {
|
|||
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{
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
|
@ -26,6 +27,7 @@ import (
|
|||
"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"
|
||||
)
|
||||
|
@ -97,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, StakingKeeperMock{}, TestHandler(dstKeeper))
|
||||
InitGenesis(dstCtx, dstKeeper, importState, &StakingKeeperMock{}, TestHandler(dstKeeper))
|
||||
|
||||
// compare whole DB
|
||||
for j := range srcStoreKeys {
|
||||
|
@ -127,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{
|
||||
|
@ -356,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, StakingKeeperMock{}, TestHandler(keeper))
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -434,7 +470,7 @@ func TestImportContractWithCodeHistoryReset(t *testing.T) {
|
|||
ctx = ctx.WithBlockHeight(0).WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
|
||||
// when
|
||||
_, err = InitGenesis(ctx, keeper, importState, StakingKeeperMock{}, TestHandler(keeper))
|
||||
_, err = InitGenesis(ctx, keeper, importState, &StakingKeeperMock{}, TestHandler(keeper))
|
||||
require.NoError(t, err)
|
||||
|
||||
// verify wasm code
|
||||
|
@ -508,7 +544,7 @@ func TestImportWithGenMsg(t *testing.T) {
|
|||
ctx = ctx.WithBlockHeight(0).WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
|
||||
// when
|
||||
_, err = InitGenesis(ctx, keeper, importState, StakingKeeperMock{}, TestHandler(keeper))
|
||||
_, err = InitGenesis(ctx, keeper, importState, &StakingKeeperMock{}, TestHandler(keeper))
|
||||
require.NoError(t, err)
|
||||
|
||||
// verify wasm code
|
||||
|
@ -553,8 +589,35 @@ func setupKeeper(t *testing.T) (*Keeper, sdk.Context, []sdk.StoreKey) {
|
|||
type StakingKeeperMock struct {
|
||||
err error
|
||||
validatorUpdate []abci.ValidatorUpdate
|
||||
expCalls int
|
||||
gotCalls int
|
||||
}
|
||||
|
||||
func (s StakingKeeperMock) ApplyAndReturnValidatorSetUpdates(_ sdk.Context) ([]abci.ValidatorUpdate, error) {
|
||||
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")
|
||||
}
|
||||
|
|
|
@ -628,6 +628,8 @@ 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.
|
||||
|
@ -635,6 +637,8 @@ func contractAddress(codeID, instanceID uint64) sdk.AccAddress {
|
|||
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)
|
||||
|
|
Loading…
Reference in New Issue