Polish gen msg code and tests

This commit is contained in:
Alex Peters 2020-12-23 12:04:44 +01:00
parent 5882bf36cd
commit 83d22a9352
No known key found for this signature in database
GPG Key ID: 55CCC24B5703B7D6
5 changed files with 415 additions and 136 deletions

View File

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

View File

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

View File

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

View File

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

View File

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