Merge pull request #111 from CosmWasm/upgrade-cosmwasm-0.8
Upgrade cosmwasm 0.8
This commit is contained in:
@@ -21,7 +21,8 @@ RUN make install
|
||||
|
||||
# Install libgo_cosmwasm.so to a shared directory where it is readable by all users
|
||||
# See https://github.com/CosmWasm/wasmd/issues/43#issuecomment-608366314
|
||||
RUN cp /go/pkg/mod/github.com/confio/go-cosmwasm@v*/api/libgo_cosmwasm.so /lib/x86_64-linux-gnu
|
||||
# Note that CosmWasm gets turned into !cosm!wasm in the pkg/mod cache
|
||||
RUN cp /go/pkg/mod/github.com/\!cosm\!wasm/go-cosmwasm@v*/api/libgo_cosmwasm.so /lib/x86_64-linux-gnu
|
||||
|
||||
COPY docker/* /opt/
|
||||
RUN chmod +x /opt/*.sh
|
||||
|
||||
@@ -224,7 +224,9 @@ func NewWasmApp(
|
||||
}
|
||||
wasmConfig := wasmWrap.Wasm
|
||||
|
||||
app.wasmKeeper = wasm.NewKeeper(app.cdc, keys[wasm.StoreKey], app.accountKeeper, app.bankKeeper, wasmRouter, wasmDir, wasmConfig)
|
||||
// The last arguments can contain custom message handlers, and custom query handlers,
|
||||
// if we want to allow any custom callbacks
|
||||
app.wasmKeeper = wasm.NewKeeper(app.cdc, keys[wasm.StoreKey], app.accountKeeper, app.bankKeeper, app.stakingKeeper, wasmRouter, wasmDir, wasmConfig, nil, nil)
|
||||
|
||||
// create evidence keeper with evidence router
|
||||
evidenceKeeper := evidence.NewKeeper(
|
||||
|
||||
4
go.mod
4
go.mod
@@ -3,8 +3,8 @@ module github.com/cosmwasm/wasmd
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/CosmWasm/go-cosmwasm v0.7.3-0.20200506091810-b5d72f383001
|
||||
github.com/btcsuite/btcd v0.0.0-20190807005414-4063feeff79a // indirect
|
||||
github.com/confio/go-cosmwasm v0.7.2
|
||||
github.com/cosmos/cosmos-sdk v0.38.3
|
||||
github.com/golang/mock v1.4.3 // indirect
|
||||
github.com/gorilla/mux v1.7.4
|
||||
@@ -22,7 +22,7 @@ require (
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/tendermint/go-amino v0.15.1
|
||||
github.com/tendermint/tendermint v0.33.3
|
||||
github.com/tendermint/tm-db v0.5.0
|
||||
github.com/tendermint/tm-db v0.5.1
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 // indirect
|
||||
)
|
||||
|
||||
|
||||
6
go.sum
6
go.sum
@@ -9,6 +9,8 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f h1:4O1om+UVU+Hfcihr1timk8YNXHxzZWgCo7ofnrZRApw=
|
||||
github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
|
||||
github.com/CosmWasm/go-cosmwasm v0.7.3-0.20200506091810-b5d72f383001 h1:QvYlAMha7Wcmjn2e/D0Vk1/EFqHcnH3I01laQrSKIgk=
|
||||
github.com/CosmWasm/go-cosmwasm v0.7.3-0.20200506091810-b5d72f383001/go.mod h1:gAFCwllx97ejI+m9SqJQrmd2SBW7HA0fOjvWWJjM2uc=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
@@ -66,8 +68,6 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/confio/cosmos-sdk v0.38.7 h1:soa3c5ZZaYEnT8sZtwkSW7F6IykbpaVumtObIIkAp2w=
|
||||
github.com/confio/cosmos-sdk v0.38.7/go.mod h1:bltscsRmNMNThQcnIF3JV0iUNEdLa7GIU7r80W12CL8=
|
||||
github.com/confio/go-cosmwasm v0.7.2 h1:pnEmTbML0jEWKjnRutTsA0XfKnjN1b9vrCDUQMQ8slY=
|
||||
github.com/confio/go-cosmwasm v0.7.2/go.mod h1:pHipRby+f3cv97QPLELkzOAlNs/s87uDyhc+SnMn7L4=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
@@ -444,6 +444,8 @@ github.com/tendermint/tendermint v0.33.3/go.mod h1:25DqB7YvV1tN3tHsjWoc2vFtlwICf
|
||||
github.com/tendermint/tm-db v0.4.1/go.mod h1:JsJ6qzYkCGiGwm5GHl/H5GLI9XLb6qZX7PRe425dHAY=
|
||||
github.com/tendermint/tm-db v0.5.0 h1:qtM5UTr1dlRnHtDY6y7MZO5Di8XAE2j3lc/pCnKJ5hQ=
|
||||
github.com/tendermint/tm-db v0.5.0/go.mod h1:lSq7q5WRR/njf1LnhiZ/lIJHk2S8Y1Zyq5oP/3o9C2U=
|
||||
github.com/tendermint/tm-db v0.5.1 h1:H9HDq8UEA7Eeg13kdYckkgwwkQLBnJGgX4PgLJRhieY=
|
||||
github.com/tendermint/tm-db v0.5.1/go.mod h1:g92zWjHpCYlEvQXvy9M168Su8V1IBEeawpXVVBaK4f4=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
|
||||
@@ -37,7 +37,7 @@ var (
|
||||
GetContractAddressKey = types.GetContractAddressKey
|
||||
GetContractStorePrefixKey = types.GetContractStorePrefixKey
|
||||
NewCodeInfo = types.NewCodeInfo
|
||||
NewParams = types.NewParams
|
||||
NewParams = types.NewEnv
|
||||
NewWasmCoins = types.NewWasmCoins
|
||||
NewContractInfo = types.NewContractInfo
|
||||
CosmosResult = types.CosmosResult
|
||||
|
||||
@@ -2,7 +2,7 @@ package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
cosmwasm "github.com/confio/go-cosmwasm"
|
||||
cosmwasm "github.com/CosmWasm/go-cosmwasm"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
|
||||
289
x/wasm/internal/keeper/handler_plugin.go
Normal file
289
x/wasm/internal/keeper/handler_plugin.go
Normal file
@@ -0,0 +1,289 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
"github.com/cosmwasm/wasmd/x/wasm/internal/types"
|
||||
)
|
||||
|
||||
type MessageHandler struct {
|
||||
router sdk.Router
|
||||
encoders MessageEncoders
|
||||
}
|
||||
|
||||
func NewMessageHandler(router sdk.Router, customEncoders *MessageEncoders) MessageHandler {
|
||||
encoders := DefaultEncoders().Merge(customEncoders)
|
||||
return MessageHandler{
|
||||
router: router,
|
||||
encoders: encoders,
|
||||
}
|
||||
}
|
||||
|
||||
type BankEncoder func(sender sdk.AccAddress, msg *wasmTypes.BankMsg) ([]sdk.Msg, error)
|
||||
type CustomEncoder func(sender sdk.AccAddress, msg json.RawMessage) ([]sdk.Msg, error)
|
||||
type StakingEncoder func(sender sdk.AccAddress, msg *wasmTypes.StakingMsg) ([]sdk.Msg, error)
|
||||
type WasmEncoder func(sender sdk.AccAddress, msg *wasmTypes.WasmMsg) ([]sdk.Msg, error)
|
||||
|
||||
type MessageEncoders struct {
|
||||
Bank BankEncoder
|
||||
Custom CustomEncoder
|
||||
Staking StakingEncoder
|
||||
Wasm WasmEncoder
|
||||
}
|
||||
|
||||
func DefaultEncoders() MessageEncoders {
|
||||
return MessageEncoders{
|
||||
Bank: EncodeBankMsg,
|
||||
Custom: NoCustomMsg,
|
||||
Staking: EncodeStakingMsg,
|
||||
Wasm: EncodeWasmMsg,
|
||||
}
|
||||
}
|
||||
|
||||
func (e MessageEncoders) Merge(o *MessageEncoders) MessageEncoders {
|
||||
if o == nil {
|
||||
return e
|
||||
}
|
||||
if o.Bank != nil {
|
||||
e.Bank = o.Bank
|
||||
}
|
||||
if o.Custom != nil {
|
||||
e.Custom = o.Custom
|
||||
}
|
||||
if o.Staking != nil {
|
||||
e.Staking = o.Staking
|
||||
}
|
||||
if o.Wasm != nil {
|
||||
e.Wasm = o.Wasm
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (e MessageEncoders) Encode(contractAddr sdk.AccAddress, msg wasmTypes.CosmosMsg) ([]sdk.Msg, error) {
|
||||
switch {
|
||||
case msg.Bank != nil:
|
||||
return e.Bank(contractAddr, msg.Bank)
|
||||
case msg.Custom != nil:
|
||||
return e.Custom(contractAddr, msg.Custom)
|
||||
case msg.Staking != nil:
|
||||
return e.Staking(contractAddr, msg.Staking)
|
||||
case msg.Wasm != nil:
|
||||
return e.Wasm(contractAddr, msg.Wasm)
|
||||
}
|
||||
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Unknown variant of Wasm")
|
||||
}
|
||||
|
||||
func EncodeBankMsg(sender sdk.AccAddress, msg *wasmTypes.BankMsg) ([]sdk.Msg, error) {
|
||||
if msg.Send == nil {
|
||||
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Unknown variant of Bank")
|
||||
}
|
||||
if len(msg.Send.Amount) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
fromAddr, stderr := sdk.AccAddressFromBech32(msg.Send.FromAddress)
|
||||
if stderr != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Send.FromAddress)
|
||||
}
|
||||
toAddr, stderr := sdk.AccAddressFromBech32(msg.Send.ToAddress)
|
||||
if stderr != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Send.ToAddress)
|
||||
}
|
||||
toSend, err := convertWasmCoinsToSdkCoins(msg.Send.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sdkMsg := bank.MsgSend{
|
||||
FromAddress: fromAddr,
|
||||
ToAddress: toAddr,
|
||||
Amount: toSend,
|
||||
}
|
||||
return []sdk.Msg{sdkMsg}, nil
|
||||
}
|
||||
|
||||
func NoCustomMsg(sender sdk.AccAddress, msg json.RawMessage) ([]sdk.Msg, error) {
|
||||
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Custom variant not supported")
|
||||
}
|
||||
|
||||
func EncodeStakingMsg(sender sdk.AccAddress, msg *wasmTypes.StakingMsg) ([]sdk.Msg, error) {
|
||||
if msg.Delegate != nil {
|
||||
validator, err := sdk.ValAddressFromBech32(msg.Delegate.Validator)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Delegate.Validator)
|
||||
}
|
||||
coin, err := convertWasmCoinToSdkCoin(msg.Delegate.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sdkMsg := staking.MsgDelegate{
|
||||
DelegatorAddress: sender,
|
||||
ValidatorAddress: validator,
|
||||
Amount: coin,
|
||||
}
|
||||
return []sdk.Msg{sdkMsg}, nil
|
||||
}
|
||||
if msg.Redelegate != nil {
|
||||
src, err := sdk.ValAddressFromBech32(msg.Redelegate.SrcValidator)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Redelegate.SrcValidator)
|
||||
}
|
||||
dst, err := sdk.ValAddressFromBech32(msg.Redelegate.DstValidator)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Redelegate.DstValidator)
|
||||
}
|
||||
coin, err := convertWasmCoinToSdkCoin(msg.Redelegate.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sdkMsg := staking.MsgBeginRedelegate{
|
||||
DelegatorAddress: sender,
|
||||
ValidatorSrcAddress: src,
|
||||
ValidatorDstAddress: dst,
|
||||
Amount: coin,
|
||||
}
|
||||
return []sdk.Msg{sdkMsg}, nil
|
||||
}
|
||||
if msg.Undelegate != nil {
|
||||
validator, err := sdk.ValAddressFromBech32(msg.Undelegate.Validator)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Undelegate.Validator)
|
||||
}
|
||||
coin, err := convertWasmCoinToSdkCoin(msg.Undelegate.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sdkMsg := staking.MsgUndelegate{
|
||||
DelegatorAddress: sender,
|
||||
ValidatorAddress: validator,
|
||||
Amount: coin,
|
||||
}
|
||||
return []sdk.Msg{sdkMsg}, nil
|
||||
}
|
||||
if msg.Withdraw != nil {
|
||||
var err error
|
||||
rcpt := sender
|
||||
if len(msg.Withdraw.Recipient) != 0 {
|
||||
rcpt, err = sdk.AccAddressFromBech32(msg.Withdraw.Recipient)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Withdraw.Recipient)
|
||||
}
|
||||
}
|
||||
validator, err := sdk.ValAddressFromBech32(msg.Withdraw.Validator)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Withdraw.Validator)
|
||||
}
|
||||
setMsg := distribution.MsgSetWithdrawAddress{
|
||||
DelegatorAddress: sender,
|
||||
WithdrawAddress: rcpt,
|
||||
}
|
||||
withdrawMsg := distribution.MsgWithdrawDelegatorReward{
|
||||
DelegatorAddress: sender,
|
||||
ValidatorAddress: validator,
|
||||
}
|
||||
return []sdk.Msg{setMsg, withdrawMsg}, nil
|
||||
}
|
||||
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Unknown variant of Staking")
|
||||
}
|
||||
|
||||
func EncodeWasmMsg(sender sdk.AccAddress, msg *wasmTypes.WasmMsg) ([]sdk.Msg, error) {
|
||||
if msg.Execute != nil {
|
||||
contractAddr, err := sdk.AccAddressFromBech32(msg.Execute.ContractAddr)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Execute.ContractAddr)
|
||||
}
|
||||
coins, err := convertWasmCoinsToSdkCoins(msg.Execute.Send)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sdkMsg := types.MsgExecuteContract{
|
||||
Sender: sender,
|
||||
Contract: contractAddr,
|
||||
Msg: msg.Execute.Msg,
|
||||
SentFunds: coins,
|
||||
}
|
||||
return []sdk.Msg{sdkMsg}, nil
|
||||
}
|
||||
if msg.Instantiate != nil {
|
||||
coins, err := convertWasmCoinsToSdkCoins(msg.Instantiate.Send)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sdkMsg := types.MsgInstantiateContract{
|
||||
Sender: sender,
|
||||
Code: msg.Instantiate.CodeID,
|
||||
// TODO: add this to CosmWasm
|
||||
Label: fmt.Sprintf("Auto-created by %s", sender),
|
||||
InitMsg: msg.Instantiate.Msg,
|
||||
InitFunds: coins,
|
||||
}
|
||||
return []sdk.Msg{sdkMsg}, nil
|
||||
}
|
||||
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Unknown variant of Wasm")
|
||||
}
|
||||
|
||||
func (h MessageHandler) Dispatch(ctx sdk.Context, contractAddr sdk.AccAddress, msg wasmTypes.CosmosMsg) error {
|
||||
sdkMsgs, err := h.encoders.Encode(contractAddr, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, sdkMsg := range sdkMsgs {
|
||||
if err := h.handleSdkMessage(ctx, contractAddr, sdkMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h MessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) error {
|
||||
// make sure this account can send it
|
||||
for _, acct := range msg.GetSigners() {
|
||||
if !acct.Equals(contractAddr) {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission")
|
||||
}
|
||||
}
|
||||
|
||||
// find the handler and execute it
|
||||
handler := h.router.Route(ctx, msg.Route())
|
||||
if handler == nil {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, msg.Route())
|
||||
}
|
||||
res, err := handler(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// redispatch all events, (type sdk.EventTypeMessage will be filtered out in the handler)
|
||||
ctx.EventManager().EmitEvents(res.Events)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertWasmCoinsToSdkCoins(coins []wasmTypes.Coin) (sdk.Coins, error) {
|
||||
var toSend sdk.Coins
|
||||
for _, coin := range coins {
|
||||
c, err := convertWasmCoinToSdkCoin(coin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toSend = append(toSend, c)
|
||||
}
|
||||
return toSend, nil
|
||||
}
|
||||
|
||||
func convertWasmCoinToSdkCoin(coin wasmTypes.Coin) (sdk.Coin, error) {
|
||||
amount, ok := sdk.NewIntFromString(coin.Amount)
|
||||
if !ok {
|
||||
return sdk.Coin{}, sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, coin.Amount+coin.Denom)
|
||||
}
|
||||
return sdk.Coin{
|
||||
Denom: coin.Denom,
|
||||
Amount: amount,
|
||||
}, nil
|
||||
}
|
||||
275
x/wasm/internal/keeper/handler_plugin_test.go
Normal file
275
x/wasm/internal/keeper/handler_plugin_test.go
Normal file
@@ -0,0 +1,275 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
"github.com/cosmwasm/wasmd/x/wasm/internal/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
|
||||
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
func TestEncoding(t *testing.T) {
|
||||
_, _, addr1 := keyPubAddr()
|
||||
_, _, addr2 := keyPubAddr()
|
||||
invalidAddr := "xrnd1d02kd90n38qvr3qb9qof83fn2d2"
|
||||
valAddr := make(sdk.ValAddress, sdk.AddrLen)
|
||||
valAddr[0] = 12
|
||||
valAddr2 := make(sdk.ValAddress, sdk.AddrLen)
|
||||
valAddr2[1] = 123
|
||||
|
||||
jsonMsg := json.RawMessage(`{"foo": 123}`)
|
||||
|
||||
cases := map[string]struct {
|
||||
sender sdk.AccAddress
|
||||
input wasmTypes.CosmosMsg
|
||||
// set if valid
|
||||
output []sdk.Msg
|
||||
// set if invalid
|
||||
isError bool
|
||||
}{
|
||||
"simple send": {
|
||||
sender: addr1,
|
||||
input: wasmTypes.CosmosMsg{
|
||||
Bank: &wasmTypes.BankMsg{
|
||||
Send: &wasmTypes.SendMsg{
|
||||
FromAddress: addr1.String(),
|
||||
ToAddress: addr2.String(),
|
||||
Amount: []wasmTypes.Coin{
|
||||
{
|
||||
Denom: "uatom",
|
||||
Amount: "12345",
|
||||
},
|
||||
{
|
||||
Denom: "usdt",
|
||||
Amount: "54321",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: []sdk.Msg{
|
||||
bank.MsgSend{
|
||||
FromAddress: addr1,
|
||||
ToAddress: addr2,
|
||||
Amount: sdk.Coins{
|
||||
sdk.NewInt64Coin("uatom", 12345),
|
||||
sdk.NewInt64Coin("usdt", 54321),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"invalid send amount": {
|
||||
sender: addr1,
|
||||
input: wasmTypes.CosmosMsg{
|
||||
Bank: &wasmTypes.BankMsg{
|
||||
Send: &wasmTypes.SendMsg{
|
||||
FromAddress: addr1.String(),
|
||||
ToAddress: addr2.String(),
|
||||
Amount: []wasmTypes.Coin{
|
||||
{
|
||||
Denom: "uatom",
|
||||
Amount: "123.456",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isError: true,
|
||||
},
|
||||
"invalid address": {
|
||||
sender: addr1,
|
||||
input: wasmTypes.CosmosMsg{
|
||||
Bank: &wasmTypes.BankMsg{
|
||||
Send: &wasmTypes.SendMsg{
|
||||
FromAddress: addr1.String(),
|
||||
ToAddress: invalidAddr,
|
||||
Amount: []wasmTypes.Coin{
|
||||
{
|
||||
Denom: "uatom",
|
||||
Amount: "7890",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isError: true,
|
||||
},
|
||||
"wasm execute": {
|
||||
sender: addr1,
|
||||
input: wasmTypes.CosmosMsg{
|
||||
Wasm: &wasmTypes.WasmMsg{
|
||||
Execute: &wasmTypes.ExecuteMsg{
|
||||
ContractAddr: addr2.String(),
|
||||
Msg: jsonMsg,
|
||||
Send: []wasmTypes.Coin{
|
||||
wasmTypes.NewCoin(12, "eth"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: []sdk.Msg{
|
||||
types.MsgExecuteContract{
|
||||
Sender: addr1,
|
||||
Contract: addr2,
|
||||
Msg: jsonMsg,
|
||||
SentFunds: sdk.NewCoins(sdk.NewInt64Coin("eth", 12)),
|
||||
},
|
||||
},
|
||||
},
|
||||
"wasm instantiate": {
|
||||
sender: addr1,
|
||||
input: wasmTypes.CosmosMsg{
|
||||
Wasm: &wasmTypes.WasmMsg{
|
||||
Instantiate: &wasmTypes.InstantiateMsg{
|
||||
CodeID: 7,
|
||||
Msg: jsonMsg,
|
||||
Send: []wasmTypes.Coin{
|
||||
wasmTypes.NewCoin(123, "eth"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: []sdk.Msg{
|
||||
types.MsgInstantiateContract{
|
||||
Sender: addr1,
|
||||
Code: 7,
|
||||
// TODO: fix this
|
||||
Label: fmt.Sprintf("Auto-created by %s", addr1),
|
||||
InitMsg: jsonMsg,
|
||||
InitFunds: sdk.NewCoins(sdk.NewInt64Coin("eth", 123)),
|
||||
},
|
||||
},
|
||||
},
|
||||
"staking delegate": {
|
||||
sender: addr1,
|
||||
input: wasmTypes.CosmosMsg{
|
||||
Staking: &wasmTypes.StakingMsg{
|
||||
Delegate: &wasmTypes.DelegateMsg{
|
||||
Validator: valAddr.String(),
|
||||
Amount: wasmTypes.NewCoin(777, "stake"),
|
||||
},
|
||||
},
|
||||
},
|
||||
output: []sdk.Msg{
|
||||
staking.MsgDelegate{
|
||||
DelegatorAddress: addr1,
|
||||
ValidatorAddress: valAddr,
|
||||
Amount: sdk.NewInt64Coin("stake", 777),
|
||||
},
|
||||
},
|
||||
},
|
||||
"staking delegate to non-validator": {
|
||||
sender: addr1,
|
||||
input: wasmTypes.CosmosMsg{
|
||||
Staking: &wasmTypes.StakingMsg{
|
||||
Delegate: &wasmTypes.DelegateMsg{
|
||||
Validator: addr2.String(),
|
||||
Amount: wasmTypes.NewCoin(777, "stake"),
|
||||
},
|
||||
},
|
||||
},
|
||||
isError: true,
|
||||
},
|
||||
"staking undelegate": {
|
||||
sender: addr1,
|
||||
input: wasmTypes.CosmosMsg{
|
||||
Staking: &wasmTypes.StakingMsg{
|
||||
Undelegate: &wasmTypes.UndelegateMsg{
|
||||
Validator: valAddr.String(),
|
||||
Amount: wasmTypes.NewCoin(555, "stake"),
|
||||
},
|
||||
},
|
||||
},
|
||||
output: []sdk.Msg{
|
||||
staking.MsgUndelegate{
|
||||
DelegatorAddress: addr1,
|
||||
ValidatorAddress: valAddr,
|
||||
Amount: sdk.NewInt64Coin("stake", 555),
|
||||
},
|
||||
},
|
||||
},
|
||||
"staking redelegate": {
|
||||
sender: addr1,
|
||||
input: wasmTypes.CosmosMsg{
|
||||
Staking: &wasmTypes.StakingMsg{
|
||||
Redelegate: &wasmTypes.RedelegateMsg{
|
||||
SrcValidator: valAddr.String(),
|
||||
DstValidator: valAddr2.String(),
|
||||
Amount: wasmTypes.NewCoin(222, "stake"),
|
||||
},
|
||||
},
|
||||
},
|
||||
output: []sdk.Msg{
|
||||
staking.MsgBeginRedelegate{
|
||||
DelegatorAddress: addr1,
|
||||
ValidatorSrcAddress: valAddr,
|
||||
ValidatorDstAddress: valAddr2,
|
||||
Amount: sdk.NewInt64Coin("stake", 222),
|
||||
},
|
||||
},
|
||||
},
|
||||
"staking withdraw (implicit recipient)": {
|
||||
sender: addr1,
|
||||
input: wasmTypes.CosmosMsg{
|
||||
Staking: &wasmTypes.StakingMsg{
|
||||
Withdraw: &wasmTypes.WithdrawMsg{
|
||||
Validator: valAddr2.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
output: []sdk.Msg{
|
||||
distribution.MsgSetWithdrawAddress{
|
||||
DelegatorAddress: addr1,
|
||||
WithdrawAddress: addr1,
|
||||
},
|
||||
distribution.MsgWithdrawDelegatorReward{
|
||||
DelegatorAddress: addr1,
|
||||
ValidatorAddress: valAddr2,
|
||||
},
|
||||
},
|
||||
},
|
||||
"staking withdraw (explicit recipient)": {
|
||||
sender: addr1,
|
||||
input: wasmTypes.CosmosMsg{
|
||||
Staking: &wasmTypes.StakingMsg{
|
||||
Withdraw: &wasmTypes.WithdrawMsg{
|
||||
Validator: valAddr2.String(),
|
||||
Recipient: addr2.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
output: []sdk.Msg{
|
||||
distribution.MsgSetWithdrawAddress{
|
||||
DelegatorAddress: addr1,
|
||||
WithdrawAddress: addr2,
|
||||
},
|
||||
distribution.MsgWithdrawDelegatorReward{
|
||||
DelegatorAddress: addr1,
|
||||
ValidatorAddress: valAddr2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
encoder := DefaultEncoders()
|
||||
for name, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(name, func(t *testing.T) {
|
||||
res, err := encoder.Encode(tc.sender, tc.input)
|
||||
if tc.isError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.output, res)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,17 +2,16 @@ package keeper
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
"path/filepath"
|
||||
|
||||
wasm "github.com/confio/go-cosmwasm"
|
||||
wasmTypes "github.com/confio/go-cosmwasm/types"
|
||||
wasm "github.com/CosmWasm/go-cosmwasm"
|
||||
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
@@ -35,30 +34,36 @@ type Keeper struct {
|
||||
accountKeeper auth.AccountKeeper
|
||||
bankKeeper bank.Keeper
|
||||
|
||||
router sdk.Router
|
||||
|
||||
wasmer wasm.Wasmer
|
||||
wasmer wasm.Wasmer
|
||||
queryPlugins QueryPlugins
|
||||
messenger MessageHandler
|
||||
// queryGasLimit is the max wasm gas that can be spent on executing a query with a contract
|
||||
queryGasLimit uint64
|
||||
}
|
||||
|
||||
// NewKeeper creates a new contract Keeper instance
|
||||
// If customEncoders is non-nil, we can use this to override some of the message handler, especially custom
|
||||
func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, accountKeeper auth.AccountKeeper, bankKeeper bank.Keeper,
|
||||
router sdk.Router, homeDir string, wasmConfig types.WasmConfig) Keeper {
|
||||
stakingKeeper staking.Keeper,
|
||||
router sdk.Router, homeDir string, wasmConfig types.WasmConfig, customEncoders *MessageEncoders, customPlugins *QueryPlugins) Keeper {
|
||||
wasmer, err := wasm.NewWasmer(filepath.Join(homeDir, "wasm"), wasmConfig.CacheSize)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return Keeper{
|
||||
messenger := NewMessageHandler(router, customEncoders)
|
||||
|
||||
keeper := Keeper{
|
||||
storeKey: storeKey,
|
||||
cdc: cdc,
|
||||
wasmer: *wasmer,
|
||||
accountKeeper: accountKeeper,
|
||||
bankKeeper: bankKeeper,
|
||||
router: router,
|
||||
messenger: messenger,
|
||||
queryGasLimit: wasmConfig.SmartQueryGasLimit,
|
||||
}
|
||||
keeper.queryPlugins = DefaultQueryPlugins(bankKeeper, stakingKeeper, keeper).Merge(customPlugins)
|
||||
return keeper
|
||||
}
|
||||
|
||||
// Create uploads and compiles a WASM contract, returning a short identifier for the contract
|
||||
@@ -96,15 +101,15 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator sdk.AccAddre
|
||||
}
|
||||
|
||||
// deposit initial contract funds
|
||||
var contractAccount exported.Account
|
||||
if !deposit.IsZero() {
|
||||
sdkerr := k.bankKeeper.SendCoins(ctx, creator, contractAddress, deposit)
|
||||
if sdkerr != nil {
|
||||
return nil, sdkerr
|
||||
}
|
||||
contractAccount = k.accountKeeper.GetAccount(ctx, contractAddress)
|
||||
} else {
|
||||
contractAccount = k.accountKeeper.NewAccountWithAddress(ctx, contractAddress)
|
||||
// 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)
|
||||
}
|
||||
|
||||
@@ -118,19 +123,28 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator sdk.AccAddre
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &codeInfo)
|
||||
|
||||
// prepare params for contract instantiate call
|
||||
params := types.NewParams(ctx, creator, deposit, contractAccount)
|
||||
params := types.NewEnv(ctx, creator, deposit, contractAddress)
|
||||
|
||||
// create prefixed data store
|
||||
// 0x03 | contractAddress (sdk.AccAddress)
|
||||
prefixStoreKey := types.GetContractStorePrefixKey(contractAddress)
|
||||
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey)
|
||||
|
||||
// prepare querier
|
||||
querier := QueryHandler{
|
||||
Ctx: ctx,
|
||||
Plugins: k.queryPlugins,
|
||||
}
|
||||
|
||||
// instantiate wasm contract
|
||||
gas := gasForContract(ctx)
|
||||
res, err := k.wasmer.Instantiate(codeInfo.CodeHash, params, initMsg, prefixStore, cosmwasmAPI, gas)
|
||||
res, err := k.wasmer.Instantiate(codeInfo.CodeHash, params, initMsg, prefixStore, cosmwasmAPI, querier, gas)
|
||||
if err != nil {
|
||||
// TODO: wasmer doesn't return wasm gas used on error. we should consume it (for error on metering failure)
|
||||
// Note: OutOfGas panics (from storage) are caught by go-cosmwasm, subtract one more gas to check if
|
||||
// this contract died due to gas limit in Storage
|
||||
consumeGas(ctx, GasMultiplier)
|
||||
return contractAddress, sdkerrors.Wrap(types.ErrInstantiateFailed, err.Error())
|
||||
// return contractAddress, sdkerrors.Wrap(err, "cosmwasm instantiate")
|
||||
}
|
||||
consumeGas(ctx, res.GasUsed)
|
||||
|
||||
@@ -138,7 +152,7 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator sdk.AccAddre
|
||||
value := types.CosmosResult(*res, contractAddress)
|
||||
ctx.EventManager().EmitEvents(value.Events)
|
||||
|
||||
err = k.dispatchMessages(ctx, contractAccount, res.Messages)
|
||||
err = k.dispatchMessages(ctx, contractAddress, res.Messages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -165,14 +179,22 @@ func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller
|
||||
return sdk.Result{}, sdkerr
|
||||
}
|
||||
}
|
||||
contractAccount := k.accountKeeper.GetAccount(ctx, contractAddress)
|
||||
|
||||
params := types.NewParams(ctx, caller, coins, contractAccount)
|
||||
params := types.NewEnv(ctx, caller, coins, contractAddress)
|
||||
|
||||
// prepare querier
|
||||
querier := QueryHandler{
|
||||
Ctx: ctx,
|
||||
Plugins: k.queryPlugins,
|
||||
}
|
||||
|
||||
gas := gasForContract(ctx)
|
||||
res, execErr := k.wasmer.Execute(codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, gas)
|
||||
res, execErr := k.wasmer.Execute(codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, querier, gas)
|
||||
if execErr != nil {
|
||||
// TODO: wasmer doesn't return gas used on error. we should consume it (for error on metering failure)
|
||||
// TODO: wasmer doesn't return wasm gas used on error. we should consume it (for error on metering failure)
|
||||
// Note: OutOfGas panics (from storage) are caught by go-cosmwasm, subtract one more gas to check if
|
||||
// this contract died due to gas limit in Storage
|
||||
consumeGas(ctx, GasMultiplier)
|
||||
return sdk.Result{}, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error())
|
||||
}
|
||||
consumeGas(ctx, res.GasUsed)
|
||||
@@ -182,8 +204,7 @@ func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller
|
||||
ctx.EventManager().EmitEvents(value.Events)
|
||||
value.Events = nil
|
||||
|
||||
// TODO: capture events here as well
|
||||
err = k.dispatchMessages(ctx, contractAccount, res.Messages)
|
||||
err = k.dispatchMessages(ctx, contractAddress, res.Messages)
|
||||
if err != nil {
|
||||
return sdk.Result{}, err
|
||||
}
|
||||
@@ -199,7 +220,12 @@ func (k Keeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []b
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queryResult, gasUsed, qErr := k.wasmer.Query(codeInfo.CodeHash, req, prefixStore, cosmwasmAPI, gasForContract(ctx))
|
||||
// prepare querier
|
||||
querier := QueryHandler{
|
||||
Ctx: ctx,
|
||||
Plugins: k.queryPlugins,
|
||||
}
|
||||
queryResult, gasUsed, qErr := k.wasmer.Query(codeInfo.CodeHash, req, prefixStore, cosmwasmAPI, querier, gasForContract(ctx))
|
||||
if qErr != nil {
|
||||
return nil, sdkerrors.Wrap(types.ErrQueryFailed, qErr.Error())
|
||||
}
|
||||
@@ -311,114 +337,15 @@ func (k Keeper) GetByteCode(ctx sdk.Context, codeID uint64) ([]byte, error) {
|
||||
return k.wasmer.GetCode(codeInfo.CodeHash)
|
||||
}
|
||||
|
||||
func (k Keeper) dispatchMessages(ctx sdk.Context, contract exported.Account, msgs []wasmTypes.CosmosMsg) error {
|
||||
func (k Keeper) dispatchMessages(ctx sdk.Context, contractAddr sdk.AccAddress, msgs []wasmTypes.CosmosMsg) error {
|
||||
for _, msg := range msgs {
|
||||
if err := k.dispatchMessage(ctx, contract, msg); err != nil {
|
||||
if err := k.messenger.Dispatch(ctx, contractAddr, msg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k Keeper) dispatchMessage(ctx sdk.Context, contract exported.Account, msg wasmTypes.CosmosMsg) error {
|
||||
// maybe use this instead for the arg?
|
||||
contractAddr := contract.GetAddress()
|
||||
if msg.Send != nil {
|
||||
return k.sendTokens(ctx, contractAddr, msg.Send.FromAddress, msg.Send.ToAddress, msg.Send.Amount)
|
||||
} else if msg.Contract != nil {
|
||||
targetAddr, stderr := sdk.AccAddressFromBech32(msg.Contract.ContractAddr)
|
||||
if stderr != nil {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Contract.ContractAddr)
|
||||
}
|
||||
sentFunds, err := convertWasmCoinToSdkCoin(msg.Contract.Send)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = k.Execute(ctx, targetAddr, contractAddr, msg.Contract.Msg, sentFunds)
|
||||
return err // may be nil
|
||||
} else if msg.Opaque != nil {
|
||||
msg, err := ParseOpaqueMsg(k.cdc, msg.Opaque)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return k.handleSdkMessage(ctx, contractAddr, msg)
|
||||
}
|
||||
// what is it?
|
||||
panic(fmt.Sprintf("Unknown CosmosMsg: %#v", msg))
|
||||
}
|
||||
|
||||
func (k Keeper) sendTokens(ctx sdk.Context, signer sdk.AccAddress, origin string, target string, tokens []wasmTypes.Coin) error {
|
||||
if len(tokens) == 0 {
|
||||
return nil
|
||||
}
|
||||
msg, err := convertCosmosSendMsg(origin, target, tokens)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return k.handleSdkMessage(ctx, signer, msg)
|
||||
}
|
||||
|
||||
func convertCosmosSendMsg(from string, to string, coins []wasmTypes.Coin) (bank.MsgSend, error) {
|
||||
fromAddr, stderr := sdk.AccAddressFromBech32(from)
|
||||
if stderr != nil {
|
||||
return bank.MsgSend{}, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, from)
|
||||
}
|
||||
toAddr, stderr := sdk.AccAddressFromBech32(to)
|
||||
if stderr != nil {
|
||||
return bank.MsgSend{}, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, to)
|
||||
}
|
||||
|
||||
toSend, err := convertWasmCoinToSdkCoin(coins)
|
||||
if err != nil {
|
||||
return bank.MsgSend{}, err
|
||||
}
|
||||
sendMsg := bank.MsgSend{
|
||||
FromAddress: fromAddr,
|
||||
ToAddress: toAddr,
|
||||
Amount: toSend,
|
||||
}
|
||||
return sendMsg, nil
|
||||
}
|
||||
|
||||
func convertWasmCoinToSdkCoin(coins []wasmTypes.Coin) (sdk.Coins, error) {
|
||||
var toSend sdk.Coins
|
||||
for _, coin := range coins {
|
||||
amount, ok := sdk.NewIntFromString(coin.Amount)
|
||||
if !ok {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, coin.Amount+coin.Denom)
|
||||
}
|
||||
c := sdk.Coin{
|
||||
Denom: coin.Denom,
|
||||
Amount: amount,
|
||||
}
|
||||
toSend = append(toSend, c)
|
||||
}
|
||||
return toSend, nil
|
||||
}
|
||||
|
||||
func (k Keeper) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) error {
|
||||
// make sure this account can send it
|
||||
for _, acct := range msg.GetSigners() {
|
||||
if !acct.Equals(contractAddr) {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission")
|
||||
}
|
||||
}
|
||||
|
||||
// find the handler and execute it
|
||||
h := k.router.Route(ctx, msg.Route())
|
||||
if h == nil {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, msg.Route())
|
||||
}
|
||||
res, err := h(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// redispatch all events, (type sdk.EventTypeMessage will be filtered out in the handler)
|
||||
ctx.EventManager().EmitEvents(res.Events)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func gasForContract(ctx sdk.Context) uint64 {
|
||||
meter := ctx.GasMeter()
|
||||
remaining := (meter.Limit() - meter.GasConsumed()) * GasMultiplier
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestNewKeeper(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
_, _, keeper := CreateTestInput(t, false, tempDir)
|
||||
_, _, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
require.NotNil(t, keeper)
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestCreate(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit)
|
||||
@@ -52,7 +52,7 @@ func TestCreateDuplicate(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit)
|
||||
@@ -83,7 +83,7 @@ func TestCreateWithSimulation(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
ctx = ctx.WithBlockHeader(abci.Header{Height: 1}).
|
||||
WithGasMeter(stypes.NewInfiniteGasMeter())
|
||||
|
||||
@@ -99,7 +99,7 @@ func TestCreateWithSimulation(t *testing.T) {
|
||||
require.Equal(t, uint64(1), contractID)
|
||||
|
||||
// then try to create it in non-simulation mode (should not fail)
|
||||
ctx, accKeeper, keeper = CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper = CreateTestInput(t, false, tempDir, nil, nil)
|
||||
contractID, err = keeper.Create(ctx, creator, wasmCode, "https://github.com/cosmwasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "confio/cosmwasm-opt:0.7.2")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1), contractID)
|
||||
@@ -139,7 +139,7 @@ func TestCreateWithGzippedPayload(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit)
|
||||
@@ -162,7 +162,7 @@ func TestInstantiate(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit)
|
||||
@@ -191,7 +191,7 @@ func TestInstantiate(t *testing.T) {
|
||||
require.Equal(t, "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", addr.String())
|
||||
|
||||
gasAfter := ctx.GasMeter().GasConsumed()
|
||||
require.Equal(t, uint64(24823), gasAfter-gasBefore)
|
||||
require.Equal(t, uint64(24874), gasAfter-gasBefore)
|
||||
|
||||
// ensure it is stored properly
|
||||
info := keeper.GetContractInfo(ctx, addr)
|
||||
@@ -206,7 +206,7 @@ func TestInstantiateWithNonExistingCodeID(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit)
|
||||
@@ -227,7 +227,7 @@ func TestExecute(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))
|
||||
@@ -271,7 +271,7 @@ func TestExecute(t *testing.T) {
|
||||
trialCtx := ctx.WithMultiStore(ctx.MultiStore().CacheWrap().(sdk.MultiStore))
|
||||
res, err := keeper.Execute(trialCtx, addr, creator, []byte(`{"release":{}}`), nil)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "Unauthorized")
|
||||
require.Contains(t, err.Error(), "unauthorized")
|
||||
|
||||
// verifier can execute, and get proper gas amount
|
||||
start := time.Now()
|
||||
@@ -284,7 +284,7 @@ func TestExecute(t *testing.T) {
|
||||
|
||||
// make sure gas is properly deducted from ctx
|
||||
gasAfter := ctx.GasMeter().GasConsumed()
|
||||
require.Equal(t, uint64(31162), gasAfter-gasBefore)
|
||||
require.Equal(t, uint64(31067), gasAfter-gasBefore)
|
||||
|
||||
// ensure bob now exists and got both payments released
|
||||
bobAcct = accKeeper.GetAccount(ctx, bob)
|
||||
@@ -304,7 +304,7 @@ func TestExecuteWithNonExistingAddress(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit.Add(deposit...))
|
||||
@@ -319,7 +319,7 @@ func TestExecuteWithPanic(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))
|
||||
@@ -352,7 +352,7 @@ func TestExecuteWithCpuLoop(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))
|
||||
@@ -382,7 +382,7 @@ func TestExecuteWithCpuLoop(t *testing.T) {
|
||||
require.Equal(t, uint64(0), ctx.GasMeter().GasConsumed())
|
||||
|
||||
// this must fail
|
||||
_, err = keeper.Execute(ctx, addr, fred, []byte(`{"cpuloop":{}}`), nil)
|
||||
_, err = keeper.Execute(ctx, addr, fred, []byte(`{"cpu_loop":{}}`), nil)
|
||||
assert.Error(t, err)
|
||||
// make sure gas ran out
|
||||
// TODO: wasmer doesn't return gas used on error. we should consume it (for error on metering failure)
|
||||
@@ -393,7 +393,7 @@ func TestExecuteWithStorageLoop(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))
|
||||
@@ -432,7 +432,7 @@ func TestExecuteWithStorageLoop(t *testing.T) {
|
||||
}()
|
||||
|
||||
// this should throw out of gas exception (panic)
|
||||
_, _ = keeper.Execute(ctx, addr, fred, []byte(`{"storageloop":{}}`), nil)
|
||||
_, err = keeper.Execute(ctx, addr, fred, []byte(`{"storage_loop":{}}`), nil)
|
||||
require.True(t, false, "We must panic before this line")
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,18 @@ package keeper
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmwasm/wasmd/x/wasm/internal/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
wasmTypes "github.com/confio/go-cosmwasm/types"
|
||||
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
@@ -17,9 +21,10 @@ import (
|
||||
|
||||
// MaskInitMsg is {}
|
||||
|
||||
// MaskHandleMsg is used to encode handle messages
|
||||
type MaskHandleMsg struct {
|
||||
Reflect *reflectPayload `json:"reflectmsg,omitempty"`
|
||||
Change *ownerPayload `json:"changeowner,omitempty"`
|
||||
Reflect *reflectPayload `json:"reflect_msg,omitempty"`
|
||||
Change *ownerPayload `json:"change_owner,omitempty"`
|
||||
}
|
||||
|
||||
type ownerPayload struct {
|
||||
@@ -27,107 +32,28 @@ type ownerPayload struct {
|
||||
}
|
||||
|
||||
type reflectPayload struct {
|
||||
Msg wasmTypes.CosmosMsg `json:"msg"`
|
||||
Msgs []wasmTypes.CosmosMsg `json:"msgs"`
|
||||
}
|
||||
|
||||
func TestMaskReflectOpaque(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
// MaskQueryMsg is used to encode query messages
|
||||
type MaskQueryMsg struct {
|
||||
Owner *struct{} `json:"owner,omitempty"`
|
||||
ReflectCustom *Text `json:"reflect_custom,omitempty"`
|
||||
}
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit)
|
||||
bob := createFakeFundedAccount(ctx, accKeeper, deposit)
|
||||
_, _, fred := keyPubAddr()
|
||||
type Text struct {
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
// upload code
|
||||
maskCode, err := ioutil.ReadFile("./testdata/mask.wasm")
|
||||
require.NoError(t, err)
|
||||
codeID, err := keeper.Create(ctx, creator, maskCode, "", "")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1), codeID)
|
||||
|
||||
// creator instantiates a contract and gives it tokens
|
||||
contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
|
||||
contractAddr, err := keeper.Instantiate(ctx, codeID, creator, []byte("{}"), "mask contract 1", contractStart)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, contractAddr)
|
||||
|
||||
// set owner to bob
|
||||
transfer := MaskHandleMsg{
|
||||
Change: &ownerPayload{
|
||||
Owner: bob,
|
||||
},
|
||||
}
|
||||
transferBz, err := json.Marshal(transfer)
|
||||
require.NoError(t, err)
|
||||
_, err = keeper.Execute(ctx, contractAddr, creator, transferBz, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// check some account values
|
||||
checkAccount(t, ctx, accKeeper, contractAddr, contractStart)
|
||||
checkAccount(t, ctx, accKeeper, bob, deposit)
|
||||
checkAccount(t, ctx, accKeeper, fred, nil)
|
||||
|
||||
// bob can send contract's tokens to fred (using SendMsg)
|
||||
msg := wasmTypes.CosmosMsg{
|
||||
Send: &wasmTypes.SendMsg{
|
||||
FromAddress: contractAddr.String(),
|
||||
ToAddress: fred.String(),
|
||||
Amount: []wasmTypes.Coin{{
|
||||
Denom: "denom",
|
||||
Amount: "15000",
|
||||
}},
|
||||
},
|
||||
}
|
||||
reflectSend := MaskHandleMsg{
|
||||
Reflect: &reflectPayload{
|
||||
Msg: msg,
|
||||
},
|
||||
}
|
||||
reflectSendBz, err := json.Marshal(reflectSend)
|
||||
require.NoError(t, err)
|
||||
_, err = keeper.Execute(ctx, contractAddr, bob, reflectSendBz, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// fred got coins
|
||||
checkAccount(t, ctx, accKeeper, fred, sdk.NewCoins(sdk.NewInt64Coin("denom", 15000)))
|
||||
// contract lost them
|
||||
checkAccount(t, ctx, accKeeper, contractAddr, sdk.NewCoins(sdk.NewInt64Coin("denom", 25000)))
|
||||
checkAccount(t, ctx, accKeeper, bob, deposit)
|
||||
|
||||
// construct an opaque message
|
||||
var sdkSendMsg sdk.Msg = &bank.MsgSend{
|
||||
FromAddress: contractAddr,
|
||||
ToAddress: fred,
|
||||
Amount: sdk.NewCoins(sdk.NewInt64Coin("denom", 23000)),
|
||||
}
|
||||
opaque, err := ToCosmosMsg(keeper.cdc, sdkSendMsg)
|
||||
require.NoError(t, err)
|
||||
reflectOpaque := MaskHandleMsg{
|
||||
Reflect: &reflectPayload{
|
||||
Msg: opaque,
|
||||
},
|
||||
}
|
||||
reflectOpaqueBz, err := json.Marshal(reflectOpaque)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = keeper.Execute(ctx, contractAddr, bob, reflectOpaqueBz, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// fred got more coins
|
||||
checkAccount(t, ctx, accKeeper, fred, sdk.NewCoins(sdk.NewInt64Coin("denom", 38000)))
|
||||
// contract lost them
|
||||
checkAccount(t, ctx, accKeeper, contractAddr, sdk.NewCoins(sdk.NewInt64Coin("denom", 2000)))
|
||||
checkAccount(t, ctx, accKeeper, bob, deposit)
|
||||
type OwnerResponse struct {
|
||||
Owner string `json:"owner,omitempty"`
|
||||
}
|
||||
|
||||
func TestMaskReflectContractSend(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, maskEncoders(MakeTestCodec()), nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit)
|
||||
@@ -176,19 +102,21 @@ func TestMaskReflectContractSend(t *testing.T) {
|
||||
// this should reduce the mask balance by 14k (to 26k)
|
||||
// this 14k is added to the escrow, then the entire balance is sent to bob (total: 39k)
|
||||
approveMsg := []byte(`{"release":{}}`)
|
||||
msg := wasmTypes.CosmosMsg{
|
||||
Contract: &wasmTypes.ContractMsg{
|
||||
ContractAddr: escrowAddr.String(),
|
||||
Msg: approveMsg,
|
||||
Send: []wasmTypes.Coin{{
|
||||
Denom: "denom",
|
||||
Amount: "14000",
|
||||
}},
|
||||
msgs := []wasmTypes.CosmosMsg{{
|
||||
Wasm: &wasmTypes.WasmMsg{
|
||||
Execute: &wasmTypes.ExecuteMsg{
|
||||
ContractAddr: escrowAddr.String(),
|
||||
Msg: approveMsg,
|
||||
Send: []wasmTypes.Coin{{
|
||||
Denom: "denom",
|
||||
Amount: "14000",
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
}}
|
||||
reflectSend := MaskHandleMsg{
|
||||
Reflect: &reflectPayload{
|
||||
Msg: msg,
|
||||
Msgs: msgs,
|
||||
},
|
||||
}
|
||||
reflectSendBz, err := json.Marshal(reflectSend)
|
||||
@@ -204,6 +132,152 @@ func TestMaskReflectContractSend(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestMaskReflectCustomMsg(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, maskEncoders(MakeTestCodec()), maskPlugins())
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit)
|
||||
bob := createFakeFundedAccount(ctx, accKeeper, deposit)
|
||||
_, _, fred := keyPubAddr()
|
||||
|
||||
// upload code
|
||||
maskCode, err := ioutil.ReadFile("./testdata/mask.wasm")
|
||||
require.NoError(t, err)
|
||||
codeID, err := keeper.Create(ctx, creator, maskCode, "", "")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1), codeID)
|
||||
|
||||
// creator instantiates a contract and gives it tokens
|
||||
contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
|
||||
contractAddr, err := keeper.Instantiate(ctx, codeID, creator, []byte("{}"), "mask contract 1", contractStart)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, contractAddr)
|
||||
|
||||
// set owner to bob
|
||||
transfer := MaskHandleMsg{
|
||||
Change: &ownerPayload{
|
||||
Owner: bob,
|
||||
},
|
||||
}
|
||||
transferBz, err := json.Marshal(transfer)
|
||||
require.NoError(t, err)
|
||||
_, err = keeper.Execute(ctx, contractAddr, creator, transferBz, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// check some account values
|
||||
checkAccount(t, ctx, accKeeper, contractAddr, contractStart)
|
||||
checkAccount(t, ctx, accKeeper, bob, deposit)
|
||||
checkAccount(t, ctx, accKeeper, fred, nil)
|
||||
|
||||
// bob can send contract's tokens to fred (using SendMsg)
|
||||
msgs := []wasmTypes.CosmosMsg{{
|
||||
Bank: &wasmTypes.BankMsg{
|
||||
Send: &wasmTypes.SendMsg{
|
||||
FromAddress: contractAddr.String(),
|
||||
ToAddress: fred.String(),
|
||||
Amount: []wasmTypes.Coin{{
|
||||
Denom: "denom",
|
||||
Amount: "15000",
|
||||
}},
|
||||
},
|
||||
},
|
||||
}}
|
||||
reflectSend := MaskHandleMsg{
|
||||
Reflect: &reflectPayload{
|
||||
Msgs: msgs,
|
||||
},
|
||||
}
|
||||
reflectSendBz, err := json.Marshal(reflectSend)
|
||||
require.NoError(t, err)
|
||||
_, err = keeper.Execute(ctx, contractAddr, bob, reflectSendBz, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// fred got coins
|
||||
checkAccount(t, ctx, accKeeper, fred, sdk.NewCoins(sdk.NewInt64Coin("denom", 15000)))
|
||||
// contract lost them
|
||||
checkAccount(t, ctx, accKeeper, contractAddr, sdk.NewCoins(sdk.NewInt64Coin("denom", 25000)))
|
||||
checkAccount(t, ctx, accKeeper, bob, deposit)
|
||||
|
||||
// construct an opaque message
|
||||
var sdkSendMsg sdk.Msg = &bank.MsgSend{
|
||||
FromAddress: contractAddr,
|
||||
ToAddress: fred,
|
||||
Amount: sdk.NewCoins(sdk.NewInt64Coin("denom", 23000)),
|
||||
}
|
||||
opaque, err := toMaskRawMsg(keeper.cdc, sdkSendMsg)
|
||||
require.NoError(t, err)
|
||||
reflectOpaque := MaskHandleMsg{
|
||||
Reflect: &reflectPayload{
|
||||
Msgs: []wasmTypes.CosmosMsg{opaque},
|
||||
},
|
||||
}
|
||||
reflectOpaqueBz, err := json.Marshal(reflectOpaque)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = keeper.Execute(ctx, contractAddr, bob, reflectOpaqueBz, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// fred got more coins
|
||||
checkAccount(t, ctx, accKeeper, fred, sdk.NewCoins(sdk.NewInt64Coin("denom", 38000)))
|
||||
// contract lost them
|
||||
checkAccount(t, ctx, accKeeper, contractAddr, sdk.NewCoins(sdk.NewInt64Coin("denom", 2000)))
|
||||
checkAccount(t, ctx, accKeeper, bob, deposit)
|
||||
}
|
||||
|
||||
func TestMaskReflectCustomQuery(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, maskEncoders(MakeTestCodec()), maskPlugins())
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit)
|
||||
|
||||
// upload code
|
||||
maskCode, err := ioutil.ReadFile("./testdata/mask.wasm")
|
||||
require.NoError(t, err)
|
||||
codeID, err := keeper.Create(ctx, creator, maskCode, "", "")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1), codeID)
|
||||
|
||||
// creator instantiates a contract and gives it tokens
|
||||
contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
|
||||
contractAddr, err := keeper.Instantiate(ctx, codeID, creator, []byte("{}"), "mask contract 1", contractStart)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, contractAddr)
|
||||
|
||||
// let's perform a normal query of state
|
||||
ownerQuery := MaskQueryMsg{
|
||||
Owner: &struct{}{},
|
||||
}
|
||||
ownerQueryBz, err := json.Marshal(ownerQuery)
|
||||
require.NoError(t, err)
|
||||
ownerRes, err := keeper.QuerySmart(ctx, contractAddr, ownerQueryBz)
|
||||
require.NoError(t, err)
|
||||
var res OwnerResponse
|
||||
err = json.Unmarshal(ownerRes, &res)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, res.Owner, creator.String())
|
||||
|
||||
// and now making use of the custom querier callbacks
|
||||
customQuery := MaskQueryMsg{
|
||||
ReflectCustom: &Text{
|
||||
Text: "all Caps noW",
|
||||
},
|
||||
}
|
||||
customQueryBz, err := json.Marshal(customQuery)
|
||||
require.NoError(t, err)
|
||||
custom, err := keeper.QuerySmart(ctx, contractAddr, customQueryBz)
|
||||
require.NoError(t, err)
|
||||
var resp customQueryResponse
|
||||
err = json.Unmarshal(custom, &resp)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, resp.Msg, "ALL CAPS NOW")
|
||||
}
|
||||
|
||||
func checkAccount(t *testing.T, ctx sdk.Context, accKeeper auth.AccountKeeper, addr sdk.AccAddress, expected sdk.Coins) {
|
||||
acct := accKeeper.GetAccount(ctx, addr)
|
||||
if expected == nil {
|
||||
@@ -218,3 +292,89 @@ func checkAccount(t *testing.T, ctx sdk.Context, accKeeper auth.AccountKeeper, a
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**** Code to support custom messages *****/
|
||||
|
||||
type maskCustomMsg struct {
|
||||
Debug string `json:"debug,omitempty"`
|
||||
Raw []byte `json:"raw,omitempty"`
|
||||
}
|
||||
|
||||
// toMaskRawMsg encodes an sdk msg using amino json encoding.
|
||||
// Then wraps it as an opaque message
|
||||
func toMaskRawMsg(cdc *codec.Codec, msg sdk.Msg) (wasmTypes.CosmosMsg, error) {
|
||||
rawBz, err := cdc.MarshalJSON(msg)
|
||||
if err != nil {
|
||||
return wasmTypes.CosmosMsg{}, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
||||
}
|
||||
customMsg, err := json.Marshal(maskCustomMsg{
|
||||
Raw: rawBz,
|
||||
})
|
||||
res := wasmTypes.CosmosMsg{
|
||||
Custom: customMsg,
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// maskEncoders needs to be registered in test setup to handle custom message callbacks
|
||||
func maskEncoders(cdc *codec.Codec) *MessageEncoders {
|
||||
return &MessageEncoders{
|
||||
Custom: fromMaskRawMsg(cdc),
|
||||
}
|
||||
}
|
||||
|
||||
// fromMaskRawMsg decodes msg.Data to an sdk.Msg using amino json encoding.
|
||||
// this needs to be registered on the Encoders
|
||||
func fromMaskRawMsg(cdc *codec.Codec) CustomEncoder {
|
||||
return func(_sender sdk.AccAddress, msg json.RawMessage) ([]sdk.Msg, error) {
|
||||
var custom maskCustomMsg
|
||||
err := json.Unmarshal(msg, &custom)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
||||
}
|
||||
if custom.Raw != nil {
|
||||
var sdkMsg sdk.Msg
|
||||
err := cdc.UnmarshalJSON(custom.Raw, &sdkMsg)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
||||
}
|
||||
return []sdk.Msg{sdkMsg}, nil
|
||||
}
|
||||
if custom.Debug != "" {
|
||||
return nil, sdkerrors.Wrapf(types.ErrInvalidMsg, "Custom Debug: %s", custom.Debug)
|
||||
}
|
||||
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Unknown Custom message variant")
|
||||
}
|
||||
}
|
||||
|
||||
type maskCustomQuery struct {
|
||||
Ping *struct{} `json:"ping,omitempty"`
|
||||
Capital *Text `json:"capital,omitempty"`
|
||||
}
|
||||
|
||||
type customQueryResponse struct {
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// maskPlugins needs to be registered in test setup to handle custom query callbacks
|
||||
func maskPlugins() *QueryPlugins {
|
||||
return &QueryPlugins{
|
||||
Custom: performCustomQuery,
|
||||
}
|
||||
}
|
||||
|
||||
func performCustomQuery(_ sdk.Context, request json.RawMessage) ([]byte, error) {
|
||||
var custom maskCustomQuery
|
||||
err := json.Unmarshal(request, &custom)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
||||
}
|
||||
if custom.Capital != nil {
|
||||
msg := strings.ToUpper(custom.Capital.Text)
|
||||
return json.Marshal(customQueryResponse{Msg: msg})
|
||||
}
|
||||
if custom.Ping != nil {
|
||||
return json.Marshal(customQueryResponse{Msg: "pong"})
|
||||
}
|
||||
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Unknown Custom query variant")
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
wasmTypes "github.com/confio/go-cosmwasm/types"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// ToCosmosMsg encodes an sdk msg using amino json encoding.
|
||||
// Then wraps it as an opaque message
|
||||
func ToCosmosMsg(cdc *codec.Codec, msg sdk.Msg) (wasmTypes.CosmosMsg, error) {
|
||||
opaqueBz, err := cdc.MarshalJSON(msg)
|
||||
if err != nil {
|
||||
return wasmTypes.CosmosMsg{}, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
||||
}
|
||||
res := wasmTypes.CosmosMsg{
|
||||
Opaque: &wasmTypes.OpaqueMsg{
|
||||
Data: opaqueBz,
|
||||
},
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// ParseOpaqueMsg decodes msg.Data to an sdk.Msg using amino json encoding.
|
||||
func ParseOpaqueMsg(cdc *codec.Codec, msg *wasmTypes.OpaqueMsg) (sdk.Msg, error) {
|
||||
// until more is changes, format is amino json encoding, wrapped base64
|
||||
var sdkmsg sdk.Msg
|
||||
err := cdc.UnmarshalJSON(msg.Data, &sdkmsg)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
||||
}
|
||||
return sdkmsg, nil
|
||||
}
|
||||
@@ -19,7 +19,7 @@ func TestQueryContractState(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))
|
||||
@@ -85,7 +85,7 @@ func TestQueryContractState(t *testing.T) {
|
||||
"query smart": {
|
||||
srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateSmart},
|
||||
srcReq: abci.RequestQuery{Data: []byte(`{"verifier":{}}`)},
|
||||
expSmartRes: anyAddr.String(),
|
||||
expSmartRes: fmt.Sprintf(`{"verifier":"%s"}`, anyAddr.String()),
|
||||
},
|
||||
"query smart invalid request": {
|
||||
srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateSmart},
|
||||
@@ -147,7 +147,7 @@ func TestListContractByCodeOrdering(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, accKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 1000000))
|
||||
topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 500))
|
||||
|
||||
243
x/wasm/internal/keeper/query_plugins.go
Normal file
243
x/wasm/internal/keeper/query_plugins.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
)
|
||||
|
||||
type QueryHandler struct {
|
||||
Ctx sdk.Context
|
||||
Plugins QueryPlugins
|
||||
}
|
||||
|
||||
var _ wasmTypes.Querier = QueryHandler{}
|
||||
|
||||
func (q QueryHandler) Query(request wasmTypes.QueryRequest) ([]byte, error) {
|
||||
if request.Bank != nil {
|
||||
return q.Plugins.Bank(q.Ctx, request.Bank)
|
||||
}
|
||||
if request.Custom != nil {
|
||||
return q.Plugins.Custom(q.Ctx, request.Custom)
|
||||
}
|
||||
if request.Staking != nil {
|
||||
return q.Plugins.Staking(q.Ctx, request.Staking)
|
||||
}
|
||||
if request.Wasm != nil {
|
||||
return q.Plugins.Wasm(q.Ctx, request.Wasm)
|
||||
}
|
||||
return nil, wasmTypes.Unknown{}
|
||||
}
|
||||
|
||||
type CustomQuerier func(ctx sdk.Context, request json.RawMessage) ([]byte, error)
|
||||
|
||||
type QueryPlugins struct {
|
||||
Bank func(ctx sdk.Context, request *wasmTypes.BankQuery) ([]byte, error)
|
||||
Custom CustomQuerier
|
||||
Staking func(ctx sdk.Context, request *wasmTypes.StakingQuery) ([]byte, error)
|
||||
Wasm func(ctx sdk.Context, request *wasmTypes.WasmQuery) ([]byte, error)
|
||||
}
|
||||
|
||||
func DefaultQueryPlugins(bank bank.ViewKeeper, staking staking.Keeper, wasm Keeper) QueryPlugins {
|
||||
return QueryPlugins{
|
||||
Bank: BankQuerier(bank),
|
||||
Custom: NoCustomQuerier,
|
||||
Staking: StakingQuerier(staking),
|
||||
Wasm: WasmQuerier(wasm),
|
||||
}
|
||||
}
|
||||
|
||||
func (e QueryPlugins) Merge(o *QueryPlugins) QueryPlugins {
|
||||
// only update if this is non-nil and then only set values
|
||||
if o == nil {
|
||||
return e
|
||||
}
|
||||
if o.Bank != nil {
|
||||
e.Bank = o.Bank
|
||||
}
|
||||
if o.Custom != nil {
|
||||
e.Custom = o.Custom
|
||||
}
|
||||
if o.Staking != nil {
|
||||
e.Staking = o.Staking
|
||||
}
|
||||
if o.Wasm != nil {
|
||||
e.Wasm = o.Wasm
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func BankQuerier(bank bank.ViewKeeper) func(ctx sdk.Context, request *wasmTypes.BankQuery) ([]byte, error) {
|
||||
return func(ctx sdk.Context, request *wasmTypes.BankQuery) ([]byte, error) {
|
||||
if request.AllBalances != nil {
|
||||
addr, err := sdk.AccAddressFromBech32(request.AllBalances.Address)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.AllBalances.Address)
|
||||
}
|
||||
coins := bank.GetCoins(ctx, addr)
|
||||
res := wasmTypes.AllBalancesResponse{
|
||||
Amount: convertSdkCoinsToWasmCoins(coins),
|
||||
}
|
||||
return json.Marshal(res)
|
||||
}
|
||||
if request.Balance != nil {
|
||||
addr, err := sdk.AccAddressFromBech32(request.Balance.Address)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Balance.Address)
|
||||
}
|
||||
coins := bank.GetCoins(ctx, addr)
|
||||
amount := coins.AmountOf(request.Balance.Denom)
|
||||
res := wasmTypes.BalanceResponse{
|
||||
Amount: wasmTypes.Coin{
|
||||
Denom: request.Balance.Denom,
|
||||
Amount: amount.String(),
|
||||
},
|
||||
}
|
||||
return json.Marshal(res)
|
||||
}
|
||||
return nil, wasmTypes.UnsupportedRequest{"unknown BankQuery variant"}
|
||||
}
|
||||
}
|
||||
|
||||
func NoCustomQuerier(ctx sdk.Context, request json.RawMessage) ([]byte, error) {
|
||||
return nil, wasmTypes.UnsupportedRequest{"custom"}
|
||||
}
|
||||
|
||||
func StakingQuerier(keeper staking.Keeper) func(ctx sdk.Context, request *wasmTypes.StakingQuery) ([]byte, error) {
|
||||
return func(ctx sdk.Context, request *wasmTypes.StakingQuery) ([]byte, error) {
|
||||
if request.Validators != nil {
|
||||
validators := keeper.GetBondedValidatorsByPower(ctx)
|
||||
wasmVals := make([]wasmTypes.Validator, len(validators))
|
||||
for i, v := range validators {
|
||||
wasmVals[i] = wasmTypes.Validator{
|
||||
Address: v.OperatorAddress.String(),
|
||||
Commission: decToWasm(v.Commission.Rate),
|
||||
MaxCommission: decToWasm(v.Commission.MaxRate),
|
||||
MaxChangeRate: decToWasm(v.Commission.MaxChangeRate),
|
||||
}
|
||||
}
|
||||
res := wasmTypes.ValidatorsResponse{
|
||||
Validators: wasmVals,
|
||||
}
|
||||
return json.Marshal(res)
|
||||
}
|
||||
if request.Delegations != nil {
|
||||
delegator, err := sdk.AccAddressFromBech32(request.Delegations.Delegator)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Delegations.Delegator)
|
||||
}
|
||||
var validator sdk.ValAddress
|
||||
validator, err = sdk.ValAddressFromBech32(request.Delegations.Validator)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Delegations.Validator)
|
||||
}
|
||||
|
||||
delegations, err := calculateDelegations(ctx, keeper, delegator, validator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := wasmTypes.DelegationsResponse{
|
||||
Delegations: delegations,
|
||||
}
|
||||
return json.Marshal(res)
|
||||
}
|
||||
return nil, wasmTypes.UnsupportedRequest{"unknown Staking variant"}
|
||||
}
|
||||
}
|
||||
|
||||
// calculateDelegations does all queries needed to get validation info
|
||||
// delegator must be set, if validator is not set, get all delegations by delegator
|
||||
func calculateDelegations(ctx sdk.Context, keeper staking.Keeper, delegator sdk.AccAddress, validator sdk.ValAddress) (wasmTypes.Delegations, error) {
|
||||
// get delegations (either by validator, or over all validators)
|
||||
var sdkDels []staking.Delegation
|
||||
if len(validator) == 0 {
|
||||
sdkDels = keeper.GetAllDelegatorDelegations(ctx, delegator)
|
||||
} else {
|
||||
d, found := keeper.GetDelegation(ctx, delegator, validator)
|
||||
if found {
|
||||
sdkDels = []staking.Delegation{d}
|
||||
}
|
||||
}
|
||||
|
||||
// convert them to the cosmwasm format, pulling in extra info as needed
|
||||
delegations := make([]wasmTypes.Delegation, len(sdkDels))
|
||||
for i, d := range sdkDels {
|
||||
// shares to amount logic comes from here:
|
||||
// https://github.com/cosmos/cosmos-sdk/blob/v0.38.3/x/staking/keeper/querier.go#L404
|
||||
val, found := keeper.GetValidator(ctx, d.ValidatorAddress)
|
||||
if !found {
|
||||
return nil, sdkerrors.Wrap(staking.ErrNoValidatorFound, "can't load validator for delegation")
|
||||
}
|
||||
bondDenom := keeper.BondDenom(ctx)
|
||||
amount := sdk.NewCoin(bondDenom, val.TokensFromShares(d.Shares).TruncateInt())
|
||||
|
||||
// Accumulated Rewards???
|
||||
|
||||
// can relegate? other query for redelegations?
|
||||
// keeper.GetRedelegation
|
||||
|
||||
delegations[i] = wasmTypes.Delegation{
|
||||
Delegator: d.DelegatorAddress.String(),
|
||||
Validator: d.ValidatorAddress.String(),
|
||||
Amount: convertSdkCoinToWasmCoin(amount),
|
||||
// TODO: AccumulatedRewards
|
||||
AccumulatedRewards: wasmTypes.NewCoin(0, bondDenom),
|
||||
// TODO: Determine redelegate
|
||||
CanRedelegate: false,
|
||||
}
|
||||
}
|
||||
return delegations, nil
|
||||
}
|
||||
|
||||
func WasmQuerier(wasm Keeper) func(ctx sdk.Context, request *wasmTypes.WasmQuery) ([]byte, error) {
|
||||
return func(ctx sdk.Context, request *wasmTypes.WasmQuery) ([]byte, error) {
|
||||
if request.Smart != nil {
|
||||
addr, err := sdk.AccAddressFromBech32(request.Smart.ContractAddr)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Smart.ContractAddr)
|
||||
}
|
||||
return wasm.QuerySmart(ctx, addr, request.Smart.Msg)
|
||||
}
|
||||
if request.Raw != nil {
|
||||
addr, err := sdk.AccAddressFromBech32(request.Raw.ContractAddr)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Raw.ContractAddr)
|
||||
}
|
||||
models := wasm.QueryRaw(ctx, addr, request.Raw.Key)
|
||||
// TODO: do we want to change the return value?
|
||||
return json.Marshal(models)
|
||||
}
|
||||
return nil, wasmTypes.UnsupportedRequest{"unknown WasmQuery variant"}
|
||||
}
|
||||
}
|
||||
|
||||
func convertSdkCoinsToWasmCoins(coins []sdk.Coin) wasmTypes.Coins {
|
||||
converted := make(wasmTypes.Coins, len(coins))
|
||||
for i, c := range coins {
|
||||
converted[i] = convertSdkCoinToWasmCoin(c)
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func convertSdkCoinToWasmCoin(coin sdk.Coin) wasmTypes.Coin {
|
||||
return wasmTypes.Coin{
|
||||
Denom: coin.Denom,
|
||||
Amount: coin.Amount.String(),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move this into go-cosmwasm, so it stays close to the definitions
|
||||
var WasmDecMultiplier int64 = 1_000_000
|
||||
|
||||
// Take the bigDec type and fit it into the wasm uint64 type (
|
||||
func decToWasm(dec sdk.Dec) uint64 {
|
||||
mul := dec.MulInt64(WasmDecMultiplier).TruncateInt64()
|
||||
if mul < 0 {
|
||||
panic("Try to conver negative value to uint64")
|
||||
}
|
||||
return uint64(mul)
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
@@ -30,15 +35,19 @@ func MakeTestCodec() *codec.Codec {
|
||||
// cdc.RegisterConcrete(&auth.BaseAccount{}, "test/wasm/BaseAccount", nil)
|
||||
auth.AppModuleBasic{}.RegisterCodec(cdc)
|
||||
bank.AppModuleBasic{}.RegisterCodec(cdc)
|
||||
wasmTypes.RegisterCodec(cdc)
|
||||
sdk.RegisterCodec(cdc)
|
||||
codec.RegisterCrypto(cdc)
|
||||
|
||||
return cdc
|
||||
}
|
||||
|
||||
func CreateTestInput(t *testing.T, isCheckTx bool, tempDir string) (sdk.Context, auth.AccountKeeper, Keeper) {
|
||||
// encoders can be nil to accept the defaults, or set it to override some of the message handlers (like default)
|
||||
func CreateTestInput(t *testing.T, isCheckTx bool, tempDir string, encoders *MessageEncoders, queriers *QueryPlugins) (sdk.Context, auth.AccountKeeper, Keeper) {
|
||||
keyContract := sdk.NewKVStoreKey(types.StoreKey)
|
||||
keyAcc := sdk.NewKVStoreKey(auth.StoreKey)
|
||||
keyStaking := sdk.NewKVStoreKey(staking.StoreKey)
|
||||
keySupply := sdk.NewKVStoreKey(supply.StoreKey)
|
||||
keyParams := sdk.NewKVStoreKey(params.StoreKey)
|
||||
tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
|
||||
|
||||
@@ -51,7 +60,10 @@ func CreateTestInput(t *testing.T, isCheckTx bool, tempDir string) (sdk.Context,
|
||||
err := ms.LoadLatestVersion()
|
||||
require.Nil(t, err)
|
||||
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, isCheckTx, log.NewNopLogger())
|
||||
ctx := sdk.NewContext(ms, abci.Header{
|
||||
Height: 1234567,
|
||||
Time: time.Date(2020, time.April, 22, 12, 0, 0, 0, time.UTC),
|
||||
}, isCheckTx, log.NewNopLogger())
|
||||
cdc := MakeTestCodec()
|
||||
|
||||
pk := params.NewKeeper(cdc, keyParams, tkeyParams)
|
||||
@@ -63,22 +75,80 @@ func CreateTestInput(t *testing.T, isCheckTx bool, tempDir string) (sdk.Context,
|
||||
auth.ProtoBaseAccount, // prototype
|
||||
)
|
||||
|
||||
bk := bank.NewBaseKeeper(
|
||||
bankKeeper := bank.NewBaseKeeper(
|
||||
accountKeeper,
|
||||
pk.Subspace(bank.DefaultParamspace),
|
||||
nil,
|
||||
)
|
||||
bk.SetSendEnabled(ctx, true)
|
||||
bankKeeper.SetSendEnabled(ctx, true)
|
||||
|
||||
maccPerms := map[string][]string{
|
||||
//mint.ModuleName: {supply.Minter},
|
||||
staking.BondedPoolName: {supply.Burner, supply.Staking},
|
||||
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
||||
//gov.ModuleName: {supply.Burner},
|
||||
}
|
||||
|
||||
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms)
|
||||
stakingKeeper := staking.NewKeeper(cdc, keyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace))
|
||||
|
||||
// TODO: register more than bank.send
|
||||
router := baseapp.NewRouter()
|
||||
h := bank.NewHandler(bk)
|
||||
router.AddRoute(bank.RouterKey, h)
|
||||
bh := bank.NewHandler(bankKeeper)
|
||||
router.AddRoute(bank.RouterKey, bh)
|
||||
sh := staking.NewHandler(stakingKeeper)
|
||||
router.AddRoute(staking.RouterKey, sh)
|
||||
|
||||
// Load default wasm config
|
||||
wasmConfig := wasmTypes.DefaultWasmConfig()
|
||||
|
||||
keeper := NewKeeper(cdc, keyContract, accountKeeper, bk, router, tempDir, wasmConfig)
|
||||
keeper := NewKeeper(cdc, keyContract, accountKeeper, bankKeeper, stakingKeeper, router, tempDir, wasmConfig, encoders, queriers)
|
||||
// add wasm handler so we can loop-back (contracts calling contracts)
|
||||
router.AddRoute(wasmTypes.RouterKey, TestHandler(keeper))
|
||||
|
||||
return ctx, accountKeeper, keeper
|
||||
}
|
||||
|
||||
// TestHandler returns a wasm handler for tests (to avoid circular imports)
|
||||
func TestHandler(k Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case wasmTypes.MsgInstantiateContract:
|
||||
return handleInstantiate(ctx, k, &msg)
|
||||
case *wasmTypes.MsgInstantiateContract:
|
||||
return handleInstantiate(ctx, k, msg)
|
||||
|
||||
case wasmTypes.MsgExecuteContract:
|
||||
return handleExecute(ctx, k, &msg)
|
||||
case *wasmTypes.MsgExecuteContract:
|
||||
return handleExecute(ctx, k, msg)
|
||||
|
||||
default:
|
||||
errMsg := fmt.Sprintf("unrecognized wasm message type: %T", msg)
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleInstantiate(ctx sdk.Context, k Keeper, msg *wasmTypes.MsgInstantiateContract) (*sdk.Result, error) {
|
||||
contractAddr, err := k.Instantiate(ctx, msg.Code, msg.Sender, msg.InitMsg, msg.Label, msg.InitFunds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &sdk.Result{
|
||||
Data: contractAddr,
|
||||
Events: ctx.EventManager().Events(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func handleExecute(ctx sdk.Context, k Keeper, msg *wasmTypes.MsgExecuteContract) (*sdk.Result, error) {
|
||||
res, err := k.Execute(ctx, msg.Contract, msg.Sender, msg.Msg, msg.SentFunds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res.Events = ctx.EventManager().Events()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
BIN
x/wasm/internal/keeper/testdata/contract.wasm
vendored
BIN
x/wasm/internal/keeper/testdata/contract.wasm
vendored
Binary file not shown.
BIN
x/wasm/internal/keeper/testdata/contract.wasm.gzip
vendored
BIN
x/wasm/internal/keeper/testdata/contract.wasm.gzip
vendored
Binary file not shown.
BIN
x/wasm/internal/keeper/testdata/mask.wasm
vendored
BIN
x/wasm/internal/keeper/testdata/mask.wasm
vendored
Binary file not shown.
@@ -31,4 +31,7 @@ var (
|
||||
|
||||
// ErrQueryFailed error for rust smart query contract failure
|
||||
ErrQueryFailed = sdkErrors.Register(DefaultCodespace, 8, "query wasm contract failed")
|
||||
|
||||
// ErrInvalidMsg error when we cannot process the error returned from the contract
|
||||
ErrInvalidMsg = sdkErrors.Register(DefaultCodespace, 9, "invalid CosmosMsg from the contract")
|
||||
)
|
||||
|
||||
@@ -2,12 +2,10 @@ package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
tmBytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
|
||||
wasmTypes "github.com/confio/go-cosmwasm/types"
|
||||
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||
)
|
||||
|
||||
const defaultLRUCacheSize = uint64(0)
|
||||
@@ -94,23 +92,23 @@ func NewContractInfo(codeID uint64, creator sdk.AccAddress, initMsg []byte, labe
|
||||
}
|
||||
}
|
||||
|
||||
// NewParams initializes params for a contract instance
|
||||
func NewParams(ctx sdk.Context, creator sdk.AccAddress, deposit sdk.Coins, contractAcct auth.Account) wasmTypes.Env {
|
||||
return wasmTypes.Env{
|
||||
// NewEnv initializes the environment for a contract instance
|
||||
func NewEnv(ctx sdk.Context, creator sdk.AccAddress, deposit sdk.Coins, contractAddr sdk.AccAddress) wasmTypes.Env {
|
||||
env := wasmTypes.Env{
|
||||
Block: wasmTypes.BlockInfo{
|
||||
Height: ctx.BlockHeight(),
|
||||
Time: ctx.BlockTime().Unix(),
|
||||
ChainID: ctx.ChainID(),
|
||||
},
|
||||
Message: wasmTypes.MessageInfo{
|
||||
Signer: wasmTypes.CanonicalAddress(creator),
|
||||
Sender: wasmTypes.CanonicalAddress(creator),
|
||||
SentFunds: NewWasmCoins(deposit),
|
||||
},
|
||||
Contract: wasmTypes.ContractInfo{
|
||||
Address: wasmTypes.CanonicalAddress(contractAcct.GetAddress()),
|
||||
Balance: NewWasmCoins(contractAcct.GetCoins()),
|
||||
Address: wasmTypes.CanonicalAddress(contractAddr),
|
||||
},
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
// NewWasmCoins translates between Cosmos SDK coins and Wasm coins
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
wasmTypes "github.com/confio/go-cosmwasm/types"
|
||||
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
@@ -34,7 +34,7 @@ func setupTest(t *testing.T) (testData, func()) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx, acctKeeper, keeper := CreateTestInput(t, false, tempDir)
|
||||
ctx, acctKeeper, keeper := CreateTestInput(t, false, tempDir, nil, nil)
|
||||
data := testData{
|
||||
module: NewAppModule(keeper),
|
||||
ctx: ctx,
|
||||
@@ -63,7 +63,8 @@ func mustLoad(path string) []byte {
|
||||
var (
|
||||
key1, pub1, addr1 = keyPubAddr()
|
||||
testContract = mustLoad("./internal/keeper/testdata/contract.wasm")
|
||||
escrowContract = mustLoad("./testdata/escrow.wasm")
|
||||
maskContract = mustLoad("./internal/keeper/testdata/mask.wasm")
|
||||
oldContract = mustLoad("./testdata/escrow_0.7.wasm")
|
||||
)
|
||||
|
||||
func TestHandleCreate(t *testing.T) {
|
||||
@@ -92,10 +93,17 @@ func TestHandleCreate(t *testing.T) {
|
||||
"other valid wasm": {
|
||||
msg: MsgStoreCode{
|
||||
Sender: addr1,
|
||||
WASMByteCode: escrowContract,
|
||||
WASMByteCode: maskContract,
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
"old wasm (0.7)": {
|
||||
msg: MsgStoreCode{
|
||||
Sender: addr1,
|
||||
WASMByteCode: oldContract,
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
@@ -310,7 +318,7 @@ func TestHandleExecuteEscrow(t *testing.T) {
|
||||
|
||||
msg := MsgStoreCode{
|
||||
Sender: creator,
|
||||
WASMByteCode: escrowContract,
|
||||
WASMByteCode: testContract,
|
||||
}
|
||||
res, err := h(data.ctx, &msg)
|
||||
require.NoError(t, err)
|
||||
@@ -318,10 +326,8 @@ func TestHandleExecuteEscrow(t *testing.T) {
|
||||
|
||||
_, _, bob := keyPubAddr()
|
||||
initMsg := map[string]interface{}{
|
||||
"arbiter": fred.String(),
|
||||
"recipient": bob.String(),
|
||||
"end_time": 0,
|
||||
"end_height": 0,
|
||||
"verifier": fred.String(),
|
||||
"beneficiary": bob.String(),
|
||||
}
|
||||
initMsgBz, err := json.Marshal(initMsg)
|
||||
require.NoError(t, err)
|
||||
@@ -338,7 +344,7 @@ func TestHandleExecuteEscrow(t *testing.T) {
|
||||
require.Equal(t, "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", contractAddr.String())
|
||||
|
||||
handleMsg := map[string]interface{}{
|
||||
"approve": map[string]interface{}{},
|
||||
"release": map[string]interface{}{},
|
||||
}
|
||||
handleMsgBz, err := json.Marshal(handleMsg)
|
||||
require.NoError(t, err)
|
||||
@@ -362,19 +368,6 @@ func TestHandleExecuteEscrow(t *testing.T) {
|
||||
contractAcct := data.acctKeeper.GetAccount(data.ctx, contractAddr)
|
||||
require.NotNil(t, contractAcct)
|
||||
assert.Equal(t, sdk.Coins(nil), contractAcct.GetCoins())
|
||||
|
||||
// q := data.module.NewQuerierHandler()
|
||||
// // ensure all contract state is as after init
|
||||
// assertCodeList(t, q, data.ctx, 1)
|
||||
// assertCodeBytes(t, q, data.ctx, 1, testContract)
|
||||
|
||||
// assertContractList(t, q, data.ctx, []string{contractAddr.String()})
|
||||
// assertContractInfo(t, q, data.ctx, contractAddr, 1, creator)
|
||||
// assertContractState(t, q, data.ctx, contractAddr, state{
|
||||
// Verifier: fred.String(),
|
||||
// Beneficiary: bob.String(),
|
||||
// Funder: creator.String(),
|
||||
// })
|
||||
}
|
||||
|
||||
type prettyEvent struct {
|
||||
|
||||
Reference in New Issue
Block a user