diff --git a/Dockerfile b/Dockerfile index b493b224c11..08efd1b0682 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,8 +15,8 @@ WORKDIR /code COPY . /code/ # See https://github.com/CosmWasm/wasmvm/releases -ADD https://github.com/CosmWasm/wasmvm/releases/download/v0.14.0-beta1/libwasmvm_muslc.a /lib/libwasmvm_muslc.a -RUN sha256sum /lib/libwasmvm_muslc.a | grep b69cf9ffbdfb2f1bd1e6f730ecee1eb0d06a1473840a24709aec7a7df5907d45 +ADD https://github.com/CosmWasm/wasmvm/releases/download/v0.14.0-beta3/libwasmvm_muslc.a /lib/libwasmvm_muslc.a +RUN sha256sum /lib/libwasmvm_muslc.a | grep adea8f977601daa8daa9885e02b31ca6dd0ab6d4dbbd8ba2ccfa447ffebda37c # force it to use static lib (from above) not standard libgo_cosmwasm.so file RUN LEDGER_ENABLED=false BUILD_TAGS=muslc make build diff --git a/app/app.go b/app/app.go index bb32d3c531a..94924455c13 100644 --- a/app/app.go +++ b/app/app.go @@ -181,6 +181,7 @@ var ( stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, govtypes.ModuleName: {authtypes.Burner}, ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + wasm.ModuleName: {authtypes.Burner}, } // module accounts that are allowed to receive tokens diff --git a/go.mod b/go.mod index e7641ed9308..52a5abc6372 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/CosmWasm/wasmd go 1.15 require ( - github.com/CosmWasm/wasmvm v0.14.0-beta1 + github.com/CosmWasm/wasmvm v0.14.0-beta3 github.com/cosmos/cosmos-sdk v0.42.4 github.com/cosmos/iavl v0.15.3 github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b diff --git a/go.sum b/go.sum index 96ab2307c98..2ac113ef1ae 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= -github.com/CosmWasm/wasmvm v0.14.0-beta1 h1:ASqXB/2D8CEBmAI/uljRw6eEMbeKXPQtL/wZzKXZGGA= -github.com/CosmWasm/wasmvm v0.14.0-beta1/go.mod h1:Id107qllDJyJjVQQsKMOy2YYF98sqPJ2t+jX1QES40A= +github.com/CosmWasm/wasmvm v0.14.0-beta3 h1:HN1+HrC2kgO/V4voGdOWrN1sdUoTnSoLuSrBXbDVnbY= +github.com/CosmWasm/wasmvm v0.14.0-beta3/go.mod h1:Id107qllDJyJjVQQsKMOy2YYF98sqPJ2t+jX1QES40A= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= @@ -101,8 +101,6 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cosmos/cosmos-sdk v0.42.3 h1:VFYq7spDBBIlygAxwcI79Xh2JuIrG1ZCPKpvqKghIZs= -github.com/cosmos/cosmos-sdk v0.42.3/go.mod h1:xiLp1G8mumj82S5KLJGCAyeAlD+7VNomg/aRSJV12yk= github.com/cosmos/cosmos-sdk v0.42.4 h1:yaD4PyOx0LnyfiWasC5egg1U76lT83GRxjJjupPo7Gk= github.com/cosmos/cosmos-sdk v0.42.4/go.mod h1:I1Zw1zmU4rA/NITaakTb71pXQnQrWyFBhqo3WSeg0vA= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= @@ -570,8 +568,6 @@ github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoM github.com/tendermint/tendermint v0.34.0-rc4/go.mod h1:yotsojf2C1QBOw4dZrTcxbyxmPUrT4hNuOQWX9XUwB4= github.com/tendermint/tendermint v0.34.0-rc6/go.mod h1:ugzyZO5foutZImv0Iyx/gOFCX6mjJTgbLHTwi17VDVg= github.com/tendermint/tendermint v0.34.0/go.mod h1:Aj3PIipBFSNO21r+Lq3TtzQ+uKESxkbA3yo/INM4QwQ= -github.com/tendermint/tendermint v0.34.8 h1:PMWgUx47FrNTsfhxCWzoiIlVAC1SE9+WBlnsF9oQW0I= -github.com/tendermint/tendermint v0.34.8/go.mod h1:JVuu3V1ZexOaZG8VJMRl8lnfrGw6hEB2TVnoUwKRbss= github.com/tendermint/tendermint v0.34.9 h1:9P2MXDEPOcPW0NBcHQ/HDSfvczZm+q5nUUw7AZ6f1Vc= github.com/tendermint/tendermint v0.34.9/go.mod h1:kl4Z1JwGx1I+u1SXIzMDy7Z3T8LiMeCAOnzNn6AIMT4= github.com/tendermint/tm-db v0.5.1/go.mod h1:g92zWjHpCYlEvQXvy9M168Su8V1IBEeawpXVVBaK4f4= diff --git a/x/wasm/genesis_test.go b/x/wasm/genesis_test.go index 6d63c1fcffc..5beeb54b5d1 100644 --- a/x/wasm/genesis_test.go +++ b/x/wasm/genesis_test.go @@ -68,9 +68,9 @@ func TestInitGenesis(t *testing.T) { assertContractList(t, q, data.ctx, 1, []string{contractBech32Addr}) assertContractInfo(t, q, data.ctx, contractBech32Addr, 1, creator) assertContractState(t, q, data.ctx, contractBech32Addr, state{ - Verifier: []byte(fred), - Beneficiary: []byte(bob), - Funder: []byte(creator), + Verifier: fred.String(), + Beneficiary: bob.String(), + Funder: creator.String(), }) // export into genstate @@ -90,8 +90,8 @@ func TestInitGenesis(t *testing.T) { assertContractList(t, q2, newData.ctx, 1, []string{contractBech32Addr}) assertContractInfo(t, q2, newData.ctx, contractBech32Addr, 1, creator) assertContractState(t, q2, newData.ctx, contractBech32Addr, state{ - Verifier: []byte(fred), - Beneficiary: []byte(bob), - Funder: []byte(creator), + Verifier: fred.String(), + Beneficiary: bob.String(), + Funder: creator.String(), }) } diff --git a/x/wasm/keeper/handler_plugin.go b/x/wasm/keeper/handler_plugin.go index daf38339eb0..5a2341d6082 100644 --- a/x/wasm/keeper/handler_plugin.go +++ b/x/wasm/keeper/handler_plugin.go @@ -24,7 +24,15 @@ type SDKMessageHandler struct { encoders msgEncoder } -func NewDefaultMessageHandler(router sdk.Router, channelKeeper types.ChannelKeeper, capabilityKeeper types.CapabilityKeeper, unpacker codectypes.AnyUnpacker, portSource types.ICS20TransferPortSource, customEncoders ...*MessageEncoders) Messenger { +func NewDefaultMessageHandler( + router sdk.Router, + channelKeeper types.ChannelKeeper, + capabilityKeeper types.CapabilityKeeper, + bankKeeper types.Burner, + unpacker codectypes.AnyUnpacker, + portSource types.ICS20TransferPortSource, + customEncoders ...*MessageEncoders, +) Messenger { encoders := DefaultEncoders(unpacker, portSource) for _, e := range customEncoders { encoders = encoders.Merge(e) @@ -32,6 +40,7 @@ func NewDefaultMessageHandler(router sdk.Router, channelKeeper types.ChannelKeep return NewMessageHandlerChain( NewSDKMessageHandler(router, encoders), NewIBCRawPacketHandler(channelKeeper, capabilityKeeper), + NewBurnCoinMessageHandler(bankKeeper), ) } @@ -168,3 +177,33 @@ func (h IBCRawPacketHandler) DispatchMsg(ctx sdk.Context, _ sdk.AccAddress, cont ) return nil, nil, h.channelKeeper.SendPacket(ctx, channelCap, packet) } + +var _ Messenger = MessageHandlerFunc(nil) + +// MessageHandlerFunc is a helper to construct simple function based message handler +type MessageHandlerFunc func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) + +func (m MessageHandlerFunc) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { + return m(ctx, contractAddr, contractIBCPortID, msg) +} + +// NewBurnCoinMessageHandler handles wasmvm.BurnMsg messages +func NewBurnCoinMessageHandler(burner types.Burner) MessageHandlerFunc { + return func(ctx sdk.Context, contractAddr sdk.AccAddress, _ string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { + if msg.Bank != nil && msg.Bank.Burn != nil { + coins, err := convertWasmCoinsToSdkCoins(msg.Bank.Burn.Amount) + if err != nil { + return nil, nil, err + } + if err := burner.SendCoinsFromAccountToModule(ctx, contractAddr, types.ModuleName, coins); err != nil { + return nil, nil, sdkerrors.Wrap(err, "transfer to module") + } + if err := burner.BurnCoins(ctx, types.ModuleName, coins); err != nil { + return nil, nil, sdkerrors.Wrap(err, "burn coins") + } + moduleLogger(ctx).Info("Burned", "amount", coins) + return nil, nil, nil + } + return nil, nil, types.ErrUnknownMsg + } +} diff --git a/x/wasm/keeper/handler_plugin_test.go b/x/wasm/keeper/handler_plugin_test.go index e46989f0f7b..c56be2c9529 100644 --- a/x/wasm/keeper/handler_plugin_test.go +++ b/x/wasm/keeper/handler_plugin_test.go @@ -4,10 +4,12 @@ import ( "encoding/json" "github.com/CosmWasm/wasmd/x/wasm/keeper/wasmtesting" "github.com/CosmWasm/wasmd/x/wasm/types" + wasmvm "github.com/CosmWasm/wasmvm" wasmvmtypes "github.com/CosmWasm/wasmvm/types" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" @@ -306,3 +308,87 @@ func TestIBCRawPacketHandler(t *testing.T) { }) } } + +func TestBurnCoinMessageHandlerIntegration(t *testing.T) { + // testing via full keeper setup so that we are confident the + // module permissions are set correct and no other handler + // picks the message in the default handler chain + ctx, keepers := CreateDefaultTestInput(t) + k := keepers.WasmKeeper + + before, err := keepers.BankKeeper.TotalSupply(sdk.WrapSDKContext(ctx), &banktypes.QueryTotalSupplyRequest{}) + require.NoError(t, err) + example := InstantiateHackatomExampleContract(t, ctx, keepers) // with deposit of 100 stake + + specs := map[string]struct { + msg wasmvmtypes.BurnMsg + expErr bool + }{ + "all good": { + msg: wasmvmtypes.BurnMsg{ + Amount: wasmvmtypes.Coins{{ + Denom: "denom", + Amount: "100", + }}, + }, + }, + "not enough funds in contract": { + msg: wasmvmtypes.BurnMsg{ + Amount: wasmvmtypes.Coins{{ + Denom: "denom", + Amount: "101", + }}, + }, + expErr: true, + }, + "zero amount rejected": { + msg: wasmvmtypes.BurnMsg{ + Amount: wasmvmtypes.Coins{{ + Denom: "denom", + Amount: "0", + }}, + }, + expErr: true, + }, + "unknown denom - insufficient funds": { + msg: wasmvmtypes.BurnMsg{ + Amount: wasmvmtypes.Coins{{ + Denom: "unknown", + Amount: "1", + }}, + }, + expErr: true, + }, + } + parentCtx := ctx + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + ctx, _ = parentCtx.CacheContext() + k.wasmVM = &wasmtesting.MockWasmer{ExecuteFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64) (*wasmvmtypes.Response, uint64, error) { + return &wasmvmtypes.Response{Messages: []wasmvmtypes.CosmosMsg{ + {Bank: &wasmvmtypes.BankMsg{Burn: &spec.msg}}, + }, + }, 0, nil + }} + + // when + _, err = k.execute(ctx, example.Contract, example.CreatorAddr, nil, nil) + + // then + if spec.expErr { + require.Error(t, err) + return + } + require.NoError(t, err) + + // and total supply reduced by burned amount + after, err := keepers.BankKeeper.TotalSupply(sdk.WrapSDKContext(ctx), &banktypes.QueryTotalSupplyRequest{}) + require.NoError(t, err) + diff := before.Supply.Sub(after.Supply) + assert.Equal(t, sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(100))), diff) + }) + } + + // test cases: + // not enough money to burn +} diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index 43b4e4be3d1..74b4cc30c33 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -121,7 +121,7 @@ func NewKeeper( bank: NewBankCoinTransferrer(bankKeeper), portKeeper: portKeeper, capabilityKeeper: capabilityKeeper, - messenger: NewDefaultMessageHandler(router, channelKeeper, capabilityKeeper, cdc, portSource), + messenger: NewDefaultMessageHandler(router, channelKeeper, capabilityKeeper, bankKeeper, cdc, portSource), queryGasLimit: wasmConfig.SmartQueryGasLimit, paramSpace: paramSpace, } @@ -831,6 +831,15 @@ func (k Keeper) dispatchSubmessages(ctx sdk.Context, contractAddr sdk.AccAddress } // on failure, revert state from sandbox, and ignore events (just skip doing the above) + // we only callback if requested. Short-circuit here the two cases we don't want to + if msg.ReplyOn == wasmvmtypes.ReplySuccess && err != nil { + return err + } + if msg.ReplyOn == wasmvmtypes.ReplyError && err == nil { + return nil + } + + // otherwise, we create a SubcallResult and pass it into the calling contract var result wasmvmtypes.SubcallResult if err == nil { // just take the first one for now if there are multiple sub-sdk messages diff --git a/x/wasm/keeper/keeper_test.go b/x/wasm/keeper/keeper_test.go index 9302fae123b..582a2eaf026 100644 --- a/x/wasm/keeper/keeper_test.go +++ b/x/wasm/keeper/keeper_test.go @@ -283,7 +283,7 @@ func TestInstantiate(t *testing.T) { gasAfter := ctx.GasMeter().GasConsumed() if types.EnableGasVerification { - require.Equal(t, uint64(0x11ca8), gasAfter-gasBefore) + require.Equal(t, uint64(0x1229f), gasAfter-gasBefore) } // ensure it is stored properly @@ -516,7 +516,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(0x12963), gasAfter-gasBefore) + require.Equal(t, uint64(0x12916), gasAfter-gasBefore) } // ensure bob now exists and got both payments released bobAcct = accKeeper.GetAccount(ctx, bob) @@ -933,11 +933,11 @@ func TestMigrate(t *testing.T) { // and verify contract state raw := keepers.WasmKeeper.QueryRaw(ctx, contractAddr, []byte("config")) - var stored map[string][]byte + var stored map[string]string require.NoError(t, json.Unmarshal(raw, &stored)) require.Contains(t, stored, "verifier") require.NoError(t, err) - assert.Equal(t, spec.expVerifier, sdk.AccAddress(stored["verifier"])) + assert.Equal(t, spec.expVerifier.String(), stored["verifier"]) }) } } diff --git a/x/wasm/keeper/recurse_test.go b/x/wasm/keeper/recurse_test.go index b0f45c384bc..08cbf31fc9b 100644 --- a/x/wasm/keeper/recurse_test.go +++ b/x/wasm/keeper/recurse_test.go @@ -57,12 +57,12 @@ func initRecurseContract(t *testing.T) (contract sdk.AccAddress, creator sdk.Acc func TestGasCostOnQuery(t *testing.T) { const ( - GasNoWork uint64 = 44_074 + GasNoWork uint64 = 44_072 // Note: about 100 SDK gas (10k wasmer gas) for each round of sha256 - GasWork50 uint64 = 49_744 // this is a little shy of 50k gas - to keep an eye on the limit + GasWork50 uint64 = 49_763 // this is a little shy of 50k gas - to keep an eye on the limit - GasReturnUnhashed uint64 = 287 - GasReturnHashed uint64 = 262 + GasReturnUnhashed uint64 = 283 + GasReturnHashed uint64 = 258 ) cases := map[string]struct { @@ -221,9 +221,9 @@ func TestLimitRecursiveQueryGas(t *testing.T) { const ( // Note: about 100 SDK gas (10k wasmer gas) for each round of sha256 - GasWork2k uint64 = 272_797 // = InstanceCost + x // we have 6x gas used in cpu than in the instance + GasWork2k uint64 = 273_566 // = InstanceCost + x // we have 6x gas used in cpu than in the instance // This is overhead for calling into a sub-contract - GasReturnHashed uint64 = 265 + GasReturnHashed uint64 = 262 ) cases := map[string]struct { diff --git a/x/wasm/keeper/reflect_test.go b/x/wasm/keeper/reflect_test.go index 1e60db4c85e..c4cba0b07d5 100644 --- a/x/wasm/keeper/reflect_test.go +++ b/x/wasm/keeper/reflect_test.go @@ -381,7 +381,7 @@ func TestReflectStargateQuery(t *testing.T) { } type reflectState struct { - Owner []byte `json:"owner"` + Owner string `json:"owner"` } func TestMaskReflectWasmQueries(t *testing.T) { @@ -418,7 +418,7 @@ func TestMaskReflectWasmQueries(t *testing.T) { raw := keeper.QueryRaw(ctx, reflectAddr, configKey) var stateRes reflectState mustParse(t, raw, &stateRes) - require.Equal(t, stateRes.Owner, []byte(creator)) + require.Equal(t, stateRes.Owner, creator.String()) // now, let's reflect a smart query into the x/wasm handlers and see if we get the same result reflectOwnerQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{ @@ -453,7 +453,7 @@ func TestMaskReflectWasmQueries(t *testing.T) { // now, with the raw data, we can parse it into state var reflectStateRes reflectState mustParse(t, reflectRawRes.Data, &reflectStateRes) - require.Equal(t, reflectStateRes.Owner, []byte(creator)) + require.Equal(t, reflectStateRes.Owner, creator.String()) } func TestWasmRawQueryWithNil(t *testing.T) { diff --git a/x/wasm/keeper/submsg_test.go b/x/wasm/keeper/submsg_test.go index bc0fa98ea3d..c33a4de36e2 100644 --- a/x/wasm/keeper/submsg_test.go +++ b/x/wasm/keeper/submsg_test.go @@ -59,8 +59,9 @@ func TestDispatchSubMsgSuccessCase(t *testing.T) { reflectSend := ReflectHandleMsg{ ReflectSubCall: &reflectSubPayload{ Msgs: []wasmvmtypes.SubMsg{{ - ID: 7, - Msg: msg, + ID: 7, + Msg: msg, + ReplyOn: wasmvmtypes.ReplyAlways, }}, }, } @@ -322,6 +323,7 @@ func TestDispatchSubMsgErrorHandling(t *testing.T) { ID: tc.submsgID, Msg: msg, GasLimit: tc.gasLimit, + ReplyOn: wasmvmtypes.ReplyAlways, }}, }, } @@ -421,8 +423,9 @@ func TestDispatchSubMsgEncodeToNoSdkMsg(t *testing.T) { reflectSend := ReflectHandleMsg{ ReflectSubCall: &reflectSubPayload{ Msgs: []wasmvmtypes.SubMsg{{ - ID: 7, - Msg: msg, + ID: 7, + Msg: msg, + ReplyOn: wasmvmtypes.ReplyAlways, }}, }, } @@ -450,3 +453,132 @@ func TestDispatchSubMsgEncodeToNoSdkMsg(t *testing.T) { assert.Empty(t, sub.Data) require.Len(t, sub.Events, 0) } + +// Try a simple send, no gas limit to for a sanity check before trying table tests +func TestDispatchSubMsgConditionalReplyOn(t *testing.T) { + ctx, keepers := CreateTestInput(t, false, ReflectFeatures) + accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper + + deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000)) + contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000)) + + creator := createFakeFundedAccount(t, ctx, accKeeper, bankKeeper, deposit) + _, _, fred := keyPubAddr() + + // upload code + reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm") + require.NoError(t, err) + codeID, err := keepers.ContractKeeper.Create(ctx, creator, reflectCode, "", "", nil) + require.NoError(t, err) + + // creator instantiates a contract and gives it tokens + contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "reflect contract 1", contractStart) + require.NoError(t, err) + + goodSend := wasmvmtypes.CosmosMsg{ + Bank: &wasmvmtypes.BankMsg{ + Send: &wasmvmtypes.SendMsg{ + ToAddress: fred.String(), + Amount: []wasmvmtypes.Coin{{ + Denom: "denom", + Amount: "1000", + }}, + }, + }, + } + failSend := wasmvmtypes.CosmosMsg{ + Bank: &wasmvmtypes.BankMsg{ + Send: &wasmvmtypes.SendMsg{ + ToAddress: fred.String(), + Amount: []wasmvmtypes.Coin{{ + Denom: "no-such-token", + Amount: "777777", + }}, + }, + }, + } + + cases := map[string]struct { + // true for wasmvmtypes.ReplySuccess, false for wasmvmtypes.ReplyError + replyOnSuccess bool + msg wasmvmtypes.CosmosMsg + // true if the call should return an error (it wasn't handled) + expectError bool + // true if the reflect contract wrote the response (success or error) - it was captured + writeResult bool + }{ + "all good, reply success": { + replyOnSuccess: true, + msg: goodSend, + expectError: false, + writeResult: true, + }, + "all good, reply error": { + replyOnSuccess: false, + msg: goodSend, + expectError: false, + writeResult: false, + }, + "bad msg, reply success": { + replyOnSuccess: true, + msg: failSend, + expectError: true, + writeResult: false, + }, + "bad msg, reply error": { + replyOnSuccess: false, + msg: failSend, + expectError: false, + writeResult: true, + }, + } + + var id uint64 = 0 + for name, tc := range cases { + id++ + t.Run(name, func(t *testing.T) { + subMsg := wasmvmtypes.SubMsg{ + ID: id, + Msg: tc.msg, + ReplyOn: wasmvmtypes.ReplySuccess, + } + if !tc.replyOnSuccess { + subMsg.ReplyOn = wasmvmtypes.ReplyError + } + + reflectSend := ReflectHandleMsg{ + ReflectSubCall: &reflectSubPayload{ + Msgs: []wasmvmtypes.SubMsg{subMsg}, + }, + } + reflectSendBz, err := json.Marshal(reflectSend) + require.NoError(t, err) + _, err = keepers.ContractKeeper.Execute(ctx, contractAddr, creator, reflectSendBz, nil) + + if tc.expectError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + + // query the reflect state to check if the result was stored + query := ReflectQueryMsg{ + SubCallResult: &SubCall{ID: id}, + } + queryBz, err := json.Marshal(query) + require.NoError(t, err) + queryRes, err := keeper.QuerySmart(ctx, contractAddr, queryBz) + if tc.writeResult { + // we got some data for this call + require.NoError(t, err) + var res wasmvmtypes.Reply + err = json.Unmarshal(queryRes, &res) + require.NoError(t, err) + require.Equal(t, id, res.ID) + } else { + // nothing should be there -> error + require.Error(t, err) + } + }) + } +} diff --git a/x/wasm/keeper/test_common.go b/x/wasm/keeper/test_common.go index 31c70d10532..51511a32a5d 100644 --- a/x/wasm/keeper/test_common.go +++ b/x/wasm/keeper/test_common.go @@ -213,6 +213,7 @@ func createTestInput( stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, govtypes.ModuleName: {authtypes.Burner}, ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + types.ModuleName: {authtypes.Burner}, } authSubsp, _ := paramsKeeper.GetSubspace(authtypes.ModuleName) authKeeper := authkeeper.NewAccountKeeper( @@ -237,9 +238,10 @@ func createTestInput( blockedAddrs, ) bankParams := banktypes.DefaultParams() - bankParams = bankParams.SetSendEnabledParam("stake", true) bankKeeper.SetParams(ctx, bankParams) - + bankKeeper.SetSupply(ctx, banktypes.NewSupply(sdk.NewCoins( + sdk.NewCoin("denom", sdk.NewInt(10000)), + ))) stakingSubsp, _ := paramsKeeper.GetSubspace(stakingtypes.ModuleName) stakingKeeper := stakingkeeper.NewKeeper(appCodec, keyStaking, authKeeper, bankKeeper, stakingSubsp) stakingKeeper.SetParams(ctx, TestingStakeParams) diff --git a/x/wasm/keeper/testdata/burner.wasm b/x/wasm/keeper/testdata/burner.wasm index 8a16e6eab76..356bdcea214 100644 Binary files a/x/wasm/keeper/testdata/burner.wasm and b/x/wasm/keeper/testdata/burner.wasm differ diff --git a/x/wasm/keeper/testdata/hackatom.wasm b/x/wasm/keeper/testdata/hackatom.wasm index fc5a53ddb37..aeecf05af13 100644 Binary files a/x/wasm/keeper/testdata/hackatom.wasm and b/x/wasm/keeper/testdata/hackatom.wasm differ diff --git a/x/wasm/keeper/testdata/hackatom.wasm.gzip b/x/wasm/keeper/testdata/hackatom.wasm.gzip index 33c0ab92508..116a90fc8b2 100644 Binary files a/x/wasm/keeper/testdata/hackatom.wasm.gzip and b/x/wasm/keeper/testdata/hackatom.wasm.gzip differ diff --git a/x/wasm/keeper/testdata/ibc_reflect.wasm b/x/wasm/keeper/testdata/ibc_reflect.wasm index 76802272c21..3ff47810abe 100644 Binary files a/x/wasm/keeper/testdata/ibc_reflect.wasm and b/x/wasm/keeper/testdata/ibc_reflect.wasm differ diff --git a/x/wasm/keeper/testdata/ibc_reflect_send.wasm b/x/wasm/keeper/testdata/ibc_reflect_send.wasm index fcb0ce69f0f..b23c7bf648f 100644 Binary files a/x/wasm/keeper/testdata/ibc_reflect_send.wasm and b/x/wasm/keeper/testdata/ibc_reflect_send.wasm differ diff --git a/x/wasm/keeper/testdata/reflect.wasm b/x/wasm/keeper/testdata/reflect.wasm index 7e4fa531b17..3ca95363a0f 100644 Binary files a/x/wasm/keeper/testdata/reflect.wasm and b/x/wasm/keeper/testdata/reflect.wasm differ diff --git a/x/wasm/keeper/testdata/staking.wasm b/x/wasm/keeper/testdata/staking.wasm index 43f7445e33a..dd70d8fff3c 100644 Binary files a/x/wasm/keeper/testdata/staking.wasm and b/x/wasm/keeper/testdata/staking.wasm differ diff --git a/x/wasm/module_test.go b/x/wasm/module_test.go index 35fdc0b7826..514cc5a66c1 100644 --- a/x/wasm/module_test.go +++ b/x/wasm/module_test.go @@ -8,7 +8,6 @@ import ( "github.com/CosmWasm/wasmd/x/wasm/keeper" "github.com/CosmWasm/wasmd/x/wasm/types" - wasmvmtypes "github.com/CosmWasm/wasmvm/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" @@ -135,9 +134,9 @@ type initMsg struct { } type state struct { - Verifier wasmvmtypes.CanonicalAddress `json:"verifier"` - Beneficiary wasmvmtypes.CanonicalAddress `json:"beneficiary"` - Funder wasmvmtypes.CanonicalAddress `json:"funder"` + Verifier string `json:"verifier"` + Beneficiary string `json:"beneficiary"` + Funder string `json:"funder"` } func TestHandleInstantiate(t *testing.T) { @@ -192,9 +191,9 @@ func TestHandleInstantiate(t *testing.T) { assertContractList(t, q, data.ctx, 1, []string{contractBech32Addr}) assertContractInfo(t, q, data.ctx, contractBech32Addr, 1, creator) assertContractState(t, q, data.ctx, contractBech32Addr, state{ - Verifier: []byte(fred), - Beneficiary: []byte(bob), - Funder: []byte(creator), + Verifier: fred.String(), + Beneficiary: bob.String(), + Funder: creator.String(), }) } @@ -311,9 +310,9 @@ func TestHandleExecute(t *testing.T) { assertContractList(t, q, data.ctx, 1, []string{contractBech32Addr}) assertContractInfo(t, q, data.ctx, contractBech32Addr, 1, creator) assertContractState(t, q, data.ctx, contractBech32Addr, state{ - Verifier: []byte(fred), - Beneficiary: []byte(bob), - Funder: []byte(creator), + Verifier: fred.String(), + Beneficiary: bob.String(), + Funder: creator.String(), }) } diff --git a/x/wasm/types/expected_keepers.go b/x/wasm/types/expected_keepers.go index 6baf00a29aa..0717a9f38e9 100644 --- a/x/wasm/types/expected_keepers.go +++ b/x/wasm/types/expected_keepers.go @@ -12,14 +12,21 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// BankKeeper defines a subset of methods implemented by the cosmos-sdk bank keeper +// BankViewKeeper defines a subset of methods implemented by the cosmos-sdk bank keeper type BankViewKeeper interface { GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins } +// Burner is a subset of the sdk bank keeper methods +type Burner interface { + BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error +} + // BankKeeper defines a subset of methods implemented by the cosmos-sdk bank keeper type BankKeeper interface { BankViewKeeper + Burner SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error BlockedAddr(addr sdk.AccAddress) bool SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error