Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle wasmvm Burn message #489

Merged
merged 3 commits into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Handle wasmvm Burn message
  • Loading branch information
alpe committed Apr 13, 2021
commit 9ec97b5de98259a84b76e869497e517a59955057
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

}

// module accounts that are allowed to receive tokens
Expand Down
41 changes: 40 additions & 1 deletion x/wasm/keeper/handler_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,23 @@ 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.Coiner,
unpacker codectypes.AnyUnpacker,
portSource types.ICS20TransferPortSource,
customEncoders ...*MessageEncoders,
) Messenger {
encoders := DefaultEncoders(unpacker, portSource)
for _, e := range customEncoders {
encoders = encoders.Merge(e)
}
return NewMessageHandlerChain(
NewSDKMessageHandler(router, encoders),
NewIBCRawPacketHandler(channelKeeper, capabilityKeeper),
NewBurnCoinMessageHandler(bankKeeper),
)
}

Expand Down Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it.

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(coiner types.Coiner) 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 := coiner.SendCoinsFromAccountToModule(ctx, contractAddr, types.ModuleName, coins); err != nil {
return nil, nil, sdkerrors.Wrap(err, "transfer to module")
}
if err := coiner.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
}
}
86 changes: 86 additions & 0 deletions x/wasm/keeper/handler_plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

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
}
2 changes: 1 addition & 1 deletion x/wasm/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(0x129d6), gasAfter-gasBefore)
require.Equal(t, uint64(0x12916), gasAfter-gasBefore)
}
// ensure bob now exists and got both payments released
bobAcct = accKeeper.GetAccount(ctx, bob)
Expand Down
6 changes: 4 additions & 2 deletions x/wasm/keeper/test_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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)
Expand Down
9 changes: 8 additions & 1 deletion x/wasm/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

// Coiner is a subset of the sdk bank keeper methods
type Coiner 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
Coiner
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
Expand Down