diff --git a/app/test_helpers.go b/app/test_helpers.go index ccf49656a6..e37db12789 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -71,6 +71,29 @@ func setup(t testing.TB, withGenesis bool, invCheckPeriod uint, opts ...wasm.Opt return app, GenesisState{} } +// Setup initializes a new WasmApp with DefaultNodeHome for integration tests +func Setup(isCheckTx bool, opts ...wasm.Option) *WasmApp { + db := dbm.NewMemDB() + app := NewWasmApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 5, MakeEncodingConfig(), wasm.EnableAllProposals, EmptyBaseAppOptions{}, opts) + + if !isCheckTx { + genesisState := NewDefaultGenesisState() + stateBytes, err := json.MarshalIndent(genesisState, "", " ") + if err != nil { + panic(err) + } + + app.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: DefaultConsensusParams, + AppStateBytes: stateBytes, + }, + ) + } + return app +} + // SetupWithGenesisValSet initializes a new WasmApp with a validator set and genesis accounts // that also act as delegators. For simplicity, each validator is bonded with a delegation // of one consensus engine unit (10^6) in the default token of the WasmApp from first genesis diff --git a/docs/proto/proto-docs.md b/docs/proto/proto-docs.md index e62564dc73..584398a709 100644 --- a/docs/proto/proto-docs.md +++ b/docs/proto/proto-docs.md @@ -430,6 +430,7 @@ MsgStoreCodeResponse returns store result data. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `code_id` | [uint64](#uint64) | | CodeID is the reference to the stored WASM code | +| `checksum` | [bytes](#bytes) | | Checksum is the sha256 hash of the stored code | diff --git a/proto/cosmwasm/wasm/v1/tx.proto b/proto/cosmwasm/wasm/v1/tx.proto index 8295907eb8..f1eb8fec68 100644 --- a/proto/cosmwasm/wasm/v1/tx.proto +++ b/proto/cosmwasm/wasm/v1/tx.proto @@ -41,6 +41,8 @@ message MsgStoreCode { message MsgStoreCodeResponse { // CodeID is the reference to the stored WASM code uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ]; + // Checksum is the sha256 hash of the stored code + bytes checksum = 2; } // MsgInstantiateContract create a new smart contract instance for the given diff --git a/x/wasm/alias.go b/x/wasm/alias.go index 67a79ac541..09f0e7af00 100644 --- a/x/wasm/alias.go +++ b/x/wasm/alias.go @@ -86,7 +86,6 @@ var ( ErrQueryFailed = types.ErrQueryFailed ErrInvalidMsg = types.ErrInvalidMsg KeyLastCodeID = types.KeyLastCodeID - KeyLastInstanceID = types.KeyLastInstanceID CodeKeyPrefix = types.CodeKeyPrefix ContractKeyPrefix = types.ContractKeyPrefix ContractStorePrefix = types.ContractStorePrefix diff --git a/x/wasm/client/cli/genesis_msg.go b/x/wasm/client/cli/genesis_msg.go index 7ed5a7f290..d7936e0145 100644 --- a/x/wasm/client/cli/genesis_msg.go +++ b/x/wasm/client/cli/genesis_msg.go @@ -103,7 +103,7 @@ func GenesisInstantiateContractCmd(defaultNodeHome string, genesisMutator Genesi } return genesisMutator.AlterWasmModuleState(cmd, func(state *types.GenesisState, appState map[string]json.RawMessage) error { - // simple sanity check that sender has some balance although it may be consumed by appState previous message already + // simple sanity check that sender has some balance, although it may be consumed by appState previous message already switch ok, err := hasAccountBalance(cmd, appState, senderAddr, msg.Funds); { case err != nil: return err @@ -112,7 +112,7 @@ func GenesisInstantiateContractCmd(defaultNodeHome string, genesisMutator Genesi } // does code id exists? - codeInfos, err := GetAllCodes(state) + codeInfos := GetAllCodes(state) if err != nil { return err } @@ -171,7 +171,7 @@ func GenesisExecuteContractCmd(defaultNodeHome string, genesisMutator GenesisMut } return genesisMutator.AlterWasmModuleState(cmd, func(state *types.GenesisState, appState map[string]json.RawMessage) error { - // simple sanity check that sender has some balance although it may be consumed by appState previous message already + // simple sanity check that sender has some balance, although it may be consumed by appState previous message already switch ok, err := hasAccountBalance(cmd, appState, senderAddr, msg.Funds); { case err != nil: return err @@ -211,7 +211,7 @@ func GenesisListCodesCmd(defaultNodeHome string, genReader GenesisReader) *cobra if err != nil { return err } - all, err := GetAllCodes(g.WasmModuleState) + all := GetAllCodes(g.WasmModuleState) if err != nil { return err } @@ -236,7 +236,10 @@ func GenesisListContractsCmd(defaultNodeHome string, genReader GenesisReader) *c return err } state := g.WasmModuleState - all := GetAllContracts(state) + all, err := GetAllContracts(state) + if err != nil { + return err + } return printJSONOutput(cmd, all) }, } @@ -245,7 +248,7 @@ func GenesisListContractsCmd(defaultNodeHome string, genReader GenesisReader) *c return cmd } -// clientCtx marshaller works only with proto or bytes so we marshal the output ourself +// clientCtx marshaller works only with proto or bytes, so we marshal the output ourselves func printJSONOutput(cmd *cobra.Command, obj interface{}) error { clientCtx := client.GetClientContextFromCmd(cmd) bz, err := json.MarshalIndent(obj, "", " ") @@ -260,7 +263,7 @@ type CodeMeta struct { Info types.CodeInfo `json:"info"` } -func GetAllCodes(state *types.GenesisState) ([]CodeMeta, error) { +func GetAllCodes(state *types.GenesisState) []CodeMeta { all := make([]CodeMeta, len(state.Codes)) for i, c := range state.Codes { all[i] = CodeMeta{ @@ -277,10 +280,7 @@ func GetAllCodes(state *types.GenesisState) ([]CodeMeta, error) { accessConfig = *msg.InstantiatePermission } else { // default - creator, err := sdk.AccAddressFromBech32(msg.Sender) - if err != nil { - return nil, fmt.Errorf("sender: %s", err) - } + creator := sdk.MustAccAddressFromBech32(msg.Sender) accessConfig = state.Params.InstantiateDefaultPermission.With(creator) } hash := sha256.Sum256(msg.WASMByteCode) @@ -295,7 +295,7 @@ func GetAllCodes(state *types.GenesisState) ([]CodeMeta, error) { seq++ } } - return all, nil + return all } type ContractMeta struct { @@ -303,7 +303,18 @@ type ContractMeta struct { Info types.ContractInfo `json:"info"` } -func GetAllContracts(state *types.GenesisState) []ContractMeta { +// returns nil when not found +func codeHashByID(state *types.GenesisState, codeID uint64) []byte { + codes := GetAllCodes(state) + for _, v := range codes { + if v.CodeID == codeID { + return v.Info.CodeHash + } + } + return nil +} + +func GetAllContracts(state *types.GenesisState) ([]ContractMeta, error) { all := make([]ContractMeta, len(state.Contracts)) for i, c := range state.Contracts { all[i] = ContractMeta{ @@ -312,11 +323,18 @@ func GetAllContracts(state *types.GenesisState) []ContractMeta { } } // add inflight - seq := contractSeqValue(state) for _, m := range state.GenMsgs { if msg := m.GetInstantiateContract(); msg != nil { + senderAddr, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(fmt.Sprintf("unsupported address %q: %s", msg.Sender, err)) + } + codeHash := codeHashByID(state, msg.CodeID) + if codeHash == nil { + return nil, types.ErrNotFound.Wrapf("hash for code-id: %d", msg.CodeID) + } all = append(all, ContractMeta{ - ContractAddress: keeper.BuildContractAddress(msg.CodeID, seq).String(), + ContractAddress: keeper.BuildContractAddress(codeHash, senderAddr, msg.Label).String(), Info: types.ContractInfo{ CodeID: msg.CodeID, Creator: msg.Sender, @@ -324,10 +342,9 @@ func GetAllContracts(state *types.GenesisState) []ContractMeta { Label: msg.Label, }, }) - seq++ } } - return all + return all, nil } func hasAccountBalance(cmd *cobra.Command, appState map[string]json.RawMessage, sender sdk.AccAddress, coins sdk.Coins) (bool, error) { @@ -354,13 +371,19 @@ func hasContract(state *types.GenesisState, contractAddr string) bool { return true } } - seq := contractSeqValue(state) for _, m := range state.GenMsgs { if msg := m.GetInstantiateContract(); msg != nil { - if keeper.BuildContractAddress(msg.CodeID, seq).String() == contractAddr { + senderAddr, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(fmt.Sprintf("unsupported address %q: %s", msg.Sender, err)) + } + hash := codeHashByID(state, msg.CodeID) + if hash == nil { + panic(fmt.Sprintf("unknown code id: %d", msg.CodeID)) + } + if keeper.BuildContractAddress(hash, senderAddr, msg.Label).String() == contractAddr { return true } - seq++ } } return false @@ -453,19 +476,6 @@ func (x DefaultGenesisIO) AlterWasmModuleState(cmd *cobra.Command, callback func 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 { diff --git a/x/wasm/client/cli/genesis_msg_test.go b/x/wasm/client/cli/genesis_msg_test.go index 78a1c53b58..6b67aa4942 100644 --- a/x/wasm/client/cli/genesis_msg_test.go +++ b/x/wasm/client/cli/genesis_msg_test.go @@ -1,12 +1,15 @@ package cli import ( + "bytes" "context" "encoding/json" "os" "path" "testing" + "github.com/cosmos/cosmos-sdk/types/address" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/crypto/hd" @@ -363,7 +366,8 @@ func TestInstantiateContractCmd(t *testing.T) { } func TestExecuteContractCmd(t *testing.T) { - const firstContractAddress = "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr" + mySenderAddr := sdk.AccAddress(bytes.Repeat([]byte{1}, address.Len)) + myFirstContractAddress := keeper.BuildContractAddress([]byte("myCodeHash"), mySenderAddr, "my").String() minimalWasmGenesis := types.GenesisState{ Params: types.DefaultParams(), } @@ -390,7 +394,7 @@ func TestExecuteContractCmd(t *testing.T) { }, Contracts: []types.Contract{ { - ContractAddress: firstContractAddress, + ContractAddress: myFirstContractAddress, ContractInfo: types.ContractInfoFixture(func(info *types.ContractInfo) { info.Created = nil }), @@ -399,53 +403,34 @@ func TestExecuteContractCmd(t *testing.T) { }, }, mutator: func(cmd *cobra.Command) { - cmd.SetArgs([]string{firstContractAddress, `{}`}) + cmd.SetArgs([]string{myFirstContractAddress, `{}`}) 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": { + "all good with contract from genesis store messages": { srcGenesis: types.GenesisState{ Params: types.DefaultParams(), Codes: []types.Code{ { - CodeID: 1, - CodeInfo: types.CodeInfoFixture(), + CodeID: 1, + CodeInfo: types.CodeInfoFixture(func(info *types.CodeInfo) { + info.CodeHash = []byte("myCodeHash") + }), CodeBytes: wasmIdent, }, }, GenMsgs: []types.GenesisState_GenMsgs{ - {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: types.MsgInstantiateContractFixture()}}, - }, - Sequences: []types.Sequence{ - {IDKey: types.KeyLastInstanceID, Value: 100}, + {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: types.MsgInstantiateContractFixture( + func(m *types.MsgInstantiateContract) { + m.Sender = mySenderAddr.String() + m.Label = "my" + })}}, }, }, mutator: func(cmd *cobra.Command) { - // See TestBuildContractAddress in keeper_test.go - cmd.SetArgs([]string{"cosmos1mujpjkwhut9yjw4xueyugc02evfv46y0dtmnz4lh8xxkkdapym9stu5qm8", `{}`}) + cmd.SetArgs([]string{myFirstContractAddress, `{}`}) flagSet := cmd.Flags() flagSet.Set("run-as", myWellFundedAccount) }, @@ -472,7 +457,7 @@ func TestExecuteContractCmd(t *testing.T) { }, Contracts: []types.Contract{ { - ContractAddress: firstContractAddress, + ContractAddress: myFirstContractAddress, ContractInfo: types.ContractInfoFixture(func(info *types.ContractInfo) { info.Created = nil }), @@ -481,7 +466,7 @@ func TestExecuteContractCmd(t *testing.T) { }, }, mutator: func(cmd *cobra.Command) { - cmd.SetArgs([]string{firstContractAddress, `{}`}) + cmd.SetArgs([]string{myFirstContractAddress, `{}`}) flagSet := cmd.Flags() flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t)) }, @@ -499,7 +484,7 @@ func TestExecuteContractCmd(t *testing.T) { }, Contracts: []types.Contract{ { - ContractAddress: firstContractAddress, + ContractAddress: myFirstContractAddress, ContractInfo: types.ContractInfoFixture(func(info *types.ContractInfo) { info.Created = nil }), @@ -508,7 +493,7 @@ func TestExecuteContractCmd(t *testing.T) { }, }, mutator: func(cmd *cobra.Command) { - cmd.SetArgs([]string{firstContractAddress, `{}`}) + cmd.SetArgs([]string{myFirstContractAddress, `{}`}) flagSet := cmd.Flags() flagSet.Set("run-as", myWellFundedAccount) flagSet.Set("amount", "100stake") @@ -527,7 +512,7 @@ func TestExecuteContractCmd(t *testing.T) { }, Contracts: []types.Contract{ { - ContractAddress: firstContractAddress, + ContractAddress: myFirstContractAddress, ContractInfo: types.ContractInfoFixture(func(info *types.ContractInfo) { info.Created = nil }), @@ -536,7 +521,7 @@ func TestExecuteContractCmd(t *testing.T) { }, }, mutator: func(cmd *cobra.Command) { - cmd.SetArgs([]string{firstContractAddress, `{}`}) + cmd.SetArgs([]string{myFirstContractAddress, `{}`}) flagSet := cmd.Flags() flagSet.Set("run-as", keeper.RandomBech32AccountAddress(t)) flagSet.Set("amount", "10stake") @@ -565,6 +550,9 @@ func TestExecuteContractCmd(t *testing.T) { } func TestGetAllContracts(t *testing.T) { + creatorAddr1 := sdk.AccAddress(bytes.Repeat([]byte{1}, address.Len)) + creatorAddr2 := sdk.AccAddress(bytes.Repeat([]byte{2}, address.Len)) + specs := map[string]struct { src types.GenesisState exp []ContractMeta @@ -595,68 +583,55 @@ func TestGetAllContracts(t *testing.T) { }, "read from message state": { src: types.GenesisState{ + Codes: []types.Code{{CodeID: 1, CodeInfo: types.CodeInfo{CodeHash: []byte("firstCodeHash")}}}, GenMsgs: []types.GenesisState_GenMsgs{ - {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &types.MsgInstantiateContract{Label: "first"}}}, - {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &types.MsgInstantiateContract{Label: "second"}}}, + {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &types.MsgInstantiateContract{Sender: creatorAddr1.String(), Label: "first", CodeID: 1}}}, + {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &types.MsgInstantiateContract{Sender: creatorAddr2.String(), Label: "second", CodeID: 1}}}, }, }, exp: []ContractMeta{ { - ContractAddress: keeper.BuildContractAddress(0, 1).String(), - Info: types.ContractInfo{Label: "first"}, - }, - { - ContractAddress: keeper.BuildContractAddress(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"}}}, + ContractAddress: keeper.BuildContractAddress([]byte("firstCodeHash"), creatorAddr1, "first").String(), + Info: types.ContractInfo{Creator: creatorAddr1.String(), Label: "first", CodeID: 1}, }, - }, - exp: []ContractMeta{ { - ContractAddress: keeper.BuildContractAddress(0, 100).String(), - Info: types.ContractInfo{Label: "hundred"}, + ContractAddress: keeper.BuildContractAddress([]byte("firstCodeHash"), creatorAddr2, "second").String(), + Info: types.ContractInfo{Creator: creatorAddr2.String(), Label: "second", CodeID: 1}, }, }, }, "read from contract and message state with contract sequence": { src: types.GenesisState{ + Codes: []types.Code{ + {CodeID: 1, CodeInfo: types.CodeInfo{CodeHash: []byte("firstCodeHash")}}, + {CodeID: 100, CodeInfo: types.CodeInfo{CodeHash: []byte("otherCodeHash")}}, + }, Contracts: []types.Contract{ { - ContractAddress: "first-contract", - ContractInfo: types.ContractInfo{Label: "first"}, + ContractAddress: keeper.BuildContractAddress([]byte("firstCodeHash"), creatorAddr1, "first").String(), + ContractInfo: types.ContractInfo{Label: "first", CodeID: 1}, }, }, - Sequences: []types.Sequence{ - {IDKey: types.KeyLastInstanceID, Value: 100}, - }, GenMsgs: []types.GenesisState_GenMsgs{ - {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &types.MsgInstantiateContract{Label: "hundred"}}}, + {Sum: &types.GenesisState_GenMsgs_InstantiateContract{InstantiateContract: &types.MsgInstantiateContract{Sender: creatorAddr1.String(), Label: "hundred", CodeID: 100}}}, }, }, exp: []ContractMeta{ { - ContractAddress: "first-contract", - Info: types.ContractInfo{Label: "first"}, + ContractAddress: keeper.BuildContractAddress([]byte("firstCodeHash"), creatorAddr1, "first").String(), + Info: types.ContractInfo{Label: "first", CodeID: 1}, }, { - ContractAddress: keeper.BuildContractAddress(0, 100).String(), - Info: types.ContractInfo{Label: "hundred"}, + ContractAddress: keeper.BuildContractAddress([]byte("otherCodeHash"), creatorAddr1, "hundred").String(), + Info: types.ContractInfo{Creator: creatorAddr1.String(), Label: "hundred", CodeID: 100}, }, }, }, } for msg, spec := range specs { t.Run(msg, func(t *testing.T) { - got := GetAllContracts(&spec.src) + got, err := GetAllContracts(&spec.src) + require.NoError(t, err) assert.Equal(t, spec.exp, got) }) } diff --git a/x/wasm/client/cli/query.go b/x/wasm/client/cli/query.go index 5114d56832..f399bfd7d6 100644 --- a/x/wasm/client/cli/query.go +++ b/x/wasm/client/cli/query.go @@ -17,6 +17,7 @@ import ( "github.com/spf13/cobra" flag "github.com/spf13/pflag" + "github.com/CosmWasm/wasmd/x/wasm/keeper" "github.com/CosmWasm/wasmd/x/wasm/types" ) @@ -39,6 +40,7 @@ func GetQueryCmd() *cobra.Command { GetCmdListPinnedCode(), GetCmdLibVersion(), GetCmdQueryParams(), + GetCmdBuildAddress(), ) return queryCmd } @@ -63,6 +65,33 @@ func GetCmdLibVersion() *cobra.Command { return cmd } +// GetCmdBuildAddress build a contract address +func GetCmdBuildAddress() *cobra.Command { + cmd := &cobra.Command{ + Use: "build-address [code-hash] [creator-address] [label]", + Short: "build contract address", + Aliases: []string{"address"}, + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + codeHash, err := hex.DecodeString(args[0]) + if err != nil { + return fmt.Errorf("code-hash: %s", err) + } + creator, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return fmt.Errorf("creator: %s", err) + } + label := args[2] + if err := types.ValidateLabel(label); err != nil { + return fmt.Errorf("label: %s", err) + } + cmd.Println(keeper.BuildContractAddress(codeHash, creator, label).String()) + return nil + }, + } + return cmd +} + // GetCmdListCode lists all wasm code uploaded func GetCmdListCode() *cobra.Command { cmd := &cobra.Command{ @@ -117,6 +146,9 @@ func GetCmdListContractByCode() *cobra.Command { if err != nil { return err } + if codeID == 0 { + return errors.New("empty code id") + } pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags())) if err != nil { diff --git a/x/wasm/genesis_test.go b/x/wasm/genesis_test.go index ea97c97f07..9b97213db5 100644 --- a/x/wasm/genesis_test.go +++ b/x/wasm/genesis_test.go @@ -13,8 +13,8 @@ func TestInitGenesis(t *testing.T) { deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) - creator := data.faucet.NewFundedAccount(data.ctx, deposit.Add(deposit...)...) - fred := data.faucet.NewFundedAccount(data.ctx, topUp...) + creator := data.faucet.NewFundedRandomAccount(data.ctx, deposit.Add(deposit...)...) + fred := data.faucet.NewFundedRandomAccount(data.ctx, topUp...) h := data.module.Route().Handler() q := data.module.LegacyQuerierHandler(nil) diff --git a/x/wasm/ibctesting/wasm.go b/x/wasm/ibctesting/wasm.go index 4639688f01..293a9f6926 100644 --- a/x/wasm/ibctesting/wasm.go +++ b/x/wasm/ibctesting/wasm.go @@ -62,6 +62,7 @@ func (chain *TestChain) StoreCode(byteCode []byte) types.MsgStoreCodeResponse { var pInstResp types.MsgStoreCodeResponse require.NoError(chain.t, pInstResp.Unmarshal(protoResult.Data[0].Data)) require.NotEmpty(chain.t, pInstResp.CodeID) + require.NotEmpty(chain.t, pInstResp.Checksum) return pInstResp } diff --git a/x/wasm/keeper/contract_keeper.go b/x/wasm/keeper/contract_keeper.go index 73d925dee1..6e1195dbd1 100644 --- a/x/wasm/keeper/contract_keeper.go +++ b/x/wasm/keeper/contract_keeper.go @@ -10,7 +10,7 @@ var _ types.ContractOpsKeeper = PermissionedKeeper{} // decoratedKeeper contains a subset of the wasm keeper that are already or can be guarded by an authorization policy in the future type decoratedKeeper interface { - create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess *types.AccessConfig, authZ AuthorizationPolicy) (codeID uint64, err error) + create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess *types.AccessConfig, authZ AuthorizationPolicy) (codeID uint64, checksum []byte, err error) instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.AccAddress, initMsg []byte, label string, deposit sdk.Coins, authZ AuthorizationPolicy) (sdk.AccAddress, []byte, error) migrate(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, newCodeID uint64, msg []byte, authZ AuthorizationPolicy) ([]byte, error) setContractAdmin(ctx sdk.Context, contractAddress, caller, newAdmin sdk.AccAddress, authZ AuthorizationPolicy) error @@ -39,7 +39,7 @@ func NewDefaultPermissionKeeper(nested decoratedKeeper) *PermissionedKeeper { return NewPermissionedKeeper(nested, DefaultAuthorizationPolicy{}) } -func (p PermissionedKeeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess *types.AccessConfig) (codeID uint64, err error) { +func (p PermissionedKeeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess *types.AccessConfig) (codeID uint64, checksum []byte, err error) { return p.nested.create(ctx, creator, wasmCode, instantiateAccess, p.authZPolicy) } diff --git a/x/wasm/keeper/genesis.go b/x/wasm/keeper/genesis.go index 7fa5280b9e..d81750b708 100644 --- a/x/wasm/keeper/genesis.go +++ b/x/wasm/keeper/genesis.go @@ -35,7 +35,6 @@ func InitGenesis(ctx sdk.Context, keeper *Keeper, data types.GenesisState, staki } } - var maxContractID int for i, contract := range data.Contracts { contractAddr, err := sdk.AccAddressFromBech32(contract.ContractAddress) if err != nil { @@ -45,7 +44,6 @@ func InitGenesis(ctx sdk.Context, keeper *Keeper, data types.GenesisState, staki if err != nil { return nil, sdkerrors.Wrapf(err, "contract number %d", i) } - maxContractID = i + 1 // not ideal but max(contractID) is not persisted otherwise } for i, seq := range data.Sequences { @@ -60,10 +58,6 @@ func InitGenesis(ctx sdk.Context, keeper *Keeper, data types.GenesisState, staki if seqVal <= maxCodeID { return nil, sdkerrors.Wrapf(types.ErrInvalid, "seq %s with value: %d must be greater than: %d ", string(types.KeyLastCodeID), seqVal, maxCodeID) } - seqVal = keeper.PeekAutoIncrementID(ctx, types.KeyLastInstanceID) - if seqVal <= uint64(maxContractID) { - return nil, sdkerrors.Wrapf(types.ErrInvalid, "seq %s with value: %d must be greater than: %d ", string(types.KeyLastInstanceID), seqVal, maxContractID) - } if len(data.GenMsgs) == 0 { return nil, nil @@ -117,7 +111,7 @@ func ExportGenesis(ctx sdk.Context, keeper *Keeper) *types.GenesisState { return false }) - for _, k := range [][]byte{types.KeyLastCodeID, types.KeyLastInstanceID} { + for _, k := range [][]byte{types.KeyLastCodeID} { genState.Sequences = append(genState.Sequences, types.Sequence{ IDKey: k, Value: keeper.PeekAutoIncrementID(ctx, k), diff --git a/x/wasm/keeper/genesis_test.go b/x/wasm/keeper/genesis_test.go index e5410aabb0..4b65f82eaa 100644 --- a/x/wasm/keeper/genesis_test.go +++ b/x/wasm/keeper/genesis_test.go @@ -11,6 +11,10 @@ import ( "testing" "time" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + + "github.com/cosmos/cosmos-sdk/types/address" + "github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" @@ -65,7 +69,7 @@ func TestGenesisExportImport(t *testing.T) { creatorAddr, err := sdk.AccAddressFromBech32(codeInfo.Creator) require.NoError(t, err) - codeID, err := contractKeeper.Create(srcCtx, creatorAddr, wasmCode, &codeInfo.InstantiateConfig) + codeID, checksum, err := contractKeeper.Create(srcCtx, creatorAddr, wasmCode, &codeInfo.InstantiateConfig) require.NoError(t, err) if pinned { contractKeeper.PinCode(srcCtx, codeID) @@ -80,7 +84,7 @@ func TestGenesisExportImport(t *testing.T) { } contract.CodeID = codeID - contractAddr := wasmKeeper.generateContractAddress(srcCtx, codeID) + contractAddr := BuildContractAddress(checksum, creatorAddr, "testing") wasmKeeper.storeContractInfo(srcCtx, contractAddr, &contract) wasmKeeper.appendToContractHistory(srcCtx, contractAddr, history...) wasmKeeper.importContractState(srcCtx, contractAddr, stateModels) @@ -156,6 +160,16 @@ func TestGenesisInit(t *testing.T) { require.NoError(t, err) myCodeInfo := wasmTypes.CodeInfoFixture(wasmTypes.WithSHA256CodeHash(wasmCode)) + mySenderAddr := sdk.AccAddress(bytes.Repeat([]byte{1}, address.Len)) + myLabel := "testing" + myContractInfoFixture := func(mutators ...func(*types.ContractInfo)) types.ContractInfo { + return types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { + c.CodeID = 1 + c.Creator = mySenderAddr.String() + c.Label = myLabel + }, types.OnlyGenesisFields) + } + specs := map[string]struct { src types.GenesisState stakingMock StakingKeeperMock @@ -171,7 +185,6 @@ func TestGenesisInit(t *testing.T) { }}, Sequences: []types.Sequence{ {IDKey: types.KeyLastCodeID, Value: 2}, - {IDKey: types.KeyLastInstanceID, Value: 1}, }, Params: types.DefaultParams(), }, @@ -190,7 +203,6 @@ func TestGenesisInit(t *testing.T) { }}, Sequences: []types.Sequence{ {IDKey: types.KeyLastCodeID, Value: 10}, - {IDKey: types.KeyLastInstanceID, Value: 1}, }, Params: types.DefaultParams(), }, @@ -210,7 +222,6 @@ func TestGenesisInit(t *testing.T) { Contracts: nil, Sequences: []types.Sequence{ {IDKey: types.KeyLastCodeID, Value: 3}, - {IDKey: types.KeyLastInstanceID, Value: 1}, }, Params: types.DefaultParams(), }, @@ -267,13 +278,12 @@ func TestGenesisInit(t *testing.T) { }}, Contracts: []types.Contract{ { - ContractAddress: BuildContractAddress(1, 1).String(), - ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.OnlyGenesisFields), + ContractAddress: BuildContractAddress(myCodeInfo.CodeHash, mySenderAddr, myLabel).String(), + ContractInfo: myContractInfoFixture(), }, }, Sequences: []types.Sequence{ {IDKey: types.KeyLastCodeID, Value: 2}, - {IDKey: types.KeyLastInstanceID, Value: 2}, }, Params: types.DefaultParams(), }, @@ -288,16 +298,17 @@ func TestGenesisInit(t *testing.T) { }}, Contracts: []types.Contract{ { - ContractAddress: BuildContractAddress(1, 1).String(), - ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.OnlyGenesisFields), + ContractAddress: BuildContractAddress(myCodeInfo.CodeHash, mySenderAddr, myLabel).String(), + ContractInfo: myContractInfoFixture(), }, { - ContractAddress: BuildContractAddress(1, 2).String(), - ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.OnlyGenesisFields), + ContractAddress: BuildContractAddress(myCodeInfo.CodeHash, mySenderAddr, "other-label").String(), + ContractInfo: myContractInfoFixture(func(i *wasmTypes.ContractInfo) { + i.Label = "other-label" + }), }, }, Sequences: []types.Sequence{ {IDKey: types.KeyLastCodeID, Value: 2}, - {IDKey: types.KeyLastInstanceID, Value: 3}, }, Params: types.DefaultParams(), }, @@ -307,8 +318,8 @@ func TestGenesisInit(t *testing.T) { src: types.GenesisState{ Contracts: []types.Contract{ { - ContractAddress: BuildContractAddress(1, 1).String(), - ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.OnlyGenesisFields), + ContractAddress: BuildContractAddress(myCodeInfo.CodeHash, mySenderAddr, myLabel).String(), + ContractInfo: myContractInfoFixture(), }, }, Params: types.DefaultParams(), @@ -323,11 +334,11 @@ func TestGenesisInit(t *testing.T) { }}, Contracts: []types.Contract{ { - ContractAddress: BuildContractAddress(1, 1).String(), - ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.OnlyGenesisFields), + ContractAddress: BuildContractAddress(myCodeInfo.CodeHash, mySenderAddr, myLabel).String(), + ContractInfo: myContractInfoFixture(), }, { - ContractAddress: BuildContractAddress(1, 1).String(), - ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.OnlyGenesisFields), + ContractAddress: BuildContractAddress(myCodeInfo.CodeHash, mySenderAddr, myLabel).String(), + ContractInfo: myContractInfoFixture(), }, }, Params: types.DefaultParams(), @@ -342,8 +353,8 @@ func TestGenesisInit(t *testing.T) { }}, Contracts: []types.Contract{ { - ContractAddress: BuildContractAddress(1, 1).String(), - ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.OnlyGenesisFields), + ContractAddress: BuildContractAddress(myCodeInfo.CodeHash, mySenderAddr, myLabel).String(), + ContractInfo: myContractInfoFixture(), ContractState: []types.Model{ { Key: []byte{0x1}, @@ -381,26 +392,6 @@ func TestGenesisInit(t *testing.T) { Params: types.DefaultParams(), }, }, - "prevent contract id seq init value == count contracts": { - src: types.GenesisState{ - Codes: []types.Code{{ - CodeID: firstCodeID, - CodeInfo: myCodeInfo, - CodeBytes: wasmCode, - }}, - Contracts: []types.Contract{ - { - ContractAddress: BuildContractAddress(1, 1).String(), - ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }, types.OnlyGenesisFields), - }, - }, - Sequences: []types.Sequence{ - {IDKey: types.KeyLastCodeID, Value: 2}, - {IDKey: types.KeyLastInstanceID, Value: 1}, - }, - Params: types.DefaultParams(), - }, - }, "validator set update called for any genesis messages": { src: wasmTypes.GenesisState{ GenMsgs: []types.GenesisState_GenMsgs{ @@ -559,16 +550,17 @@ func TestImportContractWithCodeHistoryReset(t *testing.T) { } assert.Equal(t, expHistory, keeper.GetContractHistory(ctx, contractAddr)) assert.Equal(t, uint64(2), keeper.PeekAutoIncrementID(ctx, types.KeyLastCodeID)) - assert.Equal(t, uint64(3), keeper.PeekAutoIncrementID(ctx, types.KeyLastInstanceID)) } func TestSupportedGenMsgTypes(t *testing.T) { wasmCode, err := os.ReadFile("./testdata/hackatom.wasm") require.NoError(t, err) + wasmHash := sha256.Sum256(wasmCode) var ( myAddress sdk.AccAddress = bytes.Repeat([]byte{1}, types.ContractAddrLen) verifierAddress sdk.AccAddress = bytes.Repeat([]byte{2}, types.ContractAddrLen) beneficiaryAddress sdk.AccAddress = bytes.Repeat([]byte{3}, types.ContractAddrLen) + contractAddr = BuildContractAddress(wasmHash[:], myAddress, "testing") ) const denom = "stake" importState := types.GenesisState{ @@ -600,7 +592,7 @@ func TestSupportedGenMsgTypes(t *testing.T) { Sum: &types.GenesisState_GenMsgs_ExecuteContract{ ExecuteContract: &types.MsgExecuteContract{ Sender: verifierAddress.String(), - Contract: BuildContractAddress(1, 1).String(), + Contract: contractAddr.String(), Msg: []byte(`{"release":{}}`), }, }, @@ -625,7 +617,7 @@ func TestSupportedGenMsgTypes(t *testing.T) { require.NotNil(t, codeInfo) // verify contract instantiated - cInfo := keeper.GetContractInfo(ctx, BuildContractAddress(1, 1)) + cInfo := keeper.GetContractInfo(ctx, contractAddr) require.NotNil(t, cInfo) // verify contract executed @@ -668,7 +660,24 @@ func setupKeeper(t *testing.T) (*Keeper, sdk.Context, []sdk.StoreKey) { wasmConfig := wasmTypes.DefaultWasmConfig() pk := paramskeeper.NewKeeper(encodingConfig.Marshaler, encodingConfig.Amino, keyParams, tkeyParams) - srcKeeper := NewKeeper(encodingConfig.Marshaler, keyWasm, pk.Subspace(wasmTypes.ModuleName), authkeeper.AccountKeeper{}, nil, stakingkeeper.Keeper{}, distributionkeeper.Keeper{}, nil, nil, nil, nil, nil, nil, tempDir, wasmConfig, AvailableCapabilities) + srcKeeper := NewKeeper( + encodingConfig.Marshaler, + keyWasm, + pk.Subspace(wasmTypes.ModuleName), + authkeeper.AccountKeeper{}, + &bankkeeper.BaseKeeper{}, + stakingkeeper.Keeper{}, + distributionkeeper.Keeper{}, + nil, + nil, + nil, + nil, + nil, + nil, + tempDir, + wasmConfig, + AvailableCapabilities, + ) return &srcKeeper, ctx, []sdk.StoreKey{keyWasm, keyParams} } diff --git a/x/wasm/keeper/handler_plugin_test.go b/x/wasm/keeper/handler_plugin_test.go index 4c60bc550f..557478fb44 100644 --- a/x/wasm/keeper/handler_plugin_test.go +++ b/x/wasm/keeper/handler_plugin_test.go @@ -327,7 +327,7 @@ func TestBurnCoinMessageHandlerIntegration(t *testing.T) { // picks the message in the default handler chain ctx, keepers := CreateDefaultTestInput(t) // set some supply - keepers.Faucet.NewFundedAccount(ctx, sdk.NewCoin("denom", sdk.NewInt(10_000_000))) + keepers.Faucet.NewFundedRandomAccount(ctx, sdk.NewCoin("denom", sdk.NewInt(10_000_000))) k := keepers.WasmKeeper example := InstantiateHackatomExampleContract(t, ctx, keepers) // with deposit of 100 stake diff --git a/x/wasm/keeper/ibc_test.go b/x/wasm/keeper/ibc_test.go index ef04b583cb..bdbab1a37c 100644 --- a/x/wasm/keeper/ibc_test.go +++ b/x/wasm/keeper/ibc_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + "github.com/tendermint/tendermint/libs/rand" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" @@ -41,7 +43,7 @@ func TestBindingPortForIBCContractOnInstantiate(t *testing.T) { } func TestContractFromPortID(t *testing.T) { - contractAddr := BuildContractAddress(1, 100) + contractAddr := BuildContractAddress(rand.Bytes(32), RandomAccountAddress(t), "testing") specs := map[string]struct { srcPort string expAddr sdk.AccAddress diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index 6e250e9f63..7426ade1ad 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -4,22 +4,25 @@ import ( "bytes" "context" "encoding/binary" + "encoding/hex" "fmt" "math" "path/filepath" + "reflect" "strconv" "strings" "time" - "github.com/cosmos/cosmos-sdk/types/address" - wasmvm "github.com/CosmWasm/wasmvm" wasmvmtypes "github.com/CosmWasm/wasmvm/types" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/tendermint/tendermint/libs/log" @@ -54,6 +57,13 @@ type CoinTransferrer interface { TransferCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error } +// CoinPruner handles the balances for accounts that are pruned on contract instantiate. +// This is an extension point to attach custom logic +type CoinPruner interface { + // PruneBalances handle balances for given address + PruneBalances(ctx sdk.Context, contractAddress sdk.AccAddress) error +} + // WasmVMResponseHandler is an extension point to handles the response data returned by a contract call. type WasmVMResponseHandler interface { // Handle processes the data returned by a contract invocation. @@ -66,6 +76,25 @@ type WasmVMResponseHandler interface { ) ([]byte, error) } +// list of account types that are accepted for wasm contracts. Chains importing wasmd +// can overwrite this list with the WithAcceptedAccountTypesOnContractInstantiation option. +var defaultAcceptedAccountTypes = map[reflect.Type]struct{}{ + reflect.TypeOf(&authtypes.BaseAccount{}): {}, +} + +// list of account types that are replaced with base accounts. Chains importing wasmd +// can overwrite this list with the WithPruneAccountTypesOnContractInstantiation option. +// +// contains vesting account types that can be created post genesis +var defaultPruneAccountTypes = map[reflect.Type]struct{}{ + reflect.TypeOf(&vestingtypes.DelayedVestingAccount{}): {}, + reflect.TypeOf(&vestingtypes.ContinuousVestingAccount{}): {}, + // intentionally not added: genesis account types + // reflect.TypeOf(&vestingtypes.BaseVestingAccount{}): {}, + // reflect.TypeOf(&vestingtypes.PeriodicVestingAccount{}): {}, + // reflect.TypeOf(&vestingtypes.PermanentLockedAccount{}): {}, +} + // Keeper will have a reference to Wasmer with it's own data directory. type Keeper struct { storeKey sdk.StoreKey @@ -79,10 +108,13 @@ type Keeper struct { wasmVMResponseHandler WasmVMResponseHandler messenger Messenger // queryGasLimit is the max wasmvm gas that can be spent on executing a query with a contract - queryGasLimit uint64 - paramSpace paramtypes.Subspace - gasRegister GasRegister - maxQueryStackSize uint32 + queryGasLimit uint64 + paramSpace paramtypes.Subspace + gasRegister GasRegister + maxQueryStackSize uint32 + acceptedAccountTypes map[reflect.Type]struct{} + pruneAccountTypes map[reflect.Type]struct{} + coinPruner CoinPruner } // NewKeeper creates a new contract Keeper instance @@ -116,18 +148,21 @@ func NewKeeper( } keeper := &Keeper{ - storeKey: storeKey, - cdc: cdc, - wasmVM: wasmer, - accountKeeper: accountKeeper, - bank: NewBankCoinTransferrer(bankKeeper), - portKeeper: portKeeper, - capabilityKeeper: capabilityKeeper, - messenger: NewDefaultMessageHandler(router, channelKeeper, capabilityKeeper, bankKeeper, cdc, portSource), - queryGasLimit: wasmConfig.SmartQueryGasLimit, - paramSpace: paramSpace, - gasRegister: NewDefaultWasmGasRegister(), - maxQueryStackSize: types.DefaultMaxQueryStackSize, + storeKey: storeKey, + cdc: cdc, + wasmVM: wasmer, + accountKeeper: accountKeeper, + bank: NewBankCoinTransferrer(bankKeeper), + coinPruner: NewCoinBurner(bankKeeper), + portKeeper: portKeeper, + capabilityKeeper: capabilityKeeper, + messenger: NewDefaultMessageHandler(router, channelKeeper, capabilityKeeper, bankKeeper, cdc, portSource), + queryGasLimit: wasmConfig.SmartQueryGasLimit, + paramSpace: paramSpace, + gasRegister: NewDefaultWasmGasRegister(), + maxQueryStackSize: types.DefaultMaxQueryStackSize, + acceptedAccountTypes: defaultAcceptedAccountTypes, + pruneAccountTypes: defaultPruneAccountTypes, } keeper.wasmVMQueryHandler = DefaultQueryPlugins(bankKeeper, stakingKeeper, distKeeper, channelKeeper, queryRouter, keeper) for _, o := range opts { @@ -161,13 +196,13 @@ func (k Keeper) SetParams(ctx sdk.Context, ps types.Params) { k.paramSpace.SetParamSet(ctx, &ps) } -func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess *types.AccessConfig, authZ AuthorizationPolicy) (codeID uint64, err error) { +func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess *types.AccessConfig, authZ AuthorizationPolicy) (codeID uint64, checksum []byte, err error) { if creator == nil { - return 0, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "cannot be nil") + return 0, checksum, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "cannot be nil") } if !authZ.CanCreateCode(k.getUploadAccessConfig(ctx), creator) { - return 0, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not create code") + return 0, checksum, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not create code") } // figure out proper instantiate access defaultAccessConfig := k.getInstantiateAccessConfig(ctx).With(creator) @@ -175,25 +210,25 @@ func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess = &defaultAccessConfig } else if !instantiateAccess.IsSubset(defaultAccessConfig) { // we enforce this must be subset of default upload access - return 0, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "instantiate access must be subset of default upload access") + return 0, checksum, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "instantiate access must be subset of default upload access") } if ioutils.IsGzip(wasmCode) { ctx.GasMeter().ConsumeGas(k.gasRegister.UncompressCosts(len(wasmCode)), "Uncompress gzip bytecode") wasmCode, err = ioutils.Uncompress(wasmCode, uint64(types.MaxWasmSize)) if err != nil { - return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) + return 0, checksum, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) } } ctx.GasMeter().ConsumeGas(k.gasRegister.CompileCosts(len(wasmCode)), "Compiling wasm bytecode") - checksum, err := k.wasmVM.Create(wasmCode) + checksum, err = k.wasmVM.Create(wasmCode) if err != nil { - return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) + return 0, checksum, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) } report, err := k.wasmVM.AnalyzeCode(checksum) if err != nil { - return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) + return 0, checksum, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) } codeID = k.autoIncrementID(ctx, types.KeyLastCodeID) k.Logger(ctx).Debug("storing new contract", "capabilities", report.RequiredCapabilities, "code_id", codeID) @@ -203,13 +238,14 @@ func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, evt := sdk.NewEvent( types.EventTypeStoreCode, sdk.NewAttribute(types.AttributeKeyCodeID, strconv.FormatUint(codeID, 10)), + sdk.NewAttribute(types.AttributeKeyChecksum, hex.EncodeToString(checksum)), ) for _, f := range strings.Split(report.RequiredCapabilities, ",") { evt.AppendAttributes(sdk.NewAttribute(types.AttributeKeyRequiredCapability, strings.TrimSpace(f))) } ctx.EventManager().EmitEvent(evt) - return codeID, nil + return codeID, checksum, nil } func (k Keeper) storeCodeInfo(ctx sdk.Context, codeID uint64, codeInfo types.CodeInfo) { @@ -244,31 +280,15 @@ func (k Keeper) importCode(ctx sdk.Context, codeID uint64, codeInfo types.CodeIn return nil } -func (k Keeper) instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.AccAddress, initMsg []byte, label string, deposit sdk.Coins, authZ AuthorizationPolicy) (sdk.AccAddress, []byte, error) { +func (k Keeper) instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.AccAddress, initMsg []byte, label string, deposit sdk.Coins, authPolicy AuthorizationPolicy) (sdk.AccAddress, []byte, error) { defer telemetry.MeasureSince(time.Now(), "wasm", "contract", "instantiate") + if creator == nil { + return nil, nil, types.ErrEmpty.Wrap("creator") + } instanceCosts := k.gasRegister.NewContractInstanceCosts(k.IsPinnedCode(ctx, codeID), len(initMsg)) ctx.GasMeter().ConsumeGas(instanceCosts, "Loading CosmWasm module: instantiate") - // create contract address - contractAddress := k.generateContractAddress(ctx, codeID) - existingAcct := k.accountKeeper.GetAccount(ctx, contractAddress) - if existingAcct != nil { - return nil, nil, sdkerrors.Wrap(types.ErrAccountExists, existingAcct.GetAddress().String()) - } - - // deposit initial contract funds - if !deposit.IsZero() { - if err := k.bank.TransferCoins(ctx, creator, contractAddress, deposit); err != nil { - return nil, nil, err - } - } else { - // create an empty account (so we don't have issues later) - // TODO: can we remove this? - contractAccount := k.accountKeeper.NewAccountWithAddress(ctx, contractAddress) - k.accountKeeper.SetAccount(ctx, contractAccount) - } - // get contact info store := ctx.KVStore(k.storeKey) bz := store.Get(types.GetCodeKey(codeID)) @@ -278,10 +298,53 @@ func (k Keeper) instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.A var codeInfo types.CodeInfo k.cdc.MustUnmarshal(bz, &codeInfo) - if !authZ.CanInstantiateContract(codeInfo.InstantiateConfig, creator) { + if !authPolicy.CanInstantiateContract(codeInfo.InstantiateConfig, creator) { return nil, nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not instantiate") } + contractAddress := BuildContractAddress(codeInfo.CodeHash, creator, label) + if k.HasContractInfo(ctx, contractAddress) { + return nil, nil, types.ErrDuplicate.Wrap("instance with this code id, sender and label exists: try a different label") + } + + // check account + // every cosmos module can define custom account types when needed. The cosmos-sdk comes with extension points + // to support this and a set of base and vesting account types that we integrated in our default lists. + // But not all account types of other modules are known or may make sense for contracts, therefore we kept this + // decision logic also very flexible and extendable. We provide new options to overwrite the default settings via WithAcceptedAccountTypesOnContractInstantiation and + // WithPruneAccountTypesOnContractInstantiation as constructor arguments + existingAcct := k.accountKeeper.GetAccount(ctx, contractAddress) + if existingAcct != nil { + if existingAcct.GetSequence() != 0 || existingAcct.GetPubKey() != nil { + return nil, nil, types.ErrAccountExists.Wrap("address is claimed by external account") + } + if _, accept := k.acceptedAccountTypes[reflect.TypeOf(existingAcct)]; accept { + // keep account and balance as it is + k.Logger(ctx).Info("instantiate contract with existing account", "address", contractAddress.String()) + } else if _, clear := k.pruneAccountTypes[reflect.TypeOf(existingAcct)]; clear { + k.Logger(ctx).Info("pruning existing account for contract instantiation", "address", contractAddress.String()) + // consider an account in the wasmd namespace spam and overwrite it. + contractAccount := k.accountKeeper.NewAccountWithAddress(ctx, contractAddress) + k.accountKeeper.SetAccount(ctx, contractAccount) + // also handle balance to not open cases where these accounts are abused and become liquid + if err := k.coinPruner.PruneBalances(ctx, contractAddress); err != nil { + return nil, nil, err + } + } else { // unknown account type + return nil, nil, types.ErrAccountExists.Wrapf("refusing to overwrite special account type:: %T", existingAcct) + } + } else { + // create an empty account (so we don't have issues later) + contractAccount := k.accountKeeper.NewAccountWithAddress(ctx, contractAddress) + k.accountKeeper.SetAccount(ctx, contractAccount) + } + // deposit initial contract funds + if !deposit.IsZero() { + if err := k.bank.TransferCoins(ctx, creator, contractAddress, deposit); err != nil { + return nil, nil, err + } + } + // prepare params for contract instantiate call env := types.NewEnv(ctx, contractAddress) info := types.NewInfo(creator, deposit) @@ -955,18 +1018,19 @@ func (k Keeper) consumeRuntimeGas(ctx sdk.Context, gas uint64) { } } -// generates a contract address from codeID + instanceID -func (k Keeper) generateContractAddress(ctx sdk.Context, codeID uint64) sdk.AccAddress { - instanceID := k.autoIncrementID(ctx, types.KeyLastInstanceID) - return BuildContractAddress(codeID, instanceID) -} - -// BuildContractAddress builds an sdk account address for a contract. -func BuildContractAddress(codeID, instanceID uint64) sdk.AccAddress { - contractID := make([]byte, 16) - binary.BigEndian.PutUint64(contractID[:8], codeID) - binary.BigEndian.PutUint64(contractID[8:], instanceID) - return address.Module(types.ModuleName, contractID)[:types.ContractAddrLen] +// BuildContractAddress generates a contract address for the wasm module with len = types.ContractAddrLen using the +// Cosmos SDK address.Module function. +// Internally a key is built containing (len(checksum) | checksum | len(sender_address) | sender_address | len(label) | label). +// All method parameter values must be valid and not be empty or nil. +func BuildContractAddress(checksum []byte, creator sdk.AccAddress, label string) sdk.AccAddress { + checksum = address.MustLengthPrefix(checksum) + creator = address.MustLengthPrefix(creator) + labelBz := address.MustLengthPrefix([]byte(label)) + key := make([]byte, len(checksum)+len(creator)+len(labelBz)) + copy(key[0:], checksum) + copy(key[len(checksum):], creator) + copy(key[len(checksum)+len(creator):], labelBz) + return address.Module(types.ModuleName, key)[:types.ContractAddrLen] } func (k Keeper) autoIncrementID(ctx sdk.Context, lastIDKey []byte) uint64 { @@ -1097,6 +1161,34 @@ func (c BankCoinTransferrer) TransferCoins(parentCtx sdk.Context, fromAddr sdk.A return nil } +var _ CoinPruner = CoinBurner{} + +// CoinBurner default implementation for CoinPruner to burn the coins +type CoinBurner struct { + bank types.BankKeeper +} + +// NewCoinBurner constructor +func NewCoinBurner(bank types.BankKeeper) CoinBurner { + if bank == nil { + panic("bank keeper must not be nil") + } + return CoinBurner{bank: bank} +} + +// PruneBalances burns all coins owned by the account. +func (b CoinBurner) PruneBalances(ctx sdk.Context, address sdk.AccAddress) error { + if amt := b.bank.GetAllBalances(ctx, address); !amt.IsZero() { + if err := b.bank.SendCoinsFromAccountToModule(ctx, address, types.ModuleName, amt); err != nil { + return sdkerrors.Wrap(err, "prune account balance") + } + if err := b.bank.BurnCoins(ctx, types.ModuleName, amt); err != nil { + return sdkerrors.Wrap(err, "burn account balance") + } + } + return nil +} + type msgDispatcher interface { DispatchSubmessages(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msgs []wasmvmtypes.SubMsg) ([]byte, error) } diff --git a/x/wasm/keeper/keeper_test.go b/x/wasm/keeper/keeper_test.go index 0cde32592f..f00f28799f 100644 --- a/x/wasm/keeper/keeper_test.go +++ b/x/wasm/keeper/keeper_test.go @@ -2,19 +2,24 @@ package keeper import ( "bytes" + _ "embed" + "encoding/hex" "encoding/json" "errors" - "math" "os" + "strings" "testing" "time" wasmvm "github.com/CosmWasm/wasmvm" wasmvmtypes "github.com/CosmWasm/wasmvm/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" stypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" fuzz "github.com/google/gofuzz" @@ -27,15 +32,7 @@ import ( "github.com/CosmWasm/wasmd/x/wasm/types" ) -// When migrated to go 1.16, embed package should be used instead. -func init() { - b, err := os.ReadFile("./testdata/hackatom.wasm") - if err != nil { - panic(err) - } - hackatomWasm = b -} - +//go:embed testdata/hackatom.wasm var hackatomWasm []byte const AvailableCapabilities = "iterator,staking,stargate,cosmwasm_1_1" @@ -50,10 +47,10 @@ func TestCreateSuccess(t *testing.T) { keeper := keepers.ContractKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) em := sdk.NewEventManager() - contractID, err := keeper.Create(ctx.WithEventManager(em), creator, hackatomWasm, nil) + contractID, _, err := keeper.Create(ctx.WithEventManager(em), creator, hackatomWasm, nil) require.NoError(t, err) require.Equal(t, uint64(1), contractID) // and verify content @@ -61,32 +58,33 @@ func TestCreateSuccess(t *testing.T) { require.NoError(t, err) require.Equal(t, hackatomWasm, storedCode) // and events emitted - exp := sdk.Events{sdk.NewEvent("store_code", sdk.NewAttribute("code_id", "1"))} + codeHash := "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5" + exp := sdk.Events{sdk.NewEvent("store_code", sdk.NewAttribute("code_id", "1"), sdk.NewAttribute("code_checksum", codeHash))} assert.Equal(t, exp, em.Events()) } func TestCreateNilCreatorAddress(t *testing.T) { ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) - _, err := keepers.ContractKeeper.Create(ctx, nil, hackatomWasm, nil) + _, _, err := keepers.ContractKeeper.Create(ctx, nil, hackatomWasm, nil) require.Error(t, err, "nil creator is not allowed") } func TestCreateNilWasmCode(t *testing.T) { ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) - _, err := keepers.ContractKeeper.Create(ctx, creator, nil, nil) + _, _, err := keepers.ContractKeeper.Create(ctx, creator, nil, nil) require.Error(t, err, "nil WASM code is not allowed") } func TestCreateInvalidWasmCode(t *testing.T) { ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) - _, err := keepers.ContractKeeper.Create(ctx, creator, []byte("potatoes"), nil) + _, _, err := keepers.ContractKeeper.Create(ctx, creator, []byte("potatoes"), nil) require.Error(t, err, "potatoes are not valid WASM code") } @@ -127,7 +125,7 @@ func TestCreateStoresInstantiatePermission(t *testing.T) { }) fundAccounts(t, ctx, accKeeper, bankKeeper, myAddr, deposit) - codeID, err := keeper.Create(ctx, myAddr, hackatomWasm, nil) + codeID, _, err := keeper.Create(ctx, myAddr, hackatomWasm, nil) require.NoError(t, err) codeInfo := keepers.WasmKeeper.GetCodeInfo(ctx, codeID) @@ -142,8 +140,8 @@ func TestCreateWithParamPermissions(t *testing.T) { keeper := keepers.ContractKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) - otherAddr := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) + otherAddr := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) specs := map[string]struct { srcPermission types.AccessConfig @@ -172,7 +170,7 @@ func TestCreateWithParamPermissions(t *testing.T) { params := types.DefaultParams() params.CodeUploadAccess = spec.srcPermission keepers.WasmKeeper.SetParams(ctx, params) - _, err := keeper.Create(ctx, creator, hackatomWasm, nil) + _, _, err := keeper.Create(ctx, creator, hackatomWasm, nil) require.True(t, spec.expError.Is(err), err) if spec.expError != nil { return @@ -189,8 +187,8 @@ func TestEnforceValidPermissionsOnCreate(t *testing.T) { contractKeeper := keepers.ContractKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) - other := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) + other := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) onlyCreator := types.AccessTypeOnlyAddress.With(creator) onlyOther := types.AccessTypeOnlyAddress.With(other) @@ -249,7 +247,7 @@ func TestEnforceValidPermissionsOnCreate(t *testing.T) { params := types.DefaultParams() params.InstantiateDefaultPermission = spec.defaultPermssion keeper.SetParams(ctx, params) - codeID, err := contractKeeper.Create(ctx, creator, hackatomWasm, spec.requestedPermission) + codeID, _, err := contractKeeper.Create(ctx, creator, hackatomWasm, spec.requestedPermission) require.True(t, spec.expError.Is(err), err) if spec.expError == nil { codeInfo := keeper.GetCodeInfo(ctx, codeID) @@ -264,15 +262,15 @@ func TestCreateDuplicate(t *testing.T) { keeper := keepers.ContractKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) // create one copy - contractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) + contractID, _, err := keeper.Create(ctx, creator, hackatomWasm, nil) require.NoError(t, err) require.Equal(t, uint64(1), contractID) // create second copy - duplicateID, err := keeper.Create(ctx, creator, hackatomWasm, nil) + duplicateID, _, err := keeper.Create(ctx, creator, hackatomWasm, nil) require.NoError(t, err) require.Equal(t, uint64(2), duplicateID) @@ -292,18 +290,18 @@ func TestCreateWithSimulation(t *testing.T) { WithGasMeter(stypes.NewInfiniteGasMeter()) deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) // create this once in simulation mode - contractID, err := keepers.ContractKeeper.Create(ctx, creator, hackatomWasm, nil) + contractID, _, err := keepers.ContractKeeper.Create(ctx, creator, hackatomWasm, nil) require.NoError(t, err) require.Equal(t, uint64(1), contractID) // then try to create it in non-simulation mode (should not fail) ctx, keepers = CreateTestInput(t, false, AvailableCapabilities) ctx = ctx.WithGasMeter(sdk.NewGasMeter(10_000_000)) - creator = keepers.Faucet.NewFundedAccount(ctx, deposit...) - contractID, err = keepers.ContractKeeper.Create(ctx, creator, hackatomWasm, nil) + creator = keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) + contractID, _, err = keepers.ContractKeeper.Create(ctx, creator, hackatomWasm, nil) require.NoError(t, err) require.Equal(t, uint64(1), contractID) @@ -344,12 +342,12 @@ func TestCreateWithGzippedPayload(t *testing.T) { keeper := keepers.ContractKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) wasmCode, err := os.ReadFile("./testdata/hackatom.wasm.gzip") require.NoError(t, err, "reading gzipped WASM code") - contractID, err := keeper.Create(ctx, creator, wasmCode, nil) + contractID, _, err := keeper.Create(ctx, creator, wasmCode, nil) require.NoError(t, err) require.Equal(t, uint64(1), contractID) // and verify content @@ -363,34 +361,30 @@ func TestCreateWithBrokenGzippedPayload(t *testing.T) { keeper := keepers.ContractKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) wasmCode, err := os.ReadFile("./testdata/broken_crc.gzip") require.NoError(t, err, "reading gzipped WASM code") gm := sdk.NewInfiniteGasMeter() - contractID, err := keeper.Create(ctx.WithGasMeter(gm), creator, wasmCode, nil) + codeID, checksum, err := keeper.Create(ctx.WithGasMeter(gm), creator, wasmCode, nil) require.Error(t, err) - assert.Empty(t, contractID) + assert.Empty(t, codeID) + assert.Empty(t, checksum) assert.GreaterOrEqual(t, gm.GasConsumed(), sdk.Gas(121384)) // 809232 * 0.15 (default uncompress costs) = 121384 } func TestInstantiate(t *testing.T) { ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) - keeper := keepers.ContractKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) - - codeID, err := keeper.Create(ctx, creator, hackatomWasm, nil) - require.NoError(t, err) - - _, _, bob := keyPubAddr() - _, _, fred := keyPubAddr() + creator := sdk.AccAddress(bytes.Repeat([]byte{1}, address.Len)) + keepers.Faucet.Fund(ctx, creator, deposit...) + example := StoreHackatomExampleContract(t, ctx, keepers) initMsg := HackatomExampleInitMsg{ - Verifier: fred, - Beneficiary: bob, + Verifier: RandomAccountAddress(t), + Beneficiary: RandomAccountAddress(t), } initMsgBz, err := json.Marshal(initMsg) require.NoError(t, err) @@ -399,25 +393,25 @@ func TestInstantiate(t *testing.T) { em := sdk.NewEventManager() // create with no balance is also legal - gotContractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx.WithEventManager(em), codeID, creator, nil, initMsgBz, "demo contract 1", nil) + gotContractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx.WithEventManager(em), example.CodeID, creator, nil, initMsgBz, "demo contract 1", nil) require.NoError(t, err) - require.Equal(t, "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", gotContractAddr.String()) + require.Equal(t, "cosmos1xaq0tcwz9fsqmtxlpzwjn2zr8gw66ljjr079ltfc5pelepcs7sjsk28n5n", gotContractAddr.String()) gasAfter := ctx.GasMeter().GasConsumed() if types.EnableGasVerification { - require.Equal(t, uint64(0x18db5), gasAfter-gasBefore) + require.Equal(t, uint64(0x187b8), gasAfter-gasBefore) } // ensure it is stored properly info := keepers.WasmKeeper.GetContractInfo(ctx, gotContractAddr) require.NotNil(t, info) assert.Equal(t, creator.String(), info.Creator) - assert.Equal(t, codeID, info.CodeID) + assert.Equal(t, example.CodeID, info.CodeID) assert.Equal(t, "demo contract 1", info.Label) exp := []types.ContractCodeHistoryEntry{{ Operation: types.ContractCodeHistoryOperationTypeInit, - CodeID: codeID, + CodeID: example.CodeID, Updated: types.NewAbsoluteTxPosition(ctx), Msg: initMsgBz, }} @@ -439,12 +433,9 @@ func TestInstantiateWithDeposit(t *testing.T) { fred = bytes.Repeat([]byte{2}, types.SDKAddrLen) deposit = sdk.NewCoins(sdk.NewInt64Coin("denom", 100)) - initMsg = HackatomExampleInitMsg{Verifier: fred, Beneficiary: bob} + initMsg = mustMarshal(t, HackatomExampleInitMsg{Verifier: fred, Beneficiary: bob}) ) - initMsgBz, err := json.Marshal(initMsg) - require.NoError(t, err) - specs := map[string]struct { srcActor sdk.AccAddress expError bool @@ -472,11 +463,11 @@ func TestInstantiateWithDeposit(t *testing.T) { if spec.fundAddr { fundAccounts(t, ctx, accKeeper, bankKeeper, spec.srcActor, sdk.NewCoins(sdk.NewInt64Coin("denom", 200))) } - contractID, err := keeper.Create(ctx, spec.srcActor, hackatomWasm, nil) + contractID, _, err := keeper.Create(ctx, spec.srcActor, hackatomWasm, nil) require.NoError(t, err) // when - addr, _, err := keepers.ContractKeeper.Instantiate(ctx, contractID, spec.srcActor, nil, initMsgBz, "my label", deposit) + addr, _, err := keepers.ContractKeeper.Instantiate(ctx, contractID, spec.srcActor, nil, initMsg, "my label", deposit) // then if spec.expError { require.Error(t, err) @@ -527,6 +518,7 @@ func TestInstantiateWithPermissions(t *testing.T) { srcActor: myAddr, }, "onlyAddress with non matching address": { + srcActor: myAddr, srcPermission: types.AccessTypeOnlyAddress.With(otherAddr), expError: sdkerrors.ErrUnauthorized, }, @@ -537,7 +529,7 @@ func TestInstantiateWithPermissions(t *testing.T) { accKeeper, bankKeeper, keeper := keepers.AccountKeeper, keepers.BankKeeper, keepers.ContractKeeper fundAccounts(t, ctx, accKeeper, bankKeeper, spec.srcActor, deposit) - contractID, err := keeper.Create(ctx, myAddr, hackatomWasm, &spec.srcPermission) + contractID, _, err := keeper.Create(ctx, myAddr, hackatomWasm, &spec.srcPermission) require.NoError(t, err) _, _, err = keepers.ContractKeeper.Instantiate(ctx, contractID, spec.srcActor, nil, initMsgBz, "demo contract 1", nil) @@ -546,11 +538,195 @@ func TestInstantiateWithPermissions(t *testing.T) { } } +func TestInstantiateWithUniqueContractAddress(t *testing.T) { + parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities) + example := InstantiateHackatomExampleContract(t, parentCtx, keepers) + otherExample := InstantiateReflectExampleContract(t, parentCtx, keepers) + initMsg := mustMarshal(t, HackatomExampleInitMsg{Verifier: example.VerifierAddr, Beneficiary: example.BeneficiaryAddr}) + + otherAddress := DeterministicAccountAddress(t, 1) + keepers.Faucet.Fund(parentCtx, otherAddress, sdk.NewInt64Coin("denom", 100000000)) + used := make(map[string]struct{}) + specs := map[string]struct { + codeID uint64 + sender sdk.AccAddress + label string + initMsg json.RawMessage + expErr error + }{ + "reject duplicate which generates the same address": { + codeID: example.CodeID, + sender: example.CreatorAddr, + label: example.Label, + initMsg: initMsg, + expErr: types.ErrDuplicate, + }, + "different sender": { + codeID: example.CodeID, + sender: otherAddress, + label: example.Label, + initMsg: initMsg, + }, + "different code": { + codeID: otherExample.CodeID, + sender: example.CreatorAddr, + label: example.Label, + initMsg: []byte(`{}`), + }, + "different label": { + codeID: example.CodeID, + sender: example.CreatorAddr, + label: "other label", + initMsg: initMsg, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + ctx, _ := parentCtx.CacheContext() + gotAddr, _, gotErr := keepers.ContractKeeper.Instantiate(ctx, spec.codeID, spec.sender, nil, spec.initMsg, spec.label, example.Deposit) + if spec.expErr != nil { + assert.ErrorIs(t, gotErr, spec.expErr) + return + } + require.NoError(t, gotErr) + expAddr := BuildContractAddress(keepers.WasmKeeper.GetCodeInfo(ctx, spec.codeID).CodeHash, spec.sender, spec.label) + assert.Equal(t, expAddr.String(), gotAddr.String()) + require.NotContains(t, used, gotAddr.String()) + used[gotAddr.String()] = struct{}{} + }) + } +} + +func TestInstantiateWithAccounts(t *testing.T) { + parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities) + example := StoreHackatomExampleContract(t, parentCtx, keepers) + require.Equal(t, uint64(1), example.CodeID) + initMsg := mustMarshal(t, HackatomExampleInitMsg{Verifier: RandomAccountAddress(t), Beneficiary: RandomAccountAddress(t)}) + + senderAddr := DeterministicAccountAddress(t, 1) + keepers.Faucet.Fund(parentCtx, senderAddr, sdk.NewInt64Coin("denom", 100000000)) + const myLabel = "testing" + contractAddr := BuildContractAddress(example.Checksum, senderAddr, myLabel) + + lastAccountNumber := keepers.AccountKeeper.GetAccount(parentCtx, senderAddr).GetAccountNumber() + + specs := map[string]struct { + acceptList Option + account authtypes.AccountI + initBalance sdk.Coin + deposit sdk.Coins + expErr error + expAccount authtypes.AccountI + expBalance sdk.Coins + }{ + "unused BaseAccount exists": { + account: authtypes.NewBaseAccount(contractAddr, nil, 0, 0), + initBalance: sdk.NewInt64Coin("denom", 100000000), + expAccount: authtypes.NewBaseAccount(contractAddr, nil, lastAccountNumber+1, 0), // +1 for next seq + expBalance: sdk.NewCoins(sdk.NewInt64Coin("denom", 100000000)), + }, + "BaseAccount with sequence exists": { + account: authtypes.NewBaseAccount(contractAddr, nil, 0, 1), + expErr: types.ErrAccountExists, + }, + "BaseAccount with pubkey exists": { + account: authtypes.NewBaseAccount(contractAddr, &ed25519.PubKey{}, 0, 0), + expErr: types.ErrAccountExists, + }, + "no account existed": { + expAccount: authtypes.NewBaseAccount(contractAddr, nil, lastAccountNumber+1, 0), // +1 for next seq, + expBalance: sdk.NewCoins(), + }, + "no account existed before create with deposit": { + expAccount: authtypes.NewBaseAccount(contractAddr, nil, lastAccountNumber+1, 0), // +1 for next seq + deposit: sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1_000))), + expBalance: sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1_000))), + }, + "prune listed DelayedVestingAccount gets overwritten": { + account: vestingtypes.NewDelayedVestingAccount( + authtypes.NewBaseAccount(contractAddr, nil, 0, 0), + sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1_000))), time.Now().Add(30*time.Hour).Unix()), + initBalance: sdk.NewCoin("denom", sdk.NewInt(1_000)), + deposit: sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1))), + expAccount: authtypes.NewBaseAccount(contractAddr, nil, lastAccountNumber+2, 0), // +1 for next seq, +1 for spec.account created + expBalance: sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1))), + }, + "prune listed ContinuousVestingAccount gets overwritten": { + account: vestingtypes.NewContinuousVestingAccount( + authtypes.NewBaseAccount(contractAddr, nil, 0, 0), + sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1_000))), time.Now().Add(time.Hour).Unix(), time.Now().Add(2*time.Hour).Unix()), + initBalance: sdk.NewCoin("denom", sdk.NewInt(1_000)), + deposit: sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1))), + expAccount: authtypes.NewBaseAccount(contractAddr, nil, lastAccountNumber+2, 0), // +1 for next seq, +1 for spec.account created + expBalance: sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1))), + }, + "prune listed account without balance gets overwritten": { + account: vestingtypes.NewContinuousVestingAccount( + authtypes.NewBaseAccount(contractAddr, nil, 0, 0), + sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(0))), time.Now().Add(time.Hour).Unix(), time.Now().Add(2*time.Hour).Unix()), + expAccount: authtypes.NewBaseAccount(contractAddr, nil, lastAccountNumber+2, 0), // +1 for next seq, +1 for spec.account created + expBalance: sdk.NewCoins(), + }, + "unknown account type creates error": { + account: authtypes.NewModuleAccount( + authtypes.NewBaseAccount(contractAddr, nil, 0, 0), + "testing", + ), + initBalance: sdk.NewCoin("denom", sdk.NewInt(1_000)), + expErr: types.ErrAccountExists, + }, + "with option used to set non default type to accept list": { + acceptList: WithAcceptedAccountTypesOnContractInstantiation(&vestingtypes.DelayedVestingAccount{}), + account: vestingtypes.NewDelayedVestingAccount( + authtypes.NewBaseAccount(contractAddr, nil, 0, 0), + sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1_000))), time.Now().Add(30*time.Hour).Unix()), + initBalance: sdk.NewCoin("denom", sdk.NewInt(1_000)), + deposit: sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1))), + expAccount: vestingtypes.NewDelayedVestingAccount(authtypes.NewBaseAccount(contractAddr, nil, lastAccountNumber+1, 0), + sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1_000))), time.Now().Add(30*time.Hour).Unix()), + expBalance: sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1_001))), + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + ctx, _ := parentCtx.CacheContext() + if spec.account != nil { + keepers.AccountKeeper.SetAccount(ctx, keepers.AccountKeeper.NewAccount(ctx, spec.account)) + } + if !spec.initBalance.IsNil() { + keepers.Faucet.Fund(ctx, spec.account.GetAddress(), spec.initBalance) + } + if spec.acceptList != nil { + spec.acceptList.apply(keepers.WasmKeeper) + } + defer func() { + if spec.acceptList != nil { // reset + WithAcceptedAccountTypesOnContractInstantiation(&authtypes.BaseAccount{}).apply(keepers.WasmKeeper) + } + }() + // when + gotAddr, _, gotErr := keepers.ContractKeeper.Instantiate(ctx, 1, senderAddr, nil, initMsg, myLabel, spec.deposit) + if spec.expErr != nil { + assert.ErrorIs(t, gotErr, spec.expErr) + return + } + require.NoError(t, gotErr) + assert.Equal(t, contractAddr, gotAddr) + // and + gotAcc := keepers.AccountKeeper.GetAccount(ctx, contractAddr) + assert.Equal(t, spec.expAccount, gotAcc) + // and + gotBalance := keepers.BankKeeper.GetAllBalances(ctx, contractAddr) + assert.Equal(t, spec.expBalance, gotBalance) + }) + } +} + func TestInstantiateWithNonExistingCodeID(t *testing.T) { ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) initMsg := HackatomExampleInitMsg{} initMsgBz, err := json.Marshal(initMsg) @@ -585,13 +761,14 @@ func TestExecute(t *testing.T) { deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) - fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) + creator := DeterministicAccountAddress(t, 1) + keepers.Faucet.Fund(ctx, creator, deposit.Add(deposit...)...) + fred := keepers.Faucet.NewFundedRandomAccount(ctx, topUp...) + bob := RandomAccountAddress(t) - contractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) + contractID, _, err := keeper.Create(ctx, creator, hackatomWasm, nil) require.NoError(t, err) - _, _, bob := keyPubAddr() initMsg := HackatomExampleInitMsg{ Verifier: fred, Beneficiary: bob, @@ -601,7 +778,8 @@ func TestExecute(t *testing.T) { addr, _, err := keepers.ContractKeeper.Instantiate(ctx, contractID, creator, nil, initMsgBz, "demo contract 3", deposit) require.NoError(t, err) - require.Equal(t, "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", addr.String()) + // cosmos1eycfqpgtcp4gc9g24cvg6useyncxspq8qurv2z7cs0wzcgvmffaquzwe2e build with code-id 1, DeterministicAccountAddress(t, 1) and label `demo contract 3` + require.Equal(t, "cosmos1eycfqpgtcp4gc9g24cvg6useyncxspq8qurv2z7cs0wzcgvmffaquzwe2e", addr.String()) // ensure bob doesn't exist bobAcct := accKeeper.GetAccount(ctx, bob) @@ -638,7 +816,7 @@ func TestExecute(t *testing.T) { // make sure gas is properly deducted from ctx gasAfter := ctx.GasMeter().GasConsumed() if types.EnableGasVerification { - require.Equal(t, uint64(0x17cd2), gasAfter-gasBefore) + require.Equal(t, uint64(0x17d87), gasAfter-gasBefore) } // ensure bob now exists and got both payments released bobAcct = accKeeper.GetAccount(ctx, bob) @@ -725,7 +903,7 @@ func TestExecuteWithDeposit(t *testing.T) { if spec.fundAddr { fundAccounts(t, ctx, accKeeper, bankKeeper, spec.srcActor, sdk.NewCoins(sdk.NewInt64Coin("denom", 200))) } - codeID, err := keeper.Create(ctx, spec.srcActor, hackatomWasm, nil) + codeID, _, err := keeper.Create(ctx, spec.srcActor, hackatomWasm, nil) require.NoError(t, err) initMsg := HackatomExampleInitMsg{Verifier: spec.srcActor, Beneficiary: spec.beneficiary} @@ -755,7 +933,8 @@ func TestExecuteWithNonExistingAddress(t *testing.T) { keeper := keepers.ContractKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) + creator := DeterministicAccountAddress(t, 1) + keepers.Faucet.Fund(ctx, creator, deposit.Add(deposit...)...) // unauthorized - trialCtx so we don't change state nonExistingAddress := RandomAccountAddress(t) @@ -769,10 +948,11 @@ func TestExecuteWithPanic(t *testing.T) { deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) - fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) + creator := DeterministicAccountAddress(t, 1) + keepers.Faucet.Fund(ctx, creator, deposit.Add(deposit...)...) + fred := keepers.Faucet.NewFundedRandomAccount(ctx, topUp...) - contractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) + contractID, _, err := keeper.Create(ctx, creator, hackatomWasm, nil) require.NoError(t, err) _, _, bob := keyPubAddr() @@ -800,10 +980,11 @@ func TestExecuteWithCpuLoop(t *testing.T) { deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) - fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) + creator := DeterministicAccountAddress(t, 1) + keepers.Faucet.Fund(ctx, creator, deposit.Add(deposit...)...) + fred := keepers.Faucet.NewFundedRandomAccount(ctx, topUp...) - contractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) + contractID, _, err := keeper.Create(ctx, creator, hackatomWasm, nil) require.NoError(t, err) _, _, bob := keyPubAddr() @@ -841,10 +1022,11 @@ func TestExecuteWithStorageLoop(t *testing.T) { deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) - fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) + creator := DeterministicAccountAddress(t, 1) + keepers.Faucet.Fund(ctx, creator, deposit.Add(deposit...)...) + fred := keepers.Faucet.NewFundedRandomAccount(ctx, topUp...) - contractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) + contractID, _, err := keeper.Create(ctx, creator, hackatomWasm, nil) require.NoError(t, err) _, _, bob := keyPubAddr() @@ -877,21 +1059,23 @@ func TestExecuteWithStorageLoop(t *testing.T) { } func TestMigrate(t *testing.T) { - ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) + parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities) keeper := keepers.ContractKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) - fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) - - originalCodeID := StoreHackatomExampleContract(t, ctx, keepers).CodeID - newCodeID := StoreHackatomExampleContract(t, ctx, keepers).CodeID - ibcCodeID := StoreIBCReflectContract(t, ctx, keepers).CodeID + creator := DeterministicAccountAddress(t, 1) + keepers.Faucet.Fund(parentCtx, creator, deposit.Add(deposit...)...) + fred := DeterministicAccountAddress(t, 2) + keepers.Faucet.Fund(parentCtx, fred, topUp...) + + originalCodeID := StoreHackatomExampleContract(t, parentCtx, keepers).CodeID + newCodeID := StoreHackatomExampleContract(t, parentCtx, keepers).CodeID + ibcCodeID := StoreIBCReflectContract(t, parentCtx, keepers).CodeID require.NotEqual(t, originalCodeID, newCodeID) - restrictedCodeExample := StoreHackatomExampleContract(t, ctx, keepers) - require.NoError(t, keeper.SetAccessConfig(ctx, restrictedCodeExample.CodeID, restrictedCodeExample.CreatorAddr, types.AllowNobody)) + restrictedCodeExample := StoreHackatomExampleContract(t, parentCtx, keepers) + require.NoError(t, keeper.SetAccessConfig(parentCtx, restrictedCodeExample.CodeID, restrictedCodeExample.CreatorAddr, types.AllowNobody)) require.NotEqual(t, originalCodeID, restrictedCodeExample.CodeID) anyAddr := RandomAccountAddress(t) @@ -1017,7 +1201,7 @@ func TestMigrate(t *testing.T) { "fail when no IBC callbacks": { admin: fred, caller: fred, - initMsg: IBCReflectInitMsg{ReflectCodeID: StoreReflectContract(t, ctx, keepers)}.GetBytes(t), + initMsg: IBCReflectInitMsg{ReflectCodeID: StoreReflectContract(t, parentCtx, keepers).CodeID}.GetBytes(t), fromCodeID: ibcCodeID, toCodeID: newCodeID, migrateMsg: migMsgBz, @@ -1025,10 +1209,13 @@ func TestMigrate(t *testing.T) { }, } + blockHeight := parentCtx.BlockHeight() for msg, spec := range specs { t.Run(msg, func(t *testing.T) { // given a contract instance - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + ctx, _ := parentCtx.WithBlockHeight(blockHeight + 1).CacheContext() + blockHeight++ + contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, spec.fromCodeID, creator, spec.admin, spec.initMsg, "demo contract", nil) require.NoError(t, err) if spec.overrideContractAddr != nil { @@ -1109,15 +1296,16 @@ func TestMigrateWithDispatchedMessage(t *testing.T) { keeper := keepers.ContractKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) - fred := keepers.Faucet.NewFundedAccount(ctx, sdk.NewInt64Coin("denom", 5000)) + creator := DeterministicAccountAddress(t, 1) + keepers.Faucet.Fund(ctx, creator, deposit.Add(deposit...)...) + fred := keepers.Faucet.NewFundedRandomAccount(ctx, sdk.NewInt64Coin("denom", 5000)) burnerCode, err := os.ReadFile("./testdata/burner.wasm") require.NoError(t, err) - originalContractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) + originalContractID, _, err := keeper.Create(ctx, creator, hackatomWasm, nil) require.NoError(t, err) - burnerContractID, err := keeper.Create(ctx, creator, burnerCode, nil) + burnerContractID, _, err := keeper.Create(ctx, creator, burnerCode, nil) require.NoError(t, err) require.NotEqual(t, originalContractID, burnerContractID) @@ -1279,9 +1467,10 @@ func TestSudo(t *testing.T) { accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.ContractKeeper, keepers.BankKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) + creator := DeterministicAccountAddress(t, 1) + keepers.Faucet.Fund(ctx, creator, deposit.Add(deposit...)...) - contractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) + contractID, _, err := keeper.Create(ctx, creator, hackatomWasm, nil) require.NoError(t, err) _, _, bob := keyPubAddr() @@ -1294,7 +1483,7 @@ func TestSudo(t *testing.T) { require.NoError(t, err) addr, _, err := keepers.ContractKeeper.Instantiate(ctx, contractID, creator, nil, initMsgBz, "demo contract 3", deposit) require.NoError(t, err) - require.Equal(t, "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", addr.String()) + require.Equal(t, "cosmos1eycfqpgtcp4gc9g24cvg6useyncxspq8qurv2z7cs0wzcgvmffaquzwe2e", addr.String()) // the community is broke _, _, community := keyPubAddr() @@ -1358,15 +1547,16 @@ func mustMarshal(t *testing.T, r interface{}) []byte { } func TestUpdateContractAdmin(t *testing.T) { - ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) + parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities) keeper := keepers.ContractKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) - fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) + creator := DeterministicAccountAddress(t, 1) + keepers.Faucet.Fund(parentCtx, creator, deposit.Add(deposit...)...) + fred := keepers.Faucet.NewFundedRandomAccount(parentCtx, topUp...) - originalContractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) + originalContractID, _, err := keeper.Create(parentCtx, creator, hackatomWasm, nil) require.NoError(t, err) _, _, anyAddr := keyPubAddr() @@ -1409,6 +1599,7 @@ func TestUpdateContractAdmin(t *testing.T) { } for msg, spec := range specs { t.Run(msg, func(t *testing.T) { + ctx, _ := parentCtx.CacheContext() addr, _, err := keepers.ContractKeeper.Instantiate(ctx, originalContractID, creator, spec.instAdmin, initMsgBz, "demo contract", nil) require.NoError(t, err) if spec.overrideContractAddr != nil { @@ -1426,15 +1617,16 @@ func TestUpdateContractAdmin(t *testing.T) { } func TestClearContractAdmin(t *testing.T) { - ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) + parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities) keeper := keepers.ContractKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) - fred := keepers.Faucet.NewFundedAccount(ctx, topUp...) + creator := DeterministicAccountAddress(t, 1) + keepers.Faucet.Fund(parentCtx, creator, deposit.Add(deposit...)...) + fred := keepers.Faucet.NewFundedRandomAccount(parentCtx, topUp...) - originalContractID, err := keeper.Create(ctx, creator, hackatomWasm, nil) + originalContractID, _, err := keeper.Create(parentCtx, creator, hackatomWasm, nil) require.NoError(t, err) _, _, anyAddr := keyPubAddr() @@ -1472,6 +1664,7 @@ func TestClearContractAdmin(t *testing.T) { } for msg, spec := range specs { t.Run(msg, func(t *testing.T) { + ctx, _ := parentCtx.CacheContext() addr, _, err := keepers.ContractKeeper.Instantiate(ctx, originalContractID, creator, spec.instAdmin, initMsgBz, "demo contract", nil) require.NoError(t, err) if spec.overrideContractAddr != nil { @@ -1806,52 +1999,103 @@ func TestQueryIsolation(t *testing.T) { } func TestBuildContractAddress(t *testing.T) { + x, y := sdk.GetConfig().GetBech32AccountAddrPrefix(), sdk.GetConfig().GetBech32AccountPubPrefix() + t.Cleanup(func() { + sdk.GetConfig().SetBech32PrefixForAccount(x, y) + }) + sdk.GetConfig().SetBech32PrefixForAccount("purple", "purple") + + // test vectors from https://gist.github.com/webmaster128/e4d401d414bd0e7e6f70482f11877fbe specs := map[string]struct { - srcCodeID uint64 - srcInstanceID uint64 - expectedAddr string + checksum []byte + label string + creator string + expAddress string }{ - "initial contract": { - srcCodeID: 1, - srcInstanceID: 1, - expectedAddr: "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", - }, - "demo value": { - srcCodeID: 1, - srcInstanceID: 100, - expectedAddr: "cosmos1mujpjkwhut9yjw4xueyugc02evfv46y0dtmnz4lh8xxkkdapym9stu5qm8", - }, - "both below max": { - srcCodeID: math.MaxUint32 - 1, - srcInstanceID: math.MaxUint32 - 1, - }, - "both at max": { - srcCodeID: math.MaxUint32, - srcInstanceID: math.MaxUint32, - }, - "codeID > max u32": { - srcCodeID: math.MaxUint32 + 1, - srcInstanceID: 17, - expectedAddr: "cosmos1673hrexz4h6s0ft04l96ygq667djzh2nsr335kstjp49x5dk6rpsf5t0le", - }, - "instanceID > max u32": { - srcCodeID: 22, - srcInstanceID: math.MaxUint32 + 1, - expectedAddr: "cosmos10q3pgfvmeyy0veekgtqhxujxkhz0vm9zmalqgc7evrhj68q3l62qrdfg4m", + "initial account addr example": { + checksum: fromHex("13A1FC994CC6D1C81B746EE0C0FF6F90043875E0BF1D9BE6B7D779FC978DC2A5"), + label: "instance 1", + creator: "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py", + expAddress: "purple1jukvumwqfxapueg6un6rtmafxktcluzv70xc3edz2m5t3slgw49qrmhc03", + }, + "account addr with different label": { + checksum: fromHex("13A1FC994CC6D1C81B746EE0C0FF6F90043875E0BF1D9BE6B7D779FC978DC2A5"), + label: "instance 2", + creator: "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py", + expAddress: "purple1jpc2w2vu2t6k7u09p6v8e3w28ptnzvvt2snu5rytlj8qya27dq5sjkwm06", + }, + "initial contract addr example": { + checksum: fromHex("13A1FC994CC6D1C81B746EE0C0FF6F90043875E0BF1D9BE6B7D779FC978DC2A5"), + label: "instance 1", + creator: "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m", + expAddress: "purple1wde5zlcveuh79w37w5g244qu9xja3hgfulyfkjvkxvwvjzgd5l3qfraz3c", + }, + "contract addr with different label": { + checksum: fromHex("13A1FC994CC6D1C81B746EE0C0FF6F90043875E0BF1D9BE6B7D779FC978DC2A5"), + label: "instance 2", + creator: "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m", + expAddress: "purple1vae2kf0r3ehtq5q2jmfkg7wp4ckxwrw8dv4pvazz5nlzzu05lxzq878fa9", + }, + "account addr with different checksum": { + checksum: fromHex("1DA6C16DE2CBAF7AD8CBB66F0925BA33F5C278CB2491762D04658C1480EA229B"), + label: "instance 1", + creator: "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py", + expAddress: "purple1gmgvt9levtn52mpfal3gl5tv60f47zez3wgczrh5c9352sfm9crs47zt0k", + }, + "account addr with different checksum and label": { + checksum: fromHex("1DA6C16DE2CBAF7AD8CBB66F0925BA33F5C278CB2491762D04658C1480EA229B"), + label: "instance 2", + creator: "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py", + expAddress: "purple13jrcqxknt05rhdxmegjzjel666yay6fj3xvfp6445k7a9q2km4wqa7ss34", + }, + "contract addr with different checksum": { + checksum: fromHex("1DA6C16DE2CBAF7AD8CBB66F0925BA33F5C278CB2491762D04658C1480EA229B"), + label: "instance 1", + creator: "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m", + expAddress: "purple1lu0lf6wmqeuwtrx93ptzvf4l0dyyz2vz6s8h5y9cj42fvhsmracq49pww9", + }, + "contract addr with different checksum and label": { + checksum: fromHex("1DA6C16DE2CBAF7AD8CBB66F0925BA33F5C278CB2491762D04658C1480EA229B"), + label: "instance 2", + creator: "purple1nxvenxve42424242hwamhwamenxvenxvmhwamhwaamhwamhwlllsatsy6m", + expAddress: "purple1zmerc8a9ml2au29rq3knuu35fktef3akceurckr6pf370n0wku7sw3c9mj", + }, + // regression tests + "min label size": { + checksum: fromHex("13A1FC994CC6D1C81B746EE0C0FF6F90043875E0BF1D9BE6B7D779FC978DC2A5"), + label: "x", + creator: "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py", + expAddress: "purple16pc8gt824lmp3dh2sxvttj0ykcs02n5p3ldswhv3j7y853gghlfq7mqeh9", + }, + "max label size": { + checksum: fromHex("13A1FC994CC6D1C81B746EE0C0FF6F90043875E0BF1D9BE6B7D779FC978DC2A5"), + label: strings.Repeat("x", types.MaxLabelSize), + creator: "purple1nxvenxve42424242hwamhwamenxvenxvhxf2py", + expAddress: "purple1prkdvjmvv4s3tnppfxmlpj259v9cplf3wws4qq9qd7w3s4yqzqeqem4759", }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { - gotAddr := BuildContractAddress(spec.srcCodeID, spec.srcInstanceID) - require.NotNil(t, gotAddr) - assert.Nil(t, sdk.VerifyAddressFormat(gotAddr)) - if len(spec.expectedAddr) > 0 { - require.Equal(t, spec.expectedAddr, gotAddr.String()) - } + creatorAddr, err := sdk.AccAddressFromBech32(spec.creator) + require.NoError(t, err) + + // when + gotAddr := BuildContractAddress(spec.checksum, creatorAddr, spec.label) + + require.Equal(t, spec.expAddress, gotAddr.String()) + require.NoError(t, sdk.VerifyAddressFormat(gotAddr)) }) } } +func fromHex(s string) []byte { + r, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return r +} + func TestSetAccessConfig(t *testing.T) { parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities) k := keepers.WasmKeeper diff --git a/x/wasm/keeper/legacy_querier_test.go b/x/wasm/keeper/legacy_querier_test.go index 1f22a5082c..1b6006fa30 100644 --- a/x/wasm/keeper/legacy_querier_test.go +++ b/x/wasm/keeper/legacy_querier_test.go @@ -21,13 +21,13 @@ func TestLegacyQueryContractState(t *testing.T) { keeper := keepers.WasmKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) - anyAddr := keepers.Faucet.NewFundedAccount(ctx, sdk.NewInt64Coin("denom", 5000)) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit.Add(deposit...)...) + anyAddr := keepers.Faucet.NewFundedRandomAccount(ctx, sdk.NewInt64Coin("denom", 5000)) wasmCode, err := os.ReadFile("./testdata/hackatom.wasm") require.NoError(t, err) - contractID, err := keepers.ContractKeeper.Create(ctx, creator, wasmCode, nil) + contractID, _, err := keepers.ContractKeeper.Create(ctx, creator, wasmCode, nil) require.NoError(t, err) _, _, bob := keyPubAddr() @@ -159,13 +159,13 @@ func TestLegacyQueryContractListByCodeOrdering(t *testing.T) { deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 1000000)) topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 500)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit.Add(deposit...)...) - anyAddr := keepers.Faucet.NewFundedAccount(ctx, topUp...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit.Add(deposit...)...) + anyAddr := keepers.Faucet.NewFundedRandomAccount(ctx, topUp...) wasmCode, err := os.ReadFile("./testdata/hackatom.wasm") require.NoError(t, err) - codeID, err := keepers.ContractKeeper.Create(ctx, creator, wasmCode, nil) + codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, wasmCode, nil) require.NoError(t, err) _, _, bob := keyPubAddr() diff --git a/x/wasm/keeper/migrations.go b/x/wasm/keeper/migrations.go new file mode 100644 index 0000000000..4d218d504a --- /dev/null +++ b/x/wasm/keeper/migrations.go @@ -0,0 +1,27 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/CosmWasm/wasmd/x/wasm/types" +) + +// Migrator is a struct for handling in-place store migrations. +type Migrator struct { + keeper *Keeper +} + +// NewMigrator returns a new Migrator. +func NewMigrator(keeper *Keeper) Migrator { + return Migrator{keeper: keeper} +} + +var keyLastInstanceID = append(types.SequenceKeyPrefix, []byte("lastContractId")...) + +// Migrate1to2 migrates from version 1 to 2. +// Remove the unused sequence for address generation +func (m Migrator) Migrate1to2(ctx sdk.Context) error { + store := ctx.KVStore(m.keeper.storeKey) + store.Delete(keyLastInstanceID) + return nil +} diff --git a/x/wasm/keeper/migrations_test.go b/x/wasm/keeper/migrations_test.go new file mode 100644 index 0000000000..bc9110a3b3 --- /dev/null +++ b/x/wasm/keeper/migrations_test.go @@ -0,0 +1,20 @@ +package keeper + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" +) + +func TestMigrateV1ToV2(t *testing.T) { + ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) + store := ctx.KVStore(keepers.WasmKeeper.storeKey) + store.Set(keyLastInstanceID, sdk.Uint64ToBigEndian(100)) + + // when + NewMigrator(keepers.WasmKeeper).Migrate1to2(ctx) + + // then + assert.False(t, store.Has(keyLastInstanceID)) +} diff --git a/x/wasm/keeper/msg_server.go b/x/wasm/keeper/msg_server.go index 1c45ff52ec..8c2a592ebf 100644 --- a/x/wasm/keeper/msg_server.go +++ b/x/wasm/keeper/msg_server.go @@ -32,13 +32,14 @@ func (m msgServer) StoreCode(goCtx context.Context, msg *types.MsgStoreCode) (*t sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender), )) - codeID, err := m.keeper.Create(ctx, senderAddr, msg.WASMByteCode, msg.InstantiatePermission) + codeID, checksum, err := m.keeper.Create(ctx, senderAddr, msg.WASMByteCode, msg.InstantiatePermission) if err != nil { return nil, err } return &types.MsgStoreCodeResponse{ - CodeID: codeID, + CodeID: codeID, + Checksum: checksum, }, nil } diff --git a/x/wasm/keeper/msg_server_integration_test.go b/x/wasm/keeper/msg_server_integration_test.go new file mode 100644 index 0000000000..dde8ae7744 --- /dev/null +++ b/x/wasm/keeper/msg_server_integration_test.go @@ -0,0 +1,46 @@ +package keeper_test + +import ( + "crypto/sha256" + _ "embed" + "testing" + + "github.com/cosmos/cosmos-sdk/testutil/testdata" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/CosmWasm/wasmd/app" + "github.com/CosmWasm/wasmd/x/wasm/types" +) + +//go:embed testdata/reflect.wasm +var wasmContract []byte + +func TestStoreCode(t *testing.T) { + wasmApp := app.Setup(false) + ctx := wasmApp.BaseApp.NewContext(false, tmproto.Header{}) + _, _, sender := testdata.KeyTestPubAddr() + msg := types.MsgStoreCodeFixture(func(m *types.MsgStoreCode) { + m.WASMByteCode = wasmContract + m.Sender = sender.String() + }) + + // when + rsp, err := wasmApp.MsgServiceRouter().Handler(msg)(ctx, msg) + + // then + require.NoError(t, err) + var result types.MsgStoreCodeResponse + require.NoError(t, wasmApp.AppCodec().Unmarshal(rsp.Data, &result)) + assert.Equal(t, uint64(1), result.CodeID) + expHash := sha256.Sum256(wasmContract) + assert.Equal(t, expHash[:], result.Checksum) + // and + info := wasmApp.WasmKeeper.GetCodeInfo(ctx, 1) + assert.NotNil(t, info) + assert.Equal(t, expHash[:], info.CodeHash) + assert.Equal(t, sender.String(), info.Creator) + assert.Equal(t, types.DefaultParams().InstantiateDefaultPermission.With(sender), info.InstantiateConfig) +} diff --git a/x/wasm/keeper/options.go b/x/wasm/keeper/options.go index c3d9d1248e..e6119b4478 100644 --- a/x/wasm/keeper/options.go +++ b/x/wasm/keeper/options.go @@ -2,7 +2,9 @@ package keeper import ( "fmt" + "reflect" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/prometheus/client_golang/prometheus" "github.com/CosmWasm/wasmd/x/wasm/types" @@ -89,11 +91,25 @@ func WithMessageEncoders(x *MessageEncoders) Option { // WithCoinTransferrer is an optional constructor parameter to set a custom coin transferrer func WithCoinTransferrer(x CoinTransferrer) Option { + if x == nil { + panic("must not be nil") + } return optsFn(func(k *Keeper) { k.bank = x }) } +// WithCoinPruner is an optional constructor parameter to set a custom type that handles balances +// for accounts pruned on contract instantiate +func WithCoinPruner(x CoinPruner) Option { + if x == nil { + panic("must not be nil") + } + return optsFn(func(k *Keeper) { + k.coinPruner = x + }) +} + func WithVMCacheMetrics(r prometheus.Registerer) Option { return optsFn(func(k *Keeper) { NewWasmVMMetricsCollector(k.wasmVM).Register(r) @@ -104,6 +120,9 @@ func WithVMCacheMetrics(r prometheus.Registerer) Option { // When the "gas multiplier" for wasmvm gas conversion is modified inside the new register, // make sure to also use `WithApiCosts` option for non default values func WithGasRegister(x GasRegister) Option { + if x == nil { + panic("must not be nil") + } return optsFn(func(k *Keeper) { k.gasRegister = x }) @@ -123,3 +142,42 @@ func WithMaxQueryStackSize(m uint32) Option { k.maxQueryStackSize = m }) } + +// WithAcceptedAccountTypesOnContractInstantiation sets the accepted account types. Account types of this list won't be overwritten or cause a failure +// when they exist for an address on contract instantiation. +// +// Values should be references and contain the `*authtypes.BaseAccount` as default bank account type. +func WithAcceptedAccountTypesOnContractInstantiation(accts ...authtypes.AccountI) Option { + m := asTypeMap(accts) + return optsFn(func(k *Keeper) { + k.acceptedAccountTypes = m + }) +} + +// WithPruneAccountTypesOnContractInstantiation sets the account types that should be cleared. Account types of this list +// will be overwritten with the BaseAccount type and their balance burned, when they exist for an address on contract +// instantiation. +// +// Values should be references and contain the `*vestingtypes.DelayedVestingAccount`, `*vestingtypes.ContinuousVestingAccount` +// as post genesis account types with an open address range. +func WithPruneAccountTypesOnContractInstantiation(accts ...authtypes.AccountI) Option { + m := asTypeMap(accts) + return optsFn(func(k *Keeper) { + k.pruneAccountTypes = m + }) +} + +func asTypeMap(accts []authtypes.AccountI) map[reflect.Type]struct{} { + m := make(map[reflect.Type]struct{}, len(accts)) + for _, a := range accts { + if a == nil { + panic(types.ErrEmpty.Wrap("address")) + } + at := reflect.TypeOf(a) + if _, exists := m[at]; exists { + panic(types.ErrDuplicate.Wrapf("%T", a)) + } + m[at] = struct{}{} + } + return m +} diff --git a/x/wasm/keeper/options_test.go b/x/wasm/keeper/options_test.go index 07195e0468..b4c7f0861e 100644 --- a/x/wasm/keeper/options_test.go +++ b/x/wasm/keeper/options_test.go @@ -1,9 +1,13 @@ package keeper import ( + "reflect" "testing" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" @@ -81,10 +85,36 @@ func TestConstructorOptions(t *testing.T) { assert.IsType(t, uint32(1), k.maxQueryStackSize) }, }, + "accepted account types": { + srcOpt: WithAcceptedAccountTypesOnContractInstantiation(&authtypes.BaseAccount{}, &vestingtypes.ContinuousVestingAccount{}), + verify: func(t *testing.T, k Keeper) { + exp := map[reflect.Type]struct{}{ + reflect.TypeOf(&authtypes.BaseAccount{}): {}, + reflect.TypeOf(&vestingtypes.ContinuousVestingAccount{}): {}, + } + assert.Equal(t, exp, k.acceptedAccountTypes) + }, + }, + "prune account types": { + srcOpt: WithPruneAccountTypesOnContractInstantiation(&authtypes.BaseAccount{}, &vestingtypes.ContinuousVestingAccount{}), + verify: func(t *testing.T, k Keeper) { + exp := map[reflect.Type]struct{}{ + reflect.TypeOf(&authtypes.BaseAccount{}): {}, + reflect.TypeOf(&vestingtypes.ContinuousVestingAccount{}): {}, + } + assert.Equal(t, exp, k.pruneAccountTypes) + }, + }, + "coin pruner": { + srcOpt: WithCoinPruner(CoinBurner{}), + verify: func(t *testing.T, k Keeper) { + assert.Equal(t, CoinBurner{}, k.coinPruner) + }, + }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { - k := NewKeeper(nil, nil, paramtypes.NewSubspace(nil, nil, nil, nil, ""), authkeeper.AccountKeeper{}, nil, stakingkeeper.Keeper{}, distributionkeeper.Keeper{}, nil, nil, nil, nil, nil, nil, "tempDir", types.DefaultWasmConfig(), AvailableCapabilities, spec.srcOpt) + k := NewKeeper(nil, nil, paramtypes.NewSubspace(nil, nil, nil, nil, ""), authkeeper.AccountKeeper{}, &bankkeeper.BaseKeeper{}, stakingkeeper.Keeper{}, distributionkeeper.Keeper{}, nil, nil, nil, nil, nil, nil, "tempDir", types.DefaultWasmConfig(), AvailableCapabilities, spec.srcOpt) spec.verify(t, k) }) } diff --git a/x/wasm/keeper/proposal_handler.go b/x/wasm/keeper/proposal_handler.go index 905b073d5e..29b736b00b 100644 --- a/x/wasm/keeper/proposal_handler.go +++ b/x/wasm/keeper/proposal_handler.go @@ -64,7 +64,7 @@ func handleStoreCodeProposal(ctx sdk.Context, k types.ContractOpsKeeper, p types if err != nil { return sdkerrors.Wrap(err, "run as address") } - codeID, err := k.Create(ctx, runAsAddr, p.WASMByteCode, p.InstantiatePermission) + codeID, _, err := k.Create(ctx, runAsAddr, p.WASMByteCode, p.InstantiatePermission) if err != nil { return err } diff --git a/x/wasm/keeper/proposal_integration_test.go b/x/wasm/keeper/proposal_integration_test.go index f7034db562..ace55269db 100644 --- a/x/wasm/keeper/proposal_integration_test.go +++ b/x/wasm/keeper/proposal_integration_test.go @@ -2,6 +2,7 @@ package keeper import ( "bytes" + "crypto/sha256" "encoding/hex" "encoding/json" "errors" @@ -116,9 +117,8 @@ func TestInstantiateProposal(t *testing.T) { require.NoError(t, err) // then - contractAddr, err := sdk.AccAddressFromBech32("cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr") - require.NoError(t, err) - + codeHash := keepers.WasmKeeper.GetCodeInfo(ctx, 1).CodeHash + contractAddr := BuildContractAddress(codeHash, oneAddress, "testing") cInfo := wasmKeeper.GetContractInfo(ctx, contractAddr) require.NotNil(t, cInfo) assert.Equal(t, uint64(1), cInfo.CodeID) @@ -188,9 +188,8 @@ func TestInstantiateProposal_NoAdmin(t *testing.T) { require.NoError(t, err) // then - contractAddr, err := sdk.AccAddressFromBech32("cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr") - require.NoError(t, err) - + codeHash := keepers.WasmKeeper.GetCodeInfo(ctx, 1).CodeHash + contractAddr := BuildContractAddress(codeHash, oneAddress, "testing") cInfo := wasmKeeper.GetContractInfo(ctx, contractAddr) require.NotNil(t, cInfo) assert.Equal(t, uint64(1), cInfo.CodeID) @@ -229,9 +228,9 @@ func TestMigrateProposal(t *testing.T) { require.NoError(t, wasmKeeper.importCode(ctx, 2, codeInfoFixture, wasmCode)) var ( - anyAddress sdk.AccAddress = bytes.Repeat([]byte{0x1}, types.ContractAddrLen) - otherAddress sdk.AccAddress = bytes.Repeat([]byte{0x2}, types.ContractAddrLen) - contractAddr = BuildContractAddress(1, 1) + anyAddress = DeterministicAccountAddress(t, 1) + otherAddress = DeterministicAccountAddress(t, 2) + contractAddr = BuildContractAddress(codeInfoFixture.CodeHash, RandomAccountAddress(t), "") ) contractInfoFixture := types.ContractInfoFixture(func(c *types.ContractInfo) { @@ -408,12 +407,13 @@ func TestSudoProposal(t *testing.T) { } func TestAdminProposals(t *testing.T) { - var ( - otherAddress sdk.AccAddress = bytes.Repeat([]byte{0x2}, types.ContractAddrLen) - contractAddr = BuildContractAddress(1, 1) - ) wasmCode, err := os.ReadFile("./testdata/hackatom.wasm") require.NoError(t, err) + var ( + otherAddress = DeterministicAccountAddress(t, 2) + codeHash = sha256.Sum256(wasmCode) + contractAddr = BuildContractAddress(codeHash[:], RandomAccountAddress(t), "") + ) specs := map[string]struct { state types.ContractInfo diff --git a/x/wasm/keeper/querier_test.go b/x/wasm/keeper/querier_test.go index 21df7f94fd..115eeb8419 100644 --- a/x/wasm/keeper/querier_test.go +++ b/x/wasm/keeper/querier_test.go @@ -158,8 +158,9 @@ func TestQuerySmartContractState(t *testing.T) { func TestQuerySmartContractPanics(t *testing.T) { ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) - contractAddr := BuildContractAddress(1, 1) - keepers.WasmKeeper.storeCodeInfo(ctx, 1, types.CodeInfo{}) + creator := RandomAccountAddress(t) + contractAddr := BuildContractAddress([]byte("myCodeHash"), creator, "testing") + keepers.WasmKeeper.storeCodeInfo(ctx, 1, types.CodeInfo{CodeHash: []byte("myCodeHash")}) keepers.WasmKeeper.storeContractInfo(ctx, contractAddr, &types.ContractInfo{ CodeID: 1, Created: types.NewAbsoluteTxPosition(ctx), @@ -262,13 +263,13 @@ func TestQueryContractListByCodeOrdering(t *testing.T) { deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 1000000)) topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 500)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) - anyAddr := keepers.Faucet.NewFundedAccount(ctx, topUp...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) + anyAddr := keepers.Faucet.NewFundedRandomAccount(ctx, topUp...) wasmCode, err := os.ReadFile("./testdata/hackatom.wasm") require.NoError(t, err) - codeID, err := keepers.ContractKeeper.Create(ctx, creator, wasmCode, nil) + codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, wasmCode, nil) require.NoError(t, err) _, _, bob := keyPubAddr() diff --git a/x/wasm/keeper/reflect_test.go b/x/wasm/keeper/reflect_test.go index 8e4f4829eb..f478a32935 100644 --- a/x/wasm/keeper/reflect_test.go +++ b/x/wasm/keeper/reflect_test.go @@ -43,18 +43,18 @@ func TestReflectContractSend(t *testing.T) { accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.ContractKeeper, keepers.BankKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) _, _, bob := keyPubAddr() // upload reflect code - reflectID, err := keeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) + reflectID, _, err := keeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) require.NoError(t, err) require.Equal(t, uint64(1), reflectID) // upload hackatom escrow code escrowCode, err := os.ReadFile("./testdata/hackatom.wasm") require.NoError(t, err) - escrowID, err := keeper.Create(ctx, creator, escrowCode, nil) + escrowID, _, err := keeper.Create(ctx, creator, escrowCode, nil) require.NoError(t, err) require.Equal(t, uint64(2), escrowID) @@ -122,12 +122,12 @@ func TestReflectCustomMsg(t *testing.T) { accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.ContractKeeper, keepers.BankKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) - bob := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) + bob := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) _, _, fred := keyPubAddr() // upload code - codeID, err := keeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) + codeID, _, err := keeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) require.NoError(t, err) require.Equal(t, uint64(1), codeID) @@ -213,10 +213,10 @@ func TestMaskReflectCustomQuery(t *testing.T) { keeper := keepers.WasmKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) // upload code - codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) + codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) require.NoError(t, err) require.Equal(t, uint64(1), codeID) @@ -263,10 +263,10 @@ func TestReflectStargateQuery(t *testing.T) { funds := sdk.NewCoins(sdk.NewInt64Coin("denom", 320000)) contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000)) expectedBalance := funds.Sub(contractStart) - creator := keepers.Faucet.NewFundedAccount(ctx, funds...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, funds...) // upload code - codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) + codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) require.NoError(t, err) require.Equal(t, uint64(1), codeID) @@ -303,7 +303,7 @@ func TestReflectTotalSupplyQuery(t *testing.T) { ctx, keepers := CreateTestInput(t, false, ReflectFeatures, WithMessageEncoders(reflectEncoders(cdc)), WithQueryPlugins(reflectPlugins())) keeper := keepers.WasmKeeper // upload code - codeID := StoreReflectContract(t, ctx, keepers) + codeID := StoreReflectContract(t, ctx, keepers).CodeID // creator instantiates a contract and gives it tokens creator := RandomAccountAddress(t) contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "testing", nil) @@ -356,10 +356,10 @@ func TestReflectInvalidStargateQuery(t *testing.T) { funds := sdk.NewCoins(sdk.NewInt64Coin("denom", 320000)) contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000)) - creator := keepers.Faucet.NewFundedAccount(ctx, funds...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, funds...) // upload code - codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) + codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) require.NoError(t, err) require.Equal(t, uint64(1), codeID) @@ -434,10 +434,10 @@ func TestMaskReflectWasmQueries(t *testing.T) { keeper := keepers.WasmKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) // upload reflect code - reflectID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) + reflectID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) require.NoError(t, err) require.Equal(t, uint64(1), reflectID) @@ -504,10 +504,10 @@ func TestWasmRawQueryWithNil(t *testing.T) { keeper := keepers.WasmKeeper deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) // upload reflect code - reflectID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) + reflectID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) require.NoError(t, err) require.Equal(t, uint64(1), reflectID) diff --git a/x/wasm/keeper/snapshotter_integration_test.go b/x/wasm/keeper/snapshotter_integration_test.go index bef6cf0050..f100a1698c 100644 --- a/x/wasm/keeper/snapshotter_integration_test.go +++ b/x/wasm/keeper/snapshotter_integration_test.go @@ -55,11 +55,10 @@ func TestSnapshotter(t *testing.T) { for i, v := range spec.wasmFiles { wasmCode, err := os.ReadFile(v) require.NoError(t, err) - codeID, err := contractKeeper.Create(ctx, genesisAddr, wasmCode, nil) + codeID, checksum, err := contractKeeper.Create(ctx, genesisAddr, wasmCode, nil) require.NoError(t, err) require.Equal(t, uint64(i+1), codeID) - hash := sha256.Sum256(wasmCode) - srcCodeIDToChecksum[codeID] = hash[:] + srcCodeIDToChecksum[codeID] = checksum } // create snapshot srcWasmApp.Commit() diff --git a/x/wasm/keeper/staking_test.go b/x/wasm/keeper/staking_test.go index 15f0e0ac70..23ca71819b 100644 --- a/x/wasm/keeper/staking_test.go +++ b/x/wasm/keeper/staking_test.go @@ -103,12 +103,12 @@ func TestInitializeStaking(t *testing.T) { assert.Equal(t, v.GetDelegatorShares(), sdk.NewDec(1234567)) deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000), sdk.NewInt64Coin("stake", 500000)) - creator := k.Faucet.NewFundedAccount(ctx, deposit...) + creator := k.Faucet.NewFundedRandomAccount(ctx, deposit...) // upload staking derivates code stakingCode, err := os.ReadFile("./testdata/staking.wasm") require.NoError(t, err) - stakingID, err := keeper.Create(ctx, creator, stakingCode, nil) + stakingID, _, err := keeper.Create(ctx, creator, stakingCode, nil) require.NoError(t, err) require.Equal(t, uint64(1), stakingID) @@ -186,12 +186,12 @@ func initializeStaking(t *testing.T) initInfo { assert.Equal(t, v.Status, stakingtypes.Bonded) deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000), sdk.NewInt64Coin("stake", 500000)) - creator := k.Faucet.NewFundedAccount(ctx, deposit...) + creator := k.Faucet.NewFundedRandomAccount(ctx, deposit...) // upload staking derivates code stakingCode, err := os.ReadFile("./testdata/staking.wasm") require.NoError(t, err) - stakingID, err := k.ContractKeeper.Create(ctx, creator, stakingCode, nil) + stakingID, _, err := k.ContractKeeper.Create(ctx, creator, stakingCode, nil) require.NoError(t, err) require.Equal(t, uint64(1), stakingID) @@ -239,7 +239,7 @@ func TestBonding(t *testing.T) { // bob has 160k, putting 80k into the contract full := sdk.NewCoins(sdk.NewInt64Coin("stake", 160000)) funds := sdk.NewCoins(sdk.NewInt64Coin("stake", 80000)) - bob := initInfo.faucet.NewFundedAccount(ctx, full...) + bob := initInfo.faucet.NewFundedRandomAccount(ctx, full...) // check contract state before assertBalance(t, ctx, keeper, contractAddr, bob, "0") @@ -287,7 +287,7 @@ func TestUnbonding(t *testing.T) { // bob has 160k, putting 80k into the contract full := sdk.NewCoins(sdk.NewInt64Coin("stake", 160000)) funds := sdk.NewCoins(sdk.NewInt64Coin("stake", 80000)) - bob := initInfo.faucet.NewFundedAccount(ctx, full...) + bob := initInfo.faucet.NewFundedRandomAccount(ctx, full...) bond := StakingHandleMsg{ Bond: &struct{}{}, @@ -354,7 +354,7 @@ func TestReinvest(t *testing.T) { // full is 2x funds, 1x goes to the contract, other stays on his wallet full := sdk.NewCoins(sdk.NewInt64Coin("stake", 400000)) funds := sdk.NewCoins(sdk.NewInt64Coin("stake", 200000)) - bob := initInfo.faucet.NewFundedAccount(ctx, full...) + bob := initInfo.faucet.NewFundedRandomAccount(ctx, full...) // we will stake 200k to a validator with 1M self-bond // this means we should get 1/6 of the rewards @@ -422,7 +422,7 @@ func TestQueryStakingInfo(t *testing.T) { // full is 2x funds, 1x goes to the contract, other stays on his wallet full := sdk.NewCoins(sdk.NewInt64Coin("stake", 400000)) funds := sdk.NewCoins(sdk.NewInt64Coin("stake", 200000)) - bob := initInfo.faucet.NewFundedAccount(ctx, full...) + bob := initInfo.faucet.NewFundedRandomAccount(ctx, full...) // we will stake 200k to a validator with 1M self-bond // this means we should get 1/6 of the rewards @@ -444,10 +444,10 @@ func TestQueryStakingInfo(t *testing.T) { // STEP 2: Prepare the mask contract deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) - creator := initInfo.faucet.NewFundedAccount(ctx, deposit...) + creator := initInfo.faucet.NewFundedRandomAccount(ctx, deposit...) // upload mask code - maskID, err := initInfo.contractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) + maskID, _, err := initInfo.contractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) require.NoError(t, err) require.Equal(t, uint64(2), maskID) @@ -599,7 +599,7 @@ func TestQueryStakingPlugin(t *testing.T) { // full is 2x funds, 1x goes to the contract, other stays on his wallet full := sdk.NewCoins(sdk.NewInt64Coin("stake", 400000)) funds := sdk.NewCoins(sdk.NewInt64Coin("stake", 200000)) - bob := initInfo.faucet.NewFundedAccount(ctx, full...) + bob := initInfo.faucet.NewFundedRandomAccount(ctx, full...) // we will stake 200k to a validator with 1M self-bond // this means we should get 1/6 of the rewards @@ -652,7 +652,7 @@ func TestQueryStakingPlugin(t *testing.T) { // adds a few validators and returns a list of validators that are registered func addValidator(t *testing.T, ctx sdk.Context, stakingKeeper stakingkeeper.Keeper, faucet *TestFaucet, value sdk.Coin) sdk.ValAddress { - owner := faucet.NewFundedAccount(ctx, value) + owner := faucet.NewFundedRandomAccount(ctx, value) privKey := secp256k1.GenPrivKey() pubKey := privKey.PubKey() diff --git a/x/wasm/keeper/submsg_test.go b/x/wasm/keeper/submsg_test.go index d1f1d30cda..cd2555fef7 100644 --- a/x/wasm/keeper/submsg_test.go +++ b/x/wasm/keeper/submsg_test.go @@ -26,12 +26,12 @@ func TestDispatchSubMsgSuccessCase(t *testing.T) { deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) creatorBalance := deposit.Sub(contractStart) _, _, fred := keyPubAddr() // upload code - codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) + codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) require.NoError(t, err) require.Equal(t, uint64(1), codeID) @@ -110,16 +110,16 @@ func TestDispatchSubMsgErrorHandling(t *testing.T) { ctx = ctx.WithBlockGasMeter(sdk.NewInfiniteGasMeter()) keeper := keepers.WasmKeeper contractStart := sdk.NewCoins(sdk.NewInt64Coin(fundedDenom, int64(fundedAmount))) - uploader := keepers.Faucet.NewFundedAccount(ctx, contractStart.Add(contractStart...)...) + uploader := keepers.Faucet.NewFundedRandomAccount(ctx, contractStart.Add(contractStart...)...) // upload code - reflectID, err := keepers.ContractKeeper.Create(ctx, uploader, testdata.ReflectContractWasm(), nil) + reflectID, _, err := keepers.ContractKeeper.Create(ctx, uploader, testdata.ReflectContractWasm(), nil) require.NoError(t, err) // create hackatom contract for testing (for infinite loop) hackatomCode, err := os.ReadFile("./testdata/hackatom.wasm") require.NoError(t, err) - hackatomID, err := keepers.ContractKeeper.Create(ctx, uploader, hackatomCode, nil) + hackatomID, _, err := keepers.ContractKeeper.Create(ctx, uploader, hackatomCode, nil) require.NoError(t, err) _, _, bob := keyPubAddr() _, _, fred := keyPubAddr() @@ -282,7 +282,7 @@ func TestDispatchSubMsgErrorHandling(t *testing.T) { } for name, tc := range cases { t.Run(name, func(t *testing.T) { - creator := keepers.Faucet.NewFundedAccount(ctx, contractStart...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, contractStart...) _, _, empty := keyPubAddr() contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, reflectID, creator, nil, []byte("{}"), fmt.Sprintf("contract %s", name), contractStart) @@ -366,11 +366,11 @@ func TestDispatchSubMsgEncodeToNoSdkMsg(t *testing.T) { deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) _, _, fred := keyPubAddr() // upload code - codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) + codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) require.NoError(t, err) // creator instantiates a contract and gives it tokens @@ -432,11 +432,11 @@ func TestDispatchSubMsgConditionalReplyOn(t *testing.T) { deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000)) - creator := keepers.Faucet.NewFundedAccount(ctx, deposit...) + creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...) _, _, fred := keyPubAddr() // upload code - codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) + codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil) require.NoError(t, err) // creator instantiates a contract and gives it tokens diff --git a/x/wasm/keeper/test_common.go b/x/wasm/keeper/test_common.go index 8b0f782047..fff90af36b 100644 --- a/x/wasm/keeper/test_common.go +++ b/x/wasm/keeper/test_common.go @@ -1,6 +1,7 @@ package keeper import ( + "bytes" "encoding/binary" "encoding/json" "fmt" @@ -8,7 +9,9 @@ import ( "testing" "time" - "github.com/CosmWasm/wasmd/x/wasm/keeper/testdata" + "github.com/cosmos/cosmos-sdk/x/auth/vesting" + + "github.com/cosmos/cosmos-sdk/types/address" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" @@ -91,6 +94,7 @@ var moduleBasics = module.NewBasicManager( upgrade.AppModuleBasic{}, evidence.AppModuleBasic{}, transfer.AppModuleBasic{}, + vesting.AppModuleBasic{}, ) func MakeTestCodec(t testing.TB) codec.Codec { @@ -162,7 +166,7 @@ func (f *TestFaucet) Fund(parentCtx sdk.Context, receiver sdk.AccAddress, amount f.balance = f.balance.Sub(amounts) } -func (f *TestFaucet) NewFundedAccount(ctx sdk.Context, amounts ...sdk.Coin) sdk.AccAddress { +func (f *TestFaucet) NewFundedRandomAccount(ctx sdk.Context, amounts ...sdk.Coin) sdk.AccAddress { _, _, addr := keyPubAddr() f.Fund(ctx, addr, amounts...) return addr @@ -464,7 +468,7 @@ func handleStoreCode(ctx sdk.Context, k types.ContractOpsKeeper, msg *types.MsgS if err != nil { return nil, sdkerrors.Wrap(err, "sender") } - codeID, err := k.Create(ctx, senderAddr, msg.WASMByteCode, msg.InstantiatePermission) + codeID, _, err := k.Create(ctx, senderAddr, msg.WASMByteCode, msg.InstantiatePermission) if err != nil { return nil, err } @@ -523,6 +527,11 @@ func RandomAccountAddress(_ testing.TB) sdk.AccAddress { return addr } +// DeterministicAccountAddress creates a test address with v repeated to valid address size +func DeterministicAccountAddress(_ testing.TB, v byte) sdk.AccAddress { + return bytes.Repeat([]byte{v}, address.Len) +} + func RandomBech32AccountAddress(t testing.TB) string { return RandomAccountAddress(t).String() } @@ -532,6 +541,7 @@ type ExampleContract struct { Creator crypto.PrivKey CreatorAddr sdk.AccAddress CodeID uint64 + Checksum []byte } func StoreHackatomExampleContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) ExampleContract { @@ -546,11 +556,8 @@ func StoreIBCReflectContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) return StoreExampleContract(t, ctx, keepers, "./testdata/ibc_reflect.wasm") } -func StoreReflectContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) uint64 { - _, _, creatorAddr := keyPubAddr() - codeID, err := keepers.ContractKeeper.Create(ctx, creatorAddr, testdata.ReflectContractWasm(), nil) - require.NoError(t, err) - return codeID +func StoreReflectContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) ExampleContract { + return StoreExampleContract(t, ctx, keepers, "./testdata/reflect.wasm") } func StoreExampleContract(t testing.TB, ctx sdk.Context, keepers TestKeepers, wasmFile string) ExampleContract { @@ -561,9 +568,10 @@ func StoreExampleContract(t testing.TB, ctx sdk.Context, keepers TestKeepers, wa wasmCode, err := os.ReadFile(wasmFile) require.NoError(t, err) - codeID, err := keepers.ContractKeeper.Create(ctx, creatorAddr, wasmCode, nil) + codeID, _, err := keepers.ContractKeeper.Create(ctx, creatorAddr, wasmCode, nil) require.NoError(t, err) - return ExampleContract{anyAmount, creator, creatorAddr, codeID} + hash := keepers.WasmKeeper.GetCodeInfo(ctx, codeID).CodeHash + return ExampleContract{anyAmount, creator, creatorAddr, codeID, hash} } var wasmIdent = []byte("\x00\x61\x73\x6D") @@ -602,9 +610,9 @@ func StoreRandomContractWithAccessConfig( fundAccounts(t, ctx, keepers.AccountKeeper, keepers.BankKeeper, creatorAddr, anyAmount) keepers.WasmKeeper.wasmVM = mock wasmCode := append(wasmIdent, rand.Bytes(10)...) //nolint:gocritic - codeID, err := keepers.ContractKeeper.Create(ctx, creatorAddr, wasmCode, cfg) + codeID, checksum, err := keepers.ContractKeeper.Create(ctx, creatorAddr, wasmCode, cfg) require.NoError(t, err) - exampleContract := ExampleContract{InitialAmount: anyAmount, Creator: creator, CreatorAddr: creatorAddr, CodeID: codeID} + exampleContract := ExampleContract{InitialAmount: anyAmount, Creator: creator, CreatorAddr: creatorAddr, CodeID: codeID, Checksum: checksum} return exampleContract } @@ -615,6 +623,8 @@ type HackatomExampleInstance struct { VerifierAddr sdk.AccAddress Beneficiary crypto.PrivKey BeneficiaryAddr sdk.AccAddress + Label string + Deposit sdk.Coins } // InstantiateHackatomExampleContract load and instantiate the "./testdata/hackatom.wasm" contract @@ -632,7 +642,8 @@ func InstantiateHackatomExampleContract(t testing.TB, ctx sdk.Context, keepers T initialAmount := sdk.NewCoins(sdk.NewInt64Coin("denom", 100)) adminAddr := contract.CreatorAddr - contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, contract.CodeID, contract.CreatorAddr, adminAddr, initMsgBz, "demo contract to query", initialAmount) + label := "demo contract to query" + contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, contract.CodeID, contract.CreatorAddr, adminAddr, initMsgBz, label, initialAmount) require.NoError(t, err) return HackatomExampleInstance{ ExampleContract: contract, @@ -641,6 +652,31 @@ func InstantiateHackatomExampleContract(t testing.TB, ctx sdk.Context, keepers T VerifierAddr: verifierAddr, Beneficiary: beneficiary, BeneficiaryAddr: beneficiaryAddr, + Label: label, + Deposit: initialAmount, + } +} + +type ExampleInstance struct { + ExampleContract + Contract sdk.AccAddress + Label string + Deposit sdk.Coins +} + +// InstantiateReflectExampleContract load and instantiate the "./testdata/reflect.wasm" contract +func InstantiateReflectExampleContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) ExampleInstance { + example := StoreReflectContract(t, ctx, keepers) + initialAmount := sdk.NewCoins(sdk.NewInt64Coin("denom", 100)) + label := "demo contract to query" + contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, example.CodeID, example.CreatorAddr, nil, []byte("{}"), label, initialAmount) + + require.NoError(t, err) + return ExampleInstance{ + ExampleContract: example, + Contract: contractAddr, + Label: label, + Deposit: initialAmount, } } @@ -664,7 +700,7 @@ type IBCReflectExampleInstance struct { // InstantiateIBCReflectContract load and instantiate the "./testdata/ibc_reflect.wasm" contract func InstantiateIBCReflectContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) IBCReflectExampleInstance { - reflectID := StoreReflectContract(t, ctx, keepers) + reflectID := StoreReflectContract(t, ctx, keepers).CodeID ibcReflectID := StoreIBCReflectContract(t, ctx, keepers).CodeID initMsgBz := IBCReflectInitMsg{ diff --git a/x/wasm/module.go b/x/wasm/module.go index a237a910d4..2433fe0219 100644 --- a/x/wasm/module.go +++ b/x/wasm/module.go @@ -111,7 +111,7 @@ type AppModule struct { // module. It should be incremented on each consensus-breaking change // introduced by the module. To avoid wrong/empty versions, the initial version // should be set to 1. -func (AppModule) ConsensusVersion() uint64 { return 1 } +func (AppModule) ConsensusVersion() uint64 { return 2 } // NewAppModule creates a new AppModule object func NewAppModule( @@ -134,6 +134,11 @@ func NewAppModule( func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(keeper.NewDefaultPermissionKeeper(am.keeper))) types.RegisterQueryServer(cfg.QueryServer(), NewQuerier(am.keeper)) + + m := keeper.NewMigrator(am.keeper) + if err := cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2); err != nil { + panic(err) + } } func (am AppModule) LegacyQuerierHandler(amino *codec.LegacyAmino) sdk.Querier { //nolint:staticcheck diff --git a/x/wasm/module_test.go b/x/wasm/module_test.go index 8417225ade..d3530e5a3a 100644 --- a/x/wasm/module_test.go +++ b/x/wasm/module_test.go @@ -1,11 +1,14 @@ package wasm import ( + "bytes" "encoding/json" "fmt" "os" "testing" + "github.com/cosmos/cosmos-sdk/types/address" + "github.com/CosmWasm/wasmd/x/wasm/keeper/testdata" sdk "github.com/cosmos/cosmos-sdk/types" @@ -145,7 +148,8 @@ type state struct { func TestHandleInstantiate(t *testing.T) { data := setupTest(t) - creator := data.faucet.NewFundedAccount(data.ctx, sdk.NewInt64Coin("denom", 100000)) + creator := sdk.AccAddress(bytes.Repeat([]byte{1}, address.Len)) + data.faucet.Fund(data.ctx, creator, sdk.NewInt64Coin("denom", 100000)) h := data.module.Route().Handler() q := data.module.LegacyQuerierHandler(nil) @@ -179,7 +183,7 @@ func TestHandleInstantiate(t *testing.T) { require.NoError(t, err) contractBech32Addr := parseInitResponse(t, res.Data) - require.Equal(t, "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", contractBech32Addr) + require.Equal(t, "cosmos1400ax8h2dxe8ch64sus5sczqdhwv28hpjkkaphpa83he4fhz6k4qcm9kul", contractBech32Addr) // this should be standard x/wasm init event, nothing from contract require.Equal(t, 3, len(res.Events), prettyEvents(res.Events)) require.Equal(t, "message", res.Events[0].Type) @@ -206,8 +210,10 @@ func TestHandleExecute(t *testing.T) { deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) - creator := data.faucet.NewFundedAccount(data.ctx, deposit.Add(deposit...)...) - fred := data.faucet.NewFundedAccount(data.ctx, topUp...) + creator := sdk.AccAddress(bytes.Repeat([]byte{1}, address.Len)) + data.faucet.Fund(data.ctx, creator, sdk.NewInt64Coin("denom", 100000*2)) + + fred := data.faucet.NewFundedRandomAccount(data.ctx, topUp...) h := data.module.Route().Handler() q := data.module.LegacyQuerierHandler(nil) @@ -238,7 +244,7 @@ func TestHandleExecute(t *testing.T) { require.NoError(t, err) contractBech32Addr := parseInitResponse(t, res.Data) - require.Equal(t, "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", contractBech32Addr) + require.Equal(t, "cosmos1400ax8h2dxe8ch64sus5sczqdhwv28hpjkkaphpa83he4fhz6k4qcm9kul", contractBech32Addr) // this should be standard x/wasm message event, init event, plus a bank send event (2), with no custom contract events require.Equal(t, 6, len(res.Events), prettyEvents(res.Events)) require.Equal(t, "message", res.Events[0].Type) @@ -341,8 +347,9 @@ func TestHandleExecuteEscrow(t *testing.T) { deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000)) - creator := data.faucet.NewFundedAccount(data.ctx, deposit.Add(deposit...)...) - fred := data.faucet.NewFundedAccount(data.ctx, topUp...) + creator := sdk.AccAddress(bytes.Repeat([]byte{1}, address.Len)) + data.faucet.Fund(data.ctx, creator, sdk.NewInt64Coin("denom", 100000)) + fred := data.faucet.NewFundedRandomAccount(data.ctx, topUp...) h := data.module.Route().Handler() @@ -370,7 +377,7 @@ func TestHandleExecuteEscrow(t *testing.T) { res, err = h(data.ctx, &initCmd) require.NoError(t, err) contractBech32Addr := parseInitResponse(t, res.Data) - require.Equal(t, "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", contractBech32Addr) + require.Equal(t, "cosmos1400ax8h2dxe8ch64sus5sczqdhwv28hpjkkaphpa83he4fhz6k4qcm9kul", contractBech32Addr) handleMsg := map[string]interface{}{ "release": map[string]interface{}{}, diff --git a/x/wasm/simulation/genesis.go b/x/wasm/simulation/genesis.go index 039a4394c4..5271bd2365 100644 --- a/x/wasm/simulation/genesis.go +++ b/x/wasm/simulation/genesis.go @@ -15,7 +15,6 @@ func RandomizedGenState(simstate *module.SimulationState) { Contracts: nil, Sequences: []types.Sequence{ {IDKey: types.KeyLastCodeID, Value: simstate.Rand.Uint64()}, - {IDKey: types.KeyLastInstanceID, Value: simstate.Rand.Uint64()}, }, GenMsgs: nil, } diff --git a/x/wasm/types/events.go b/x/wasm/types/events.go index 251b95f042..579024ed0c 100644 --- a/x/wasm/types/events.go +++ b/x/wasm/types/events.go @@ -23,6 +23,7 @@ const ( AttributeKeyContractAddr = "_contract_address" AttributeKeyCodeID = "code_id" + AttributeKeyChecksum = "code_checksum" AttributeKeyResultDataHex = "result" AttributeKeyRequiredCapability = "required_capability" ) diff --git a/x/wasm/types/exported_keepers.go b/x/wasm/types/exported_keepers.go index 88f9d980b3..df09957915 100644 --- a/x/wasm/types/exported_keepers.go +++ b/x/wasm/types/exported_keepers.go @@ -26,7 +26,7 @@ type ViewKeeper interface { // ContractOpsKeeper contains mutable operations on a contract. type ContractOpsKeeper interface { // Create uploads and compiles a WASM contract, returning a short identifier for the contract - Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess *AccessConfig) (codeID uint64, err error) + Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess *AccessConfig) (codeID uint64, checksum []byte, err error) // Instantiate creates an instance of a WASM contract Instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.AccAddress, initMsg []byte, label string, deposit sdk.Coins) (sdk.AccAddress, []byte, error) diff --git a/x/wasm/types/keys.go b/x/wasm/types/keys.go index fb636ef4f3..9660360cb8 100644 --- a/x/wasm/types/keys.go +++ b/x/wasm/types/keys.go @@ -32,8 +32,7 @@ var ( PinnedCodeIndexPrefix = []byte{0x07} TXCounterPrefix = []byte{0x08} - KeyLastCodeID = append(SequenceKeyPrefix, []byte("lastCodeId")...) - KeyLastInstanceID = append(SequenceKeyPrefix, []byte("lastContractId")...) + KeyLastCodeID = append(SequenceKeyPrefix, []byte("lastCodeId")...) ) // GetCodeKey constructs the key for retreiving the ID for the WASM code diff --git a/x/wasm/types/proposal.go b/x/wasm/types/proposal.go index 748a03895b..acf30784c9 100644 --- a/x/wasm/types/proposal.go +++ b/x/wasm/types/proposal.go @@ -170,7 +170,7 @@ func (p InstantiateContractProposal) ValidateBasic() error { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "code id is required") } - if err := validateLabel(p.Label); err != nil { + if err := ValidateLabel(p.Label); err != nil { return err } diff --git a/x/wasm/types/tx.go b/x/wasm/types/tx.go index 498dc28266..0c83018df0 100644 --- a/x/wasm/types/tx.go +++ b/x/wasm/types/tx.go @@ -95,7 +95,7 @@ func (msg MsgInstantiateContract) ValidateBasic() error { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "code id is required") } - if err := validateLabel(msg.Label); err != nil { + if err := ValidateLabel(msg.Label); err != nil { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "label is required") } diff --git a/x/wasm/types/tx.pb.go b/x/wasm/types/tx.pb.go index 7976d9e8f0..52d5098de4 100644 --- a/x/wasm/types/tx.pb.go +++ b/x/wasm/types/tx.pb.go @@ -87,6 +87,8 @@ var xxx_messageInfo_MsgStoreCode proto.InternalMessageInfo type MsgStoreCodeResponse struct { // CodeID is the reference to the stored WASM code CodeID uint64 `protobuf:"varint,1,opt,name=code_id,json=codeId,proto3" json:"code_id,omitempty"` + // Checksum is the sha256 hash of the stored code + Checksum []byte `protobuf:"bytes,2,opt,name=checksum,proto3" json:"checksum,omitempty"` } func (m *MsgStoreCodeResponse) Reset() { *m = MsgStoreCodeResponse{} } @@ -611,55 +613,55 @@ func init() { func init() { proto.RegisterFile("cosmwasm/wasm/v1/tx.proto", fileDescriptor_4f74d82755520264) } var fileDescriptor_4f74d82755520264 = []byte{ - // 759 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xcd, 0x6e, 0xd3, 0x4a, - 0x14, 0x8e, 0x1b, 0x27, 0x4d, 0x4e, 0x73, 0x7b, 0x23, 0xdf, 0x36, 0x37, 0xf5, 0xbd, 0x72, 0xa2, - 0x80, 0x8a, 0x17, 0x60, 0x37, 0x45, 0x62, 0xc3, 0xaa, 0x49, 0x59, 0xb4, 0x92, 0x11, 0x72, 0x55, - 0x2a, 0xd8, 0x44, 0x13, 0x7b, 0x6a, 0x2c, 0x1a, 0x4f, 0xf0, 0x71, 0x9b, 0xf6, 0x25, 0x10, 0x3b, - 0xde, 0x81, 0xb7, 0x60, 0xd7, 0x15, 0xea, 0x06, 0x89, 0x55, 0x80, 0xf4, 0x2d, 0x58, 0x21, 0xff, - 0xa5, 0x6e, 0xea, 0xa4, 0x41, 0x88, 0x4d, 0x32, 0xc7, 0xf3, 0x7d, 0xe7, 0xcc, 0xf9, 0xf4, 0xcd, - 0x19, 0x58, 0x33, 0x18, 0xf6, 0x06, 0x04, 0x7b, 0x6a, 0xf0, 0x73, 0xd2, 0x54, 0xbd, 0x53, 0xa5, - 0xef, 0x32, 0x8f, 0x09, 0xe5, 0x78, 0x4b, 0x09, 0x7e, 0x4e, 0x9a, 0xa2, 0xe4, 0x7f, 0x61, 0xa8, - 0x76, 0x09, 0x52, 0xf5, 0xa4, 0xd9, 0xa5, 0x1e, 0x69, 0xaa, 0x06, 0xb3, 0x9d, 0x90, 0x21, 0xae, - 0x58, 0xcc, 0x62, 0xc1, 0x52, 0xf5, 0x57, 0xd1, 0xd7, 0xff, 0x6f, 0x96, 0x38, 0xeb, 0x53, 0x0c, - 0x77, 0x1b, 0x1f, 0x39, 0x28, 0x69, 0x68, 0xed, 0x79, 0xcc, 0xa5, 0x6d, 0x66, 0x52, 0xa1, 0x02, - 0x79, 0xa4, 0x8e, 0x49, 0xdd, 0x2a, 0x57, 0xe7, 0xe4, 0xa2, 0x1e, 0x45, 0xc2, 0x23, 0x58, 0xf6, - 0xf9, 0x9d, 0xee, 0x99, 0x47, 0x3b, 0x06, 0x33, 0x69, 0x75, 0xa1, 0xce, 0xc9, 0xa5, 0x56, 0x79, - 0x34, 0xac, 0x95, 0x0e, 0xb6, 0xf6, 0xb4, 0xd6, 0x99, 0x17, 0x64, 0xd0, 0x4b, 0x3e, 0x2e, 0x8e, - 0x84, 0x7d, 0xa8, 0xd8, 0x0e, 0x7a, 0xc4, 0xf1, 0x6c, 0xe2, 0xd1, 0x4e, 0x9f, 0xba, 0x3d, 0x1b, - 0xd1, 0x66, 0x4e, 0x35, 0x57, 0xe7, 0xe4, 0xa5, 0x4d, 0x49, 0x99, 0xec, 0x53, 0xd9, 0x32, 0x0c, - 0x8a, 0xd8, 0x66, 0xce, 0xa1, 0x6d, 0xe9, 0xab, 0x09, 0xf6, 0xb3, 0x31, 0x79, 0x97, 0x2f, 0x64, - 0xcb, 0xfc, 0x2e, 0x5f, 0xe0, 0xcb, 0xb9, 0xc6, 0x63, 0x58, 0x49, 0xb6, 0xa0, 0x53, 0xec, 0x33, - 0x07, 0xa9, 0x70, 0x07, 0x16, 0xfd, 0x83, 0x76, 0x6c, 0x33, 0xe8, 0x85, 0x6f, 0xc1, 0x68, 0x58, - 0xcb, 0xfb, 0x90, 0x9d, 0x6d, 0x3d, 0xef, 0x6f, 0xed, 0x98, 0x8d, 0xb7, 0x0b, 0x50, 0xd1, 0xd0, - 0xda, 0xb9, 0xaa, 0xd2, 0x66, 0x8e, 0xe7, 0x12, 0xc3, 0x9b, 0x2a, 0xc5, 0x0a, 0xe4, 0x88, 0xd9, - 0xb3, 0x9d, 0x40, 0x81, 0xa2, 0x1e, 0x06, 0xc9, 0x6a, 0xd9, 0x69, 0xd5, 0x7c, 0xea, 0x11, 0xe9, - 0xd2, 0xa3, 0x2a, 0x1f, 0x52, 0x83, 0x40, 0x90, 0x21, 0xdb, 0x43, 0x2b, 0x10, 0xa4, 0xd4, 0xaa, - 0xfc, 0x18, 0xd6, 0x04, 0x9d, 0x0c, 0xe2, 0x63, 0x68, 0x14, 0x91, 0x58, 0x54, 0xf7, 0x21, 0x02, - 0x81, 0xdc, 0xe1, 0xb1, 0x63, 0x62, 0x35, 0x5f, 0xcf, 0xca, 0x4b, 0x9b, 0x6b, 0x4a, 0x68, 0x09, - 0xc5, 0xb7, 0x84, 0x12, 0x59, 0x42, 0x69, 0x33, 0xdb, 0x69, 0x6d, 0x9c, 0x0f, 0x6b, 0x99, 0x0f, - 0x5f, 0x6b, 0xb2, 0x65, 0x7b, 0xaf, 0x8e, 0xbb, 0x8a, 0xc1, 0x7a, 0x6a, 0xe4, 0x9f, 0xf0, 0xef, - 0x01, 0x9a, 0xaf, 0x23, 0x2b, 0xf8, 0x04, 0xd4, 0xc3, 0xcc, 0x8d, 0xa7, 0x20, 0xa5, 0xeb, 0x31, - 0xd6, 0xb5, 0x0a, 0x8b, 0xc4, 0x34, 0x5d, 0x8a, 0x18, 0x09, 0x13, 0x87, 0x82, 0x00, 0xbc, 0x49, - 0x3c, 0x12, 0x5a, 0x43, 0x0f, 0xd6, 0x8d, 0xcf, 0x1c, 0x08, 0x1a, 0x5a, 0x4f, 0x4e, 0xa9, 0x71, - 0x3c, 0x87, 0xb8, 0x22, 0x14, 0x8c, 0x08, 0x13, 0xe9, 0x3b, 0x8e, 0x63, 0x9d, 0xb2, 0xbf, 0xa0, - 0x53, 0xee, 0x8f, 0xe9, 0xb4, 0x01, 0xe2, 0xcd, 0xb6, 0xc6, 0x1a, 0xc5, 0x4a, 0x70, 0x09, 0x25, - 0xde, 0x87, 0x4a, 0x68, 0xb6, 0xe5, 0x92, 0xdf, 0x54, 0x62, 0x2e, 0xb3, 0x45, 0x72, 0xf1, 0xb7, - 0xca, 0x15, 0xf5, 0x32, 0x71, 0xb0, 0x99, 0xbd, 0x10, 0x58, 0xd6, 0xd0, 0xda, 0xef, 0x9b, 0xc4, - 0xa3, 0x5b, 0x81, 0xff, 0xa7, 0xb5, 0xf1, 0x1f, 0x14, 0x1d, 0x3a, 0xe8, 0x24, 0x6f, 0x4c, 0xc1, - 0xa1, 0x83, 0x90, 0x94, 0xec, 0x31, 0x7b, 0xbd, 0xc7, 0x46, 0x35, 0xb8, 0x98, 0x89, 0x12, 0xf1, - 0x81, 0x1a, 0x6d, 0xf8, 0x4b, 0x43, 0xab, 0x7d, 0x44, 0x89, 0x3b, 0xbb, 0xf6, 0xac, 0xf4, 0xff, - 0xc2, 0xea, 0xb5, 0x24, 0x71, 0xf6, 0xcd, 0x4f, 0x3c, 0x64, 0x35, 0xb4, 0x84, 0x3d, 0x28, 0x5e, - 0x8d, 0xc5, 0x94, 0x31, 0x95, 0x9c, 0x39, 0xe2, 0xfa, 0xec, 0xfd, 0xb1, 0x96, 0x6f, 0xe0, 0x9f, - 0xb4, 0x51, 0x23, 0xa7, 0xd2, 0x53, 0x90, 0xe2, 0xc6, 0xbc, 0xc8, 0x71, 0x49, 0x0a, 0x7f, 0x4f, - 0x5e, 0xbe, 0xbb, 0xa9, 0x49, 0x26, 0x50, 0xe2, 0xfd, 0x79, 0x50, 0xc9, 0x32, 0x93, 0xce, 0x4e, - 0x2f, 0x33, 0x81, 0x9a, 0x52, 0x66, 0x9a, 0x19, 0x5f, 0xc0, 0x52, 0xd2, 0x75, 0xf5, 0x54, 0x72, - 0x02, 0x21, 0xca, 0xb7, 0x21, 0xc6, 0xa9, 0x9f, 0x03, 0x24, 0x3c, 0x55, 0x4b, 0xe5, 0x5d, 0x01, - 0xc4, 0x7b, 0xb7, 0x00, 0xe2, 0xbc, 0xad, 0xed, 0xf3, 0xef, 0x52, 0xe6, 0x7c, 0x24, 0x71, 0x17, - 0x23, 0x89, 0xfb, 0x36, 0x92, 0xb8, 0x77, 0x97, 0x52, 0xe6, 0xe2, 0x52, 0xca, 0x7c, 0xb9, 0x94, - 0x32, 0x2f, 0xd7, 0x13, 0x83, 0xa7, 0xcd, 0xb0, 0x77, 0x10, 0x3f, 0xd5, 0xa6, 0x7a, 0x1a, 0x3e, - 0xd9, 0xc1, 0xf0, 0xe9, 0xe6, 0x83, 0x07, 0xfb, 0xe1, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x56, - 0xd2, 0x4b, 0x17, 0x33, 0x08, 0x00, 0x00, + // 766 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xcb, 0x6e, 0xd3, 0x40, + 0x14, 0x8d, 0x1b, 0x27, 0x4d, 0x6e, 0x43, 0x89, 0x4c, 0x1a, 0x52, 0x83, 0x9c, 0x28, 0xa0, 0xe2, + 0x05, 0xd8, 0x4d, 0x91, 0xd8, 0x37, 0x29, 0x8b, 0x56, 0x32, 0x42, 0xae, 0x4a, 0x05, 0x9b, 0x68, + 0x62, 0x4f, 0x5d, 0xab, 0xb5, 0x27, 0x78, 0x9c, 0xa6, 0xfd, 0x09, 0xc4, 0x8e, 0x7f, 0xe0, 0x2f, + 0xd8, 0x75, 0x85, 0xba, 0x41, 0x62, 0x15, 0x20, 0xfd, 0x0b, 0x56, 0xc8, 0xcf, 0xba, 0xa9, 0x93, + 0x06, 0x21, 0x36, 0xce, 0xdc, 0x99, 0x73, 0x5f, 0x27, 0x67, 0xee, 0xc0, 0xaa, 0x46, 0xa8, 0x35, + 0x44, 0xd4, 0x92, 0xfd, 0xcf, 0x49, 0x4b, 0x76, 0x4f, 0xa5, 0xbe, 0x43, 0x5c, 0xc2, 0x95, 0xa3, + 0x23, 0xc9, 0xff, 0x9c, 0xb4, 0x78, 0xc1, 0xdb, 0x21, 0x54, 0xee, 0x21, 0x8a, 0xe5, 0x93, 0x56, + 0x0f, 0xbb, 0xa8, 0x25, 0x6b, 0xc4, 0xb4, 0x03, 0x0f, 0xbe, 0x62, 0x10, 0x83, 0xf8, 0x4b, 0xd9, + 0x5b, 0x85, 0xbb, 0x0f, 0x6f, 0xa6, 0x38, 0xeb, 0x63, 0x1a, 0x9c, 0x36, 0xbf, 0x30, 0x50, 0x52, + 0xa8, 0xb1, 0xeb, 0x12, 0x07, 0x77, 0x88, 0x8e, 0xb9, 0x2a, 0xe4, 0x29, 0xb6, 0x75, 0xec, 0xd4, + 0x98, 0x06, 0x23, 0x16, 0xd5, 0xd0, 0xe2, 0x5e, 0xc0, 0xb2, 0xe7, 0xdf, 0xed, 0x9d, 0xb9, 0xb8, + 0xab, 0x11, 0x1d, 0xd7, 0x16, 0x1a, 0x8c, 0x58, 0x6a, 0x97, 0xc7, 0xa3, 0x7a, 0x69, 0x7f, 0x73, + 0x57, 0x69, 0x9f, 0xb9, 0x7e, 0x04, 0xb5, 0xe4, 0xe1, 0x22, 0x8b, 0xdb, 0x83, 0xaa, 0x69, 0x53, + 0x17, 0xd9, 0xae, 0x89, 0x5c, 0xdc, 0xed, 0x63, 0xc7, 0x32, 0x29, 0x35, 0x89, 0x5d, 0xcb, 0x35, + 0x18, 0x71, 0x69, 0x43, 0x90, 0x26, 0xfb, 0x94, 0x36, 0x35, 0x0d, 0x53, 0xda, 0x21, 0xf6, 0x81, + 0x69, 0xa8, 0x2b, 0x09, 0xef, 0xd7, 0xb1, 0xf3, 0x0e, 0x5b, 0xc8, 0x96, 0xd9, 0x1d, 0xb6, 0xc0, + 0x96, 0x73, 0xcd, 0x7d, 0xa8, 0x24, 0x5b, 0x50, 0x31, 0xed, 0x13, 0x9b, 0x62, 0xee, 0x11, 0x2c, + 0x7a, 0x85, 0x76, 0x4d, 0xdd, 0xef, 0x85, 0x6d, 0xc3, 0x78, 0x54, 0xcf, 0x7b, 0x90, 0xed, 0x2d, + 0x35, 0xef, 0x1d, 0x6d, 0xeb, 0x1c, 0x0f, 0x05, 0xed, 0x10, 0x6b, 0x47, 0x74, 0x60, 0x05, 0x1d, + 0xa9, 0xb1, 0xdd, 0xfc, 0xb0, 0x00, 0x55, 0x85, 0x1a, 0xdb, 0x57, 0x15, 0x74, 0x88, 0xed, 0x3a, + 0x48, 0x73, 0xa7, 0xd2, 0x54, 0x81, 0x1c, 0xd2, 0x2d, 0xd3, 0xf6, 0x63, 0x15, 0xd5, 0xc0, 0x48, + 0x56, 0x92, 0x9d, 0x5a, 0x49, 0x05, 0x72, 0xc7, 0xa8, 0x87, 0x8f, 0x6b, 0x6c, 0xe0, 0xea, 0x1b, + 0x9c, 0x08, 0x59, 0x8b, 0x1a, 0x3e, 0x59, 0xa5, 0x76, 0xf5, 0xf7, 0xa8, 0xce, 0xa9, 0x68, 0x18, + 0x95, 0xa1, 0x60, 0x4a, 0x91, 0x81, 0x55, 0x0f, 0xc2, 0x21, 0xc8, 0x1d, 0x0c, 0x6c, 0x9d, 0xd6, + 0xf2, 0x8d, 0xac, 0xb8, 0xb4, 0xb1, 0x2a, 0x05, 0x72, 0x91, 0x3c, 0xb9, 0x48, 0xa1, 0x5c, 0xa4, + 0x0e, 0x31, 0xed, 0xf6, 0xfa, 0xf9, 0xa8, 0x9e, 0xf9, 0xfc, 0xa3, 0x2e, 0x1a, 0xa6, 0x7b, 0x38, + 0xe8, 0x49, 0x1a, 0xb1, 0xe4, 0x50, 0x5b, 0xc1, 0xcf, 0x33, 0xaa, 0x1f, 0x85, 0x32, 0xf1, 0x1c, + 0xa8, 0x1a, 0x44, 0x6e, 0xbe, 0x02, 0x21, 0x9d, 0x8f, 0x98, 0xf3, 0x1a, 0x2c, 0x22, 0x5d, 0x77, + 0x30, 0xa5, 0x21, 0x31, 0x91, 0xc9, 0x71, 0xc0, 0xea, 0xc8, 0x45, 0x21, 0xc9, 0xfe, 0xba, 0xf9, + 0x8d, 0x01, 0x4e, 0xa1, 0xc6, 0xcb, 0x53, 0xac, 0x0d, 0xe6, 0x20, 0xd7, 0xfb, 0xaf, 0x42, 0x4c, + 0xc8, 0x6f, 0x6c, 0x47, 0x3c, 0x65, 0xff, 0x82, 0xa7, 0xdc, 0x7f, 0xe3, 0x69, 0x1d, 0xf8, 0x9b, + 0x6d, 0xc5, 0x1c, 0x45, 0x4c, 0x30, 0x09, 0x26, 0x3e, 0x05, 0x4c, 0x28, 0xa6, 0xe1, 0xa0, 0x7f, + 0x64, 0x62, 0x2e, 0xb1, 0x85, 0x74, 0xb1, 0xb7, 0xd2, 0x15, 0xf6, 0x32, 0x51, 0xd8, 0xcc, 0x5e, + 0x10, 0x2c, 0x2b, 0xd4, 0xd8, 0xeb, 0xeb, 0xc8, 0xc5, 0x9b, 0xbe, 0xfe, 0xa7, 0xb5, 0xf1, 0x00, + 0x8a, 0x36, 0x1e, 0x76, 0x93, 0x37, 0xa6, 0x60, 0xe3, 0x61, 0xe0, 0x94, 0xec, 0x31, 0x7b, 0xbd, + 0xc7, 0x66, 0xcd, 0xbf, 0x98, 0x89, 0x14, 0x51, 0x41, 0xcd, 0x0e, 0xdc, 0x51, 0xa8, 0xd1, 0x39, + 0xc6, 0xc8, 0x99, 0x9d, 0x7b, 0x56, 0xf8, 0xfb, 0xb0, 0x72, 0x2d, 0x48, 0x14, 0x7d, 0xe3, 0x2b, + 0x0b, 0x59, 0x85, 0x1a, 0xdc, 0x2e, 0x14, 0xaf, 0x46, 0x66, 0xca, 0x08, 0x4b, 0xce, 0x23, 0x7e, + 0x6d, 0xf6, 0x79, 0xcc, 0xe5, 0x7b, 0xb8, 0x97, 0x36, 0x6a, 0xc4, 0x54, 0xf7, 0x14, 0x24, 0xbf, + 0x3e, 0x2f, 0x32, 0x4e, 0x89, 0xe1, 0xee, 0xe4, 0xe5, 0x7b, 0x9c, 0x1a, 0x64, 0x02, 0xc5, 0x3f, + 0x9d, 0x07, 0x95, 0x4c, 0x33, 0xa9, 0xec, 0xf4, 0x34, 0x13, 0xa8, 0x29, 0x69, 0xa6, 0x89, 0xf1, + 0x2d, 0x2c, 0x25, 0x55, 0xd7, 0x48, 0x75, 0x4e, 0x20, 0x78, 0xf1, 0x36, 0x44, 0x1c, 0xfa, 0x0d, + 0x40, 0x42, 0x53, 0xf5, 0x54, 0xbf, 0x2b, 0x00, 0xff, 0xe4, 0x16, 0x40, 0x14, 0xb7, 0xbd, 0x75, + 0xfe, 0x4b, 0xc8, 0x9c, 0x8f, 0x05, 0xe6, 0x62, 0x2c, 0x30, 0x3f, 0xc7, 0x02, 0xf3, 0xf1, 0x52, + 0xc8, 0x5c, 0x5c, 0x0a, 0x99, 0xef, 0x97, 0x42, 0xe6, 0xdd, 0x5a, 0x62, 0xf0, 0x74, 0x08, 0xb5, + 0xf6, 0xa3, 0x67, 0x5c, 0x97, 0x4f, 0x83, 0xe7, 0xdc, 0x1f, 0x3e, 0xbd, 0xbc, 0xff, 0x98, 0x3f, + 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0xef, 0x6b, 0xa0, 0xa3, 0x4f, 0x08, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1007,6 +1009,13 @@ func (m *MsgStoreCodeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Checksum) > 0 { + i -= len(m.Checksum) + copy(dAtA[i:], m.Checksum) + i = encodeVarintTx(dAtA, i, uint64(len(m.Checksum))) + i-- + dAtA[i] = 0x12 + } if m.CodeID != 0 { i = encodeVarintTx(dAtA, i, uint64(m.CodeID)) i-- @@ -1458,6 +1467,10 @@ func (m *MsgStoreCodeResponse) Size() (n int) { if m.CodeID != 0 { n += 1 + sovTx(uint64(m.CodeID)) } + l = len(m.Checksum) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -1854,6 +1867,40 @@ func (m *MsgStoreCodeResponse) Unmarshal(dAtA []byte) error { break } } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Checksum", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Checksum = append(m.Checksum[:0], dAtA[iNdEx:postIndex]...) + if m.Checksum == nil { + m.Checksum = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/x/wasm/types/tx_test.go b/x/wasm/types/tx_test.go index b0ea43e969..b59ad7ce40 100644 --- a/x/wasm/types/tx_test.go +++ b/x/wasm/types/tx_test.go @@ -84,6 +84,7 @@ func TestInstantiateContractValidation(t *testing.T) { badAddress := bad.String() // proper address size goodAddress := sdk.AccAddress(make([]byte, 20)).String() + sdk.GetConfig().SetAddressVerifier(VerifyAddressLen()) cases := map[string]struct { msg MsgInstantiateContract diff --git a/x/wasm/types/types.go b/x/wasm/types/types.go index 3712af192c..6453c5f1d0 100644 --- a/x/wasm/types/types.go +++ b/x/wasm/types/types.go @@ -88,7 +88,7 @@ func (c *ContractInfo) ValidateBasic() error { return sdkerrors.Wrap(err, "admin") } } - if err := validateLabel(c.Label); err != nil { + if err := ValidateLabel(c.Label); err != nil { return sdkerrors.Wrap(err, "label") } if c.Extension == nil { diff --git a/x/wasm/types/validation.go b/x/wasm/types/validation.go index 4984fa28dd..0619c7f082 100644 --- a/x/wasm/types/validation.go +++ b/x/wasm/types/validation.go @@ -22,7 +22,8 @@ func validateWasmCode(s []byte) error { return nil } -func validateLabel(label string) error { +// ValidateLabel ensure label constraints +func ValidateLabel(label string) error { if label == "" { return sdkerrors.Wrap(ErrEmpty, "is required") }