Merge pull request #111 from CosmWasm/upgrade-cosmwasm-0.8

Upgrade cosmwasm 0.8
This commit is contained in:
Ethan Frey
2020-05-12 22:40:49 +02:00
committed by GitHub
22 changed files with 1261 additions and 332 deletions

View File

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

View File

@@ -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
View File

@@ -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
View File

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

View File

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

View File

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

View 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
}

View 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)
}
})
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View 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)
}

View File

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

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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