Integrate wasmvm v1.2.0 (#1161)

* Bump wasmvm version

* Bump wasm test contracts

* Encode weighted votes

* Encode instantiate2

* Handle code info query; better wasmvm errors

* Fix readme

* Make linter happy

* add non cgo build

* Review comments

* Bump wasmvm to release version

Co-authored-by: jhernandezb <contact@jhernandez.me>
This commit is contained in:
Alexander Peters 2023-01-25 10:01:26 +01:00 committed by GitHub
parent a925a9ed61
commit 957b38e0a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 688 additions and 142 deletions

View File

@ -14,12 +14,11 @@ RUN apk add git
WORKDIR /code
COPY . /code/
# See https://github.com/CosmWasm/wasmvm/releases
ADD https://github.com/CosmWasm/wasmvm/releases/download/v1.1.1/libwasmvm_muslc.aarch64.a /lib/libwasmvm_muslc.aarch64.a
ADD https://github.com/CosmWasm/wasmvm/releases/download/v1.1.1/libwasmvm_muslc.x86_64.a /lib/libwasmvm_muslc.x86_64.a
RUN sha256sum /lib/libwasmvm_muslc.aarch64.a | grep 9ecb037336bd56076573dc18c26631a9d2099a7f2b40dc04b6cae31ffb4c8f9a
RUN sha256sum /lib/libwasmvm_muslc.x86_64.a | grep 6e4de7ba9bad4ae9679c7f9ecf7e283dd0160e71567c6a7be6ae47c81ebe7f32
ADD https://github.com/CosmWasm/wasmvm/releases/download/v1.2.0/libwasmvm_muslc.aarch64.a /lib/libwasmvm_muslc.aarch64.a
ADD https://github.com/CosmWasm/wasmvm/releases/download/v1.2.0/libwasmvm_muslc.x86_64.a /lib/libwasmvm_muslc.x86_64.a
RUN sha256sum /lib/libwasmvm_muslc.aarch64.a | grep cba4b334893456c64df177939cbdd09afe4812432c02ae37d60d69a111b1b50d
RUN sha256sum /lib/libwasmvm_muslc.x86_64.a | grep 6f87082f7a62602f9725d529677f330b9c4dd4607887be52a86328c6c919495b
# Copy the library you want to the final location that will be found by the linker flag `-lwasmvm_muslc`
RUN cp /lib/libwasmvm_muslc.${arch}.a /lib/libwasmvm_muslc.a

View File

@ -1,3 +1,4 @@
# Wasm Zone
[![CircleCI](https://circleci.com/gh/CosmWasm/wasmd/tree/main.svg?style=shield)](https://circleci.com/gh/CosmWasm/wasmd/tree/main)
@ -26,6 +27,7 @@ compatibility list:
| wasmd | wasmvm | cosmwasm-vm | cosmwasm-std |
|-------|--------------|-------------|--------------|
| 0.31 | v1.2.0 | | 1.0-1.2 |
| 0.30 | v1.1.0 | | 1.0-1.1 |
| 0.29 | v1.1.0 | | 1.0-1.1 |
| 0.28 | v1.0.0 | | 1.0-1.1 |

View File

@ -513,7 +513,8 @@ func NewWasmApp(
// The last arguments can contain custom message handlers, and custom query handlers,
// if we want to allow any custom callbacks
availableCapabilities := "iterator,staking,stargate,cosmwasm_1_1"
// See https://github.com/CosmWasm/cosmwasm/blob/main/docs/CAPABILITIES-BUILT-IN.md
availableCapabilities := "iterator,staking,stargate,cosmwasm_1_1,cosmwasm_1_2"
app.WasmKeeper = wasm.NewKeeper(
appCodec,
keys[wasm.StoreKey],

4
go.mod
View File

@ -3,7 +3,7 @@ module github.com/CosmWasm/wasmd
go 1.19
require (
github.com/CosmWasm/wasmvm v1.1.1
github.com/CosmWasm/wasmvm v1.2.0
github.com/cosmos/cosmos-proto v1.0.0-beta.1
github.com/cosmos/cosmos-sdk v0.45.11
github.com/cosmos/gogoproto v1.4.3
@ -24,7 +24,6 @@ require (
github.com/spf13/cast v1.5.0
github.com/spf13/cobra v1.6.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.14.0
github.com/stretchr/testify v1.8.1
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
github.com/tendermint/tendermint v0.34.23
@ -111,6 +110,7 @@ require (
github.com/sasha-s/go-deadlock v0.3.1 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/viper v1.14.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/tendermint/btcd v0.1.1 // indirect
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 // indirect

4
go.sum
View File

@ -59,8 +59,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg=
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
github.com/CosmWasm/wasmvm v1.1.1 h1:0xtdrmmsP9fibe+x42WcMkp5aQ738BICgcH3FNVLzm4=
github.com/CosmWasm/wasmvm v1.1.1/go.mod h1:ei0xpvomwSdONsxDuONzV7bL1jSET1M8brEx0FCXc+A=
github.com/CosmWasm/wasmvm v1.2.0 h1:pNCp175id+r/dSa4Ii5zoTkmauOoeipkvepvEJM1bao=
github.com/CosmWasm/wasmvm v1.2.0/go.mod h1:OIhXFPi9BbcEL1USBj4OIrBTtSSds+9eEql56fsdyfE=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=

View File

@ -104,7 +104,7 @@ func TestParseCodeInfoFlags(t *testing.T) {
wasmBin, err := os.ReadFile("../../keeper/testdata/hackatom.wasm")
require.NoError(t, err)
checksumStr := "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5"
checksumStr := "beb3de5e9b93b52e514c74ce87ccddb594b9bcd33b7f1af1bb6da63fc883917b"
specs := map[string]struct {
args []string

View File

@ -237,6 +237,24 @@ func EncodeWasmMsg(sender sdk.AccAddress, msg *wasmvmtypes.WasmMsg) ([]sdk.Msg,
Funds: coins,
}
return []sdk.Msg{&sdkMsg}, nil
case msg.Instantiate2 != nil:
coins, err := ConvertWasmCoinsToSdkCoins(msg.Instantiate2.Funds)
if err != nil {
return nil, err
}
sdkMsg := types.MsgInstantiateContract2{
Sender: sender.String(),
Admin: msg.Instantiate2.Admin,
CodeID: msg.Instantiate2.CodeID,
Label: msg.Instantiate2.Label,
Msg: msg.Instantiate2.Msg,
Funds: coins,
Salt: msg.Instantiate2.Salt,
// FixMsg is discouraged, see: https://medium.com/cosmwasm/dev-note-3-limitations-of-instantiate2-and-how-to-deal-with-them-a3f946874230
FixMsg: false,
}
return []sdk.Msg{&sdkMsg}, nil
case msg.Migrate != nil:
sdkMsg := types.MsgMigrateContract{
Sender: sender.String(),
@ -288,14 +306,44 @@ func EncodeIBCMsg(portSource types.ICS20TransferPortSource) func(ctx sdk.Context
}
return []sdk.Msg{msg}, nil
default:
return nil, sdkerrors.Wrap(types.ErrUnknownMsg, "Unknown variant of IBC")
return nil, sdkerrors.Wrap(types.ErrUnknownMsg, "unknown variant of IBC")
}
}
}
func EncodeGovMsg(sender sdk.AccAddress, msg *wasmvmtypes.GovMsg) ([]sdk.Msg, error) {
switch {
case msg.Vote != nil:
voteOption, err := convertVoteOption(msg.Vote.Vote)
if err != nil {
return nil, sdkerrors.Wrap(err, "vote option")
}
m := govtypes.NewMsgVote(sender, msg.Vote.ProposalId, voteOption)
return []sdk.Msg{m}, nil
case msg.VoteWeighted != nil:
opts := make([]govtypes.WeightedVoteOption, len(msg.VoteWeighted.Options))
for i, v := range msg.VoteWeighted.Options {
weight, err := sdk.NewDecFromStr(v.Weight)
if err != nil {
return nil, sdkerrors.Wrapf(err, "weight for vote %d", i+1)
}
voteOption, err := convertVoteOption(v.Option)
if err != nil {
return nil, sdkerrors.Wrap(err, "vote option")
}
opts[i] = govtypes.WeightedVoteOption{Option: voteOption, Weight: weight}
}
m := govtypes.NewMsgVoteWeighted(sender, msg.VoteWeighted.ProposalId, opts)
return []sdk.Msg{m}, nil
default:
return nil, types.ErrUnknownMsg.Wrap("unknown variant of gov")
}
}
func convertVoteOption(s interface{}) (govtypes.VoteOption, error) {
var option govtypes.VoteOption
switch msg.Vote.Vote {
switch s {
case wasmvmtypes.Yes:
option = govtypes.OptionYes
case wasmvmtypes.No:
@ -304,13 +352,10 @@ func EncodeGovMsg(sender sdk.AccAddress, msg *wasmvmtypes.GovMsg) ([]sdk.Msg, er
option = govtypes.OptionNoWithVeto
case wasmvmtypes.Abstain:
option = govtypes.OptionAbstain
default:
return govtypes.OptionEmpty, types.ErrInvalid
}
vote := &govtypes.MsgVote{
ProposalId: msg.Vote.ProposalId,
Voter: sender.String(),
Option: option,
}
return []sdk.Msg{vote}, nil
return option, nil
}
// ConvertWasmIBCTimeoutHeightToCosmosHeight converts a wasmvm type ibc timeout height to ibc module type height

View File

@ -65,8 +65,10 @@ func TestEncoding(t *testing.T) {
transferPortSource types.ICS20TransferPortSource
// set if valid
output []sdk.Msg
// set if invalid
isError bool
// set if expect mapping fails
expError bool
// set if sdk validate basic should fail
expInvalid bool
}{
"simple send": {
sender: addr1,
@ -113,7 +115,7 @@ func TestEncoding(t *testing.T) {
},
},
},
isError: true,
expError: true,
},
"invalid address": {
sender: addr1,
@ -130,7 +132,8 @@ func TestEncoding(t *testing.T) {
},
},
},
isError: false, // addresses are checked in the handler
expError: false, // addresses are checked in the handler
expInvalid: true,
output: []sdk.Msg{
&banktypes.MsgSend{
FromAddress: addr1.String(),
@ -189,6 +192,35 @@ func TestEncoding(t *testing.T) {
},
},
},
"wasm instantiate2": {
sender: addr1,
srcMsg: wasmvmtypes.CosmosMsg{
Wasm: &wasmvmtypes.WasmMsg{
Instantiate2: &wasmvmtypes.Instantiate2Msg{
CodeID: 7,
Msg: jsonMsg,
Funds: []wasmvmtypes.Coin{
wasmvmtypes.NewCoin(123, "eth"),
},
Label: "myLabel",
Admin: addr2.String(),
Salt: []byte("mySalt"),
},
},
},
output: []sdk.Msg{
&types.MsgInstantiateContract2{
Sender: addr1.String(),
Admin: addr2.String(),
CodeID: 7,
Label: "myLabel",
Msg: jsonMsg,
Funds: sdk.NewCoins(sdk.NewInt64Coin("eth", 123)),
Salt: []byte("mySalt"),
FixMsg: false,
},
},
},
"wasm migrate": {
sender: addr2,
srcMsg: wasmvmtypes.CosmosMsg{
@ -271,7 +303,7 @@ func TestEncoding(t *testing.T) {
},
},
},
isError: false, // fails in the handler
expError: false, // fails in the handler
output: []sdk.Msg{
&stakingtypes.MsgDelegate{
DelegatorAddress: addr1.String(),
@ -378,7 +410,7 @@ func TestEncoding(t *testing.T) {
Value: bankMsgBin,
},
},
isError: true,
expError: true,
},
"IBC transfer with block timeout": {
sender: addr1,
@ -500,9 +532,49 @@ func TestEncoding(t *testing.T) {
},
},
},
}
encodingConfig := MakeEncodingConfig(t)
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
var ctx sdk.Context
encoder := DefaultEncoders(encodingConfig.Marshaler, tc.transferPortSource)
res, err := encoder.Encode(ctx, tc.sender, tc.srcContractIBCPort, tc.srcMsg)
if tc.expError {
assert.Error(t, err)
return
} else {
require.NoError(t, err)
assert.Equal(t, tc.output, res)
}
// and valid sdk message
for _, v := range res {
gotErr := v.ValidateBasic()
if tc.expInvalid {
assert.Error(t, gotErr)
} else {
assert.NoError(t, gotErr)
}
}
})
}
}
func TestEncodeGovMsg(t *testing.T) {
myAddr := RandomAccountAddress(t)
cases := map[string]struct {
sender sdk.AccAddress
srcMsg wasmvmtypes.CosmosMsg
transferPortSource types.ICS20TransferPortSource
// set if valid
output []sdk.Msg
// set if expect mapping fails
expError bool
// set if sdk validate basic should fail
expInvalid bool
}{
"Gov vote: yes": {
sender: addr1,
srcContractIBCPort: "myIBCPort",
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
Vote: &wasmvmtypes.VoteMsg{ProposalId: 1, Vote: wasmvmtypes.Yes},
@ -511,14 +583,13 @@ func TestEncoding(t *testing.T) {
output: []sdk.Msg{
&govtypes.MsgVote{
ProposalId: 1,
Voter: addr1.String(),
Voter: myAddr.String(),
Option: govtypes.OptionYes,
},
},
},
"Gov vote: No": {
sender: addr1,
srcContractIBCPort: "myIBCPort",
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
Vote: &wasmvmtypes.VoteMsg{ProposalId: 1, Vote: wasmvmtypes.No},
@ -527,14 +598,13 @@ func TestEncoding(t *testing.T) {
output: []sdk.Msg{
&govtypes.MsgVote{
ProposalId: 1,
Voter: addr1.String(),
Voter: myAddr.String(),
Option: govtypes.OptionNo,
},
},
},
"Gov vote: Abstain": {
sender: addr1,
srcContractIBCPort: "myIBCPort",
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
Vote: &wasmvmtypes.VoteMsg{ProposalId: 10, Vote: wasmvmtypes.Abstain},
@ -543,14 +613,13 @@ func TestEncoding(t *testing.T) {
output: []sdk.Msg{
&govtypes.MsgVote{
ProposalId: 10,
Voter: addr1.String(),
Voter: myAddr.String(),
Option: govtypes.OptionAbstain,
},
},
},
"Gov vote: No with veto": {
sender: addr1,
srcContractIBCPort: "myIBCPort",
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
Vote: &wasmvmtypes.VoteMsg{ProposalId: 1, Vote: wasmvmtypes.NoWithVeto},
@ -559,24 +628,168 @@ func TestEncoding(t *testing.T) {
output: []sdk.Msg{
&govtypes.MsgVote{
ProposalId: 1,
Voter: addr1.String(),
Voter: myAddr.String(),
Option: govtypes.OptionNoWithVeto,
},
},
},
"Gov vote: unset option": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
Vote: &wasmvmtypes.VoteMsg{ProposalId: 1},
},
},
expError: true,
},
"Gov weighted vote: single vote": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
VoteWeighted: &wasmvmtypes.VoteWeightedMsg{
ProposalId: 1,
Options: []wasmvmtypes.WeightedVoteOption{
{Option: wasmvmtypes.Yes, Weight: "1"},
},
},
},
},
output: []sdk.Msg{
&govtypes.MsgVoteWeighted{
ProposalId: 1,
Voter: myAddr.String(),
Options: []govtypes.WeightedVoteOption{
{Option: govtypes.OptionYes, Weight: sdk.NewDec(1)},
},
},
},
},
"Gov weighted vote: splitted": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
VoteWeighted: &wasmvmtypes.VoteWeightedMsg{
ProposalId: 1,
Options: []wasmvmtypes.WeightedVoteOption{
{Option: wasmvmtypes.Yes, Weight: "0.23"},
{Option: wasmvmtypes.No, Weight: "0.24"},
{Option: wasmvmtypes.Abstain, Weight: "0.26"},
{Option: wasmvmtypes.NoWithVeto, Weight: "0.27"},
},
},
},
},
output: []sdk.Msg{
&govtypes.MsgVoteWeighted{
ProposalId: 1,
Voter: myAddr.String(),
Options: []govtypes.WeightedVoteOption{
{Option: govtypes.OptionYes, Weight: sdk.NewDecWithPrec(23, 2)},
{Option: govtypes.OptionNo, Weight: sdk.NewDecWithPrec(24, 2)},
{Option: govtypes.OptionAbstain, Weight: sdk.NewDecWithPrec(26, 2)},
{Option: govtypes.OptionNoWithVeto, Weight: sdk.NewDecWithPrec(27, 2)},
},
},
},
},
"Gov weighted vote: duplicate option": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
VoteWeighted: &wasmvmtypes.VoteWeightedMsg{
ProposalId: 1,
Options: []wasmvmtypes.WeightedVoteOption{
{Option: wasmvmtypes.Yes, Weight: "0.5"},
{Option: wasmvmtypes.Yes, Weight: "0.5"},
},
},
},
},
output: []sdk.Msg{
&govtypes.MsgVoteWeighted{
ProposalId: 1,
Voter: myAddr.String(),
Options: []govtypes.WeightedVoteOption{
{Option: govtypes.OptionYes, Weight: sdk.NewDecWithPrec(5, 1)},
{Option: govtypes.OptionYes, Weight: sdk.NewDecWithPrec(5, 1)},
},
},
},
expInvalid: true,
},
"Gov weighted vote: weight sum exceeds 1": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
VoteWeighted: &wasmvmtypes.VoteWeightedMsg{
ProposalId: 1,
Options: []wasmvmtypes.WeightedVoteOption{
{Option: wasmvmtypes.Yes, Weight: "0.51"},
{Option: wasmvmtypes.No, Weight: "0.5"},
},
},
},
},
output: []sdk.Msg{
&govtypes.MsgVoteWeighted{
ProposalId: 1,
Voter: myAddr.String(),
Options: []govtypes.WeightedVoteOption{
{Option: govtypes.OptionYes, Weight: sdk.NewDecWithPrec(51, 2)},
{Option: govtypes.OptionNo, Weight: sdk.NewDecWithPrec(5, 1)},
},
},
},
expInvalid: true,
},
"Gov weighted vote: weight sum less than 1": {
sender: myAddr,
srcMsg: wasmvmtypes.CosmosMsg{
Gov: &wasmvmtypes.GovMsg{
VoteWeighted: &wasmvmtypes.VoteWeightedMsg{
ProposalId: 1,
Options: []wasmvmtypes.WeightedVoteOption{
{Option: wasmvmtypes.Yes, Weight: "0.49"},
{Option: wasmvmtypes.No, Weight: "0.5"},
},
},
},
},
output: []sdk.Msg{
&govtypes.MsgVoteWeighted{
ProposalId: 1,
Voter: myAddr.String(),
Options: []govtypes.WeightedVoteOption{
{Option: govtypes.OptionYes, Weight: sdk.NewDecWithPrec(49, 2)},
{Option: govtypes.OptionNo, Weight: sdk.NewDecWithPrec(5, 1)},
},
},
},
expInvalid: true,
},
}
encodingConfig := MakeEncodingConfig(t)
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
var ctx sdk.Context
encoder := DefaultEncoders(encodingConfig.Marshaler, tc.transferPortSource)
res, err := encoder.Encode(ctx, tc.sender, tc.srcContractIBCPort, tc.srcMsg)
if tc.isError {
require.Error(t, err)
res, gotEncErr := encoder.Encode(ctx, tc.sender, "myIBCPort", tc.srcMsg)
if tc.expError {
assert.Error(t, gotEncErr)
return
} else {
require.NoError(t, err)
require.NoError(t, gotEncErr)
assert.Equal(t, tc.output, res)
}
// and valid sdk message
for _, v := range res {
gotErr := v.ValidateBasic()
if tc.expInvalid {
assert.Error(t, gotErr)
} else {
assert.NoError(t, gotErr)
}
}
})
}
}

View File

@ -7,7 +7,6 @@ import (
"encoding/hex"
"fmt"
"math"
"path/filepath"
"reflect"
"strconv"
"strings"
@ -104,61 +103,6 @@ type Keeper struct {
accountPruner AccountPruner
}
// 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,
paramSpace paramtypes.Subspace,
accountKeeper types.AccountKeeper,
bankKeeper types.BankKeeper,
stakingKeeper types.StakingKeeper,
distKeeper types.DistributionKeeper,
channelKeeper types.ChannelKeeper,
portKeeper types.PortKeeper,
capabilityKeeper types.CapabilityKeeper,
portSource types.ICS20TransferPortSource,
router MessageRouter,
_ GRPCQueryRouter,
homeDir string,
wasmConfig types.WasmConfig,
availableCapabilities string,
opts ...Option,
) Keeper {
wasmer, err := wasmvm.NewVM(filepath.Join(homeDir, "wasm"), availableCapabilities, contractMemoryLimit, wasmConfig.ContractDebugMode, wasmConfig.MemoryCacheSize)
if err != nil {
panic(err)
}
// set KeyTable if it has not already been set
if !paramSpace.HasKeyTable() {
paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
}
keeper := &Keeper{
storeKey: storeKey,
cdc: cdc,
wasmVM: wasmer,
accountKeeper: accountKeeper,
bank: NewBankCoinTransferrer(bankKeeper),
accountPruner: NewVestingCoinBurner(bankKeeper),
portKeeper: portKeeper,
capabilityKeeper: capabilityKeeper,
messenger: NewDefaultMessageHandler(router, channelKeeper, capabilityKeeper, bankKeeper, cdc, portSource),
queryGasLimit: wasmConfig.SmartQueryGasLimit,
paramSpace: paramSpace,
gasRegister: NewDefaultWasmGasRegister(),
maxQueryStackSize: types.DefaultMaxQueryStackSize,
acceptedAccountTypes: defaultAcceptedAccountTypes,
}
keeper.wasmVMQueryHandler = DefaultQueryPlugins(bankKeeper, stakingKeeper, distKeeper, channelKeeper, keeper)
for _, o := range opts {
o.apply(keeper)
}
// not updateable, yet
keeper.wasmVMResponseHandler = NewDefaultWasmVMContractResponseHandler(NewMessageDispatcher(keeper.messenger, keeper))
return *keeper
}
func (k Keeper) getUploadAccessConfig(ctx sdk.Context) types.AccessConfig {
var a types.AccessConfig
k.paramSpace.Get(ctx, types.ParamStoreKeyUploadAccess, &a)

View File

@ -0,0 +1,69 @@
//go:build cgo
package keeper
import (
"path/filepath"
wasmvm "github.com/CosmWasm/wasmvm"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/CosmWasm/wasmd/x/wasm/types"
)
// 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,
paramSpace paramtypes.Subspace,
accountKeeper types.AccountKeeper,
bankKeeper types.BankKeeper,
stakingKeeper types.StakingKeeper,
distKeeper types.DistributionKeeper,
channelKeeper types.ChannelKeeper,
portKeeper types.PortKeeper,
capabilityKeeper types.CapabilityKeeper,
portSource types.ICS20TransferPortSource,
router MessageRouter,
queryRouter GRPCQueryRouter,
homeDir string,
wasmConfig types.WasmConfig,
availableCapabilities string,
opts ...Option,
) Keeper {
wasmer, err := wasmvm.NewVM(filepath.Join(homeDir, "wasm"), availableCapabilities, contractMemoryLimit, wasmConfig.ContractDebugMode, wasmConfig.MemoryCacheSize)
if err != nil {
panic(err)
}
// set KeyTable if it has not already been set
if !paramSpace.HasKeyTable() {
paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
}
keeper := &Keeper{
storeKey: storeKey,
cdc: cdc,
wasmVM: wasmer,
accountKeeper: accountKeeper,
bank: NewBankCoinTransferrer(bankKeeper),
accountPruner: NewVestingCoinBurner(bankKeeper),
portKeeper: portKeeper,
capabilityKeeper: capabilityKeeper,
messenger: NewDefaultMessageHandler(router, channelKeeper, capabilityKeeper, bankKeeper, cdc, portSource),
queryGasLimit: wasmConfig.SmartQueryGasLimit,
paramSpace: paramSpace,
gasRegister: NewDefaultWasmGasRegister(),
maxQueryStackSize: types.DefaultMaxQueryStackSize,
acceptedAccountTypes: defaultAcceptedAccountTypes,
}
keeper.wasmVMQueryHandler = DefaultQueryPlugins(bankKeeper, stakingKeeper, distKeeper, channelKeeper, keeper)
for _, o := range opts {
o.apply(keeper)
}
// not updateable, yet
keeper.wasmVMResponseHandler = NewDefaultWasmVMContractResponseHandler(NewMessageDispatcher(keeper.messenger, keeper))
return *keeper
}

View File

@ -0,0 +1,35 @@
//go:build !cgo
package keeper
import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/CosmWasm/wasmd/x/wasm/types"
)
// 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,
paramSpace paramtypes.Subspace,
accountKeeper types.AccountKeeper,
bankKeeper types.BankKeeper,
stakingKeeper types.StakingKeeper,
distKeeper types.DistributionKeeper,
channelKeeper types.ChannelKeeper,
portKeeper types.PortKeeper,
capabilityKeeper types.CapabilityKeeper,
portSource types.ICS20TransferPortSource,
router MessageRouter,
queryRouter GRPCQueryRouter,
homeDir string,
wasmConfig types.WasmConfig,
availableCapabilities string,
opts ...Option,
) Keeper {
panic("not implemented, please build with cgo enabled")
}

View File

@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"os"
"strings"
"testing"
"time"
@ -59,7 +60,7 @@ func TestCreateSuccess(t *testing.T) {
require.NoError(t, err)
require.Equal(t, hackatomWasm, storedCode)
// and events emitted
codeHash := "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5"
codeHash := strings.ToLower("beb3de5e9b93b52e514c74ce87ccddb594b9bcd33b7f1af1bb6da63fc883917b")
exp := sdk.Events{sdk.NewEvent("store_code", sdk.NewAttribute("code_checksum", codeHash), sdk.NewAttribute("code_id", "1"))}
assert.Equal(t, exp, em.Events())
}
@ -409,7 +410,7 @@ func TestInstantiate(t *testing.T) {
gasAfter := ctx.GasMeter().GasConsumed()
if types.EnableGasVerification {
require.Equal(t, uint64(0x1a7bb), gasAfter-gasBefore)
require.Equal(t, uint64(0x1a7b6), gasAfter-gasBefore)
}
// ensure it is stored properly
@ -853,7 +854,7 @@ func TestExecute(t *testing.T) {
// make sure gas is properly deducted from ctx
gasAfter := ctx.GasMeter().GasConsumed()
if types.EnableGasVerification {
require.Equal(t, uint64(0x17d87), gasAfter-gasBefore)
require.Equal(t, uint64(0x17d7f), gasAfter-gasBefore)
}
// ensure bob now exists and got both payments released
bobAcct = accKeeper.GetAccount(ctx, bob)
@ -1008,7 +1009,7 @@ func TestExecuteWithPanic(t *testing.T) {
require.Error(t, err)
require.True(t, errors.Is(err, types.ErrExecuteFailed))
// test with contains as "Display" implementation of the Wasmer "RuntimeError" is different for Mac and Linux
assert.Contains(t, err.Error(), "Error calling the VM: Error executing Wasm: Wasmer runtime error: RuntimeError: unreachable")
assert.Contains(t, err.Error(), "Error calling the VM: Error executing Wasm: Wasmer runtime error: RuntimeError: Aborted: panicked at 'This page intentionally faulted', src/contract.rs:169:5: execute wasm contract failed")
}
func TestExecuteWithCpuLoop(t *testing.T) {

View File

@ -29,7 +29,7 @@ func TestStoreCodeProposal(t *testing.T) {
})
wasmCode, err := os.ReadFile("./testdata/hackatom.wasm")
require.NoError(t, err)
checksum, err := hex.DecodeString("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5")
checksum, err := hex.DecodeString("beb3de5e9b93b52e514c74ce87ccddb594b9bcd33b7f1af1bb6da63fc883917b")
require.NoError(t, err)
specs := map[string]struct {
@ -288,7 +288,7 @@ func TestStoreAndInstantiateContractProposal(t *testing.T) {
wasmCode, err := os.ReadFile("./testdata/hackatom.wasm")
require.NoError(t, err)
checksum, err := hex.DecodeString("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5")
checksum, err := hex.DecodeString("beb3de5e9b93b52e514c74ce87ccddb594b9bcd33b7f1af1bb6da63fc883917b")
require.NoError(t, err)
var (

View File

@ -61,10 +61,10 @@ func (q QueryHandler) Query(request wasmvmtypes.QueryRequest, gasLimit uint64) (
return res, nil
}
// special mappings to system error (which are not redacted)
var noSuchContract *types.ErrNoSuchContract
if ok := errors.As(err, &noSuchContract); ok {
err = wasmvmtypes.NoSuchContract{Addr: noSuchContract.Addr}
// special mappings to wasmvm system error (which are not redacted)
var wasmvmErr types.WasmVMErrorable
if ok := errors.As(err, &wasmvmErr); ok {
err = wasmvmErr.ToWasmVMError()
}
// Issue #759 - we don't return error string for worries of non-determinism
@ -92,6 +92,7 @@ type contractMetaDataSource interface {
type wasmQueryKeeper interface {
contractMetaDataSource
GetCodeInfo(ctx sdk.Context, codeID uint64) *types.CodeInfo
QueryRaw(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []byte
QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error)
IsPinnedCode(ctx sdk.Context, codeID uint64) bool
@ -528,15 +529,16 @@ func WasmQuerier(k wasmQueryKeeper) func(ctx sdk.Context, request *wasmvmtypes.W
}
return k.QueryRaw(ctx, addr, request.Raw.Key), nil
case request.ContractInfo != nil:
addr, err := sdk.AccAddressFromBech32(request.ContractInfo.ContractAddr)
contractAddr := request.ContractInfo.ContractAddr
addr, err := sdk.AccAddressFromBech32(contractAddr)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.ContractInfo.ContractAddr)
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, contractAddr)
}
info := k.GetContractInfo(ctx, addr)
if info == nil {
return nil, &types.ErrNoSuchContract{Addr: request.ContractInfo.ContractAddr}
return nil, types.ErrNoSuchContractFn(contractAddr).
Wrapf("address %s", contractAddr)
}
res := wasmvmtypes.ContractInfoResponse{
CodeID: info.CodeID,
Creator: info.Creator,
@ -545,6 +547,22 @@ func WasmQuerier(k wasmQueryKeeper) func(ctx sdk.Context, request *wasmvmtypes.W
IBCPort: info.IBCPortID,
}
return json.Marshal(res)
case request.CodeInfo != nil:
if request.CodeInfo.CodeID == 0 {
return nil, types.ErrEmpty.Wrap("code id")
}
info := k.GetCodeInfo(ctx, request.CodeInfo.CodeID)
if info == nil {
return nil, types.ErrNoSuchCodeFn(request.CodeInfo.CodeID).
Wrapf("code id %d", request.CodeInfo.CodeID)
}
res := wasmvmtypes.CodeInfoResponse{
CodeID: request.CodeInfo.CodeID,
Creator: info.Creator,
Checksum: info.CodeHash,
}
return json.Marshal(res)
}
return nil, wasmvmtypes.UnsupportedRequest{Kind: "unknown WasmQuery variant"}
}

View File

@ -460,6 +460,73 @@ func TestContractInfoWasmQuerier(t *testing.T) {
}
}
func TestCodeInfoWasmQuerier(t *testing.T) {
myCreatorAddr := keeper.RandomBech32AccountAddress(t)
var ctx sdk.Context
myRawChecksum := []byte("myHash78901234567890123456789012")
specs := map[string]struct {
req *wasmvmtypes.WasmQuery
mock mockWasmQueryKeeper
expRes wasmvmtypes.CodeInfoResponse
expErr bool
}{
"all good": {
req: &wasmvmtypes.WasmQuery{
CodeInfo: &wasmvmtypes.CodeInfoQuery{CodeID: 1},
},
mock: mockWasmQueryKeeper{
GetCodeInfoFn: func(ctx sdk.Context, codeID uint64) *types.CodeInfo {
return &types.CodeInfo{
CodeHash: myRawChecksum,
Creator: myCreatorAddr,
InstantiateConfig: types.AccessConfig{
Permission: types.AccessTypeNobody,
Addresses: []string{myCreatorAddr},
},
}
},
},
expRes: wasmvmtypes.CodeInfoResponse{
CodeID: 1,
Creator: myCreatorAddr,
Checksum: myRawChecksum,
},
},
"empty code id": {
req: &wasmvmtypes.WasmQuery{
CodeInfo: &wasmvmtypes.CodeInfoQuery{},
},
expErr: true,
},
"unknown code id": {
req: &wasmvmtypes.WasmQuery{
CodeInfo: &wasmvmtypes.CodeInfoQuery{CodeID: 1},
},
mock: mockWasmQueryKeeper{
GetCodeInfoFn: func(ctx sdk.Context, codeID uint64) *types.CodeInfo {
return nil
},
},
expErr: true,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
q := keeper.WasmQuerier(spec.mock)
gotBz, gotErr := q(ctx, spec.req)
if spec.expErr {
require.Error(t, gotErr)
return
}
require.NoError(t, gotErr)
var gotRes wasmvmtypes.CodeInfoResponse
require.NoError(t, json.Unmarshal(gotBz, &gotRes), string(gotBz))
assert.Equal(t, spec.expRes, gotRes)
})
}
}
func TestQueryErrors(t *testing.T) {
specs := map[string]struct {
src error
@ -467,13 +534,21 @@ func TestQueryErrors(t *testing.T) {
}{
"no error": {},
"no such contract": {
src: &types.ErrNoSuchContract{Addr: "contract-addr"},
src: types.ErrNoSuchContractFn("contract-addr"),
expErr: wasmvmtypes.NoSuchContract{Addr: "contract-addr"},
},
"no such contract - wrapped": {
src: sdkerrors.Wrap(&types.ErrNoSuchContract{Addr: "contract-addr"}, "my additional data"),
src: sdkerrors.Wrap(types.ErrNoSuchContractFn("contract-addr"), "my additional data"),
expErr: wasmvmtypes.NoSuchContract{Addr: "contract-addr"},
},
"no such code": {
src: types.ErrNoSuchCodeFn(123),
expErr: wasmvmtypes.NoSuchCode{CodeID: 123},
},
"no such code - wrapped": {
src: sdkerrors.Wrap(types.ErrNoSuchCodeFn(123), "my additional data"),
expErr: wasmvmtypes.NoSuchCode{CodeID: 123},
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
@ -558,6 +633,7 @@ type mockWasmQueryKeeper struct {
QueryRawFn func(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []byte
QuerySmartFn func(ctx sdk.Context, contractAddr sdk.AccAddress, req types.RawContractMessage) ([]byte, error)
IsPinnedCodeFn func(ctx sdk.Context, codeID uint64) bool
GetCodeInfoFn func(ctx sdk.Context, codeID uint64) *types.CodeInfo
}
func (m mockWasmQueryKeeper) GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo {
@ -588,6 +664,13 @@ func (m mockWasmQueryKeeper) IsPinnedCode(ctx sdk.Context, codeID uint64) bool {
return m.IsPinnedCodeFn(ctx, codeID)
}
func (m mockWasmQueryKeeper) GetCodeInfo(ctx sdk.Context, codeID uint64) *types.CodeInfo {
if m.GetCodeInfoFn == nil {
panic("not expected to be called")
}
return m.GetCodeInfoFn(ctx, codeID)
}
type bankKeeperMock struct {
GetSupplyFn func(ctx sdk.Context, denom string) sdk.Coin
GetBalanceFn func(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin

View File

@ -16,9 +16,8 @@ import (
)
type Recurse struct {
Depth uint32 `json:"depth"`
Work uint32 `json:"work"`
Contract sdk.AccAddress `json:"contract"`
Depth uint32 `json:"depth"`
Work uint32 `json:"work"`
}
type recurseWrapper struct {
@ -54,12 +53,12 @@ func initRecurseContract(t *testing.T) (contract sdk.AccAddress, creator sdk.Acc
func TestGasCostOnQuery(t *testing.T) {
const (
GasNoWork uint64 = 63_958
GasNoWork uint64 = 63_950
// Note: about 100 SDK gas (10k wasmer gas) for each round of sha256
GasWork50 uint64 = 64_401 // this is a little shy of 50k gas - to keep an eye on the limit
GasWork50 uint64 = 64_218 // this is a little shy of 50k gas - to keep an eye on the limit
GasReturnUnhashed uint64 = 33
GasReturnHashed uint64 = 25
GasReturnUnhashed uint64 = 32
GasReturnHashed uint64 = 27
)
cases := map[string]struct {
@ -92,7 +91,7 @@ func TestGasCostOnQuery(t *testing.T) {
Depth: 1,
Work: 50,
},
expectedGas: 2*GasWork50 + GasReturnHashed + 1, // +1 for rounding
expectedGas: 2*GasWork50 + GasReturnHashed,
},
"recursion 4, some work": {
gasLimit: 400_000,
@ -100,7 +99,7 @@ func TestGasCostOnQuery(t *testing.T) {
Depth: 4,
Work: 50,
},
expectedGas: 5*GasWork50 + 4*GasReturnHashed + 1,
expectedGas: 5*GasWork50 + 4*GasReturnHashed,
},
}
@ -117,7 +116,6 @@ func TestGasCostOnQuery(t *testing.T) {
// do the query
recurse := tc.msg
recurse.Contract = contractAddr
msg := buildRecurseQuery(t, recurse)
data, err := keeper.QuerySmart(ctx, contractAddr, msg)
require.NoError(t, err)
@ -186,7 +184,6 @@ func TestGasOnExternalQuery(t *testing.T) {
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
recurse := tc.msg
recurse.Contract = contractAddr
msg := buildRecurseQuery(t, recurse)
querier := NewGrpcQuerier(keeper.cdc, keeper.storeKey, keeper, tc.gasLimit)
@ -211,9 +208,9 @@ func TestLimitRecursiveQueryGas(t *testing.T) {
const (
// Note: about 100 SDK gas (10k wasmer gas) for each round of sha256
GasWork2k uint64 = 84_236 // = NewContractInstanceCosts + x // we have 6x gas used in cpu than in the instance
GasWork2k uint64 = 77_206 // = NewContractInstanceCosts + x // we have 6x gas used in cpu than in the instance
// This is overhead for calling into a sub-contract
GasReturnHashed uint64 = 26
GasReturnHashed uint64 = 27
)
cases := map[string]struct {
@ -241,10 +238,10 @@ func TestLimitRecursiveQueryGas(t *testing.T) {
},
expectQueriesFromContract: 5,
// FIXME: why -1 ... confused a bit by calculations, seems like rounding issues
expectedGas: GasWork2k + 5*(GasWork2k+GasReturnHashed) - 1,
expectedGas: GasWork2k + 5*(GasWork2k+GasReturnHashed),
},
// this is where we expect an error...
// it has enough gas to run 4 times and die on the 5th (4th time dispatching to sub-contract)
// it has enough gas to run 5 times and die on the 6th (5th time dispatching to sub-contract)
// however, if we don't charge the cpu gas before sub-dispatching, we can recurse over 20 times
"deep recursion, should die on 5th level": {
gasLimit: 400_000,
@ -252,7 +249,7 @@ func TestLimitRecursiveQueryGas(t *testing.T) {
Depth: 50,
Work: 2000,
},
expectQueriesFromContract: 4,
expectQueriesFromContract: 5,
expectOutOfGas: true,
},
"very deep recursion, hits recursion limit": {
@ -264,7 +261,7 @@ func TestLimitRecursiveQueryGas(t *testing.T) {
expectQueriesFromContract: 10,
expectOutOfGas: false,
expectError: "query wasm contract failed", // Error we get from the contract instance doing the failing query, not wasmd
expectedGas: 10*(GasWork2k+GasReturnHashed) - 264,
expectedGas: 10*(GasWork2k+GasReturnHashed) - 247,
},
}
@ -281,7 +278,6 @@ func TestLimitRecursiveQueryGas(t *testing.T) {
// prepare the query
recurse := tc.msg
recurse.Contract = contractAddr
msg := buildRecurseQuery(t, recurse)
// if we expect out of gas, make sure this panics

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1 +1 @@
v1.0.0-beta
v1.2.0

View File

@ -1,6 +1,7 @@
package types
import (
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdkErrors "github.com/cosmos/cosmos-sdk/types/errors"
)
@ -70,27 +71,80 @@ var (
// ErrInvalidEvent error if an attribute/event from the contract is invalid
ErrInvalidEvent = sdkErrors.Register(DefaultCodespace, 21, "invalid event")
// error if an address does not belong to a contract (just for registration)
_ = sdkErrors.Register(DefaultCodespace, 22, "no such contract")
// ErrNoSuchContractFn error factory for an error when an address does not belong to a contract
ErrNoSuchContractFn = WasmVMFlavouredErrorFactory(sdkErrors.Register(DefaultCodespace, 22, "no such contract"),
func(addr string) error { return wasmvmtypes.NoSuchContract{Addr: addr} },
)
// code 23 -26 were used for json parser
// ErrExceedMaxQueryStackSize error if max query stack size is exceeded
ErrExceedMaxQueryStackSize = sdkErrors.Register(DefaultCodespace, 27, "max query stack size exceeded")
// ErrNoSuchCodeFn factory for an error when a code id does not belong to a code info
ErrNoSuchCodeFn = WasmVMFlavouredErrorFactory(sdkErrors.Register(DefaultCodespace, 28, "no such code"),
func(id uint64) error { return wasmvmtypes.NoSuchCode{CodeID: id} },
)
)
type ErrNoSuchContract struct {
Addr string
// WasmVMErrorable mapped error type in wasmvm and are not redacted
type WasmVMErrorable interface {
// ToWasmVMError convert instance to wasmvm friendly error if possible otherwise root cause. never nil
ToWasmVMError() error
}
func (m *ErrNoSuchContract) Error() string {
return "no such contract: " + m.Addr
var _ WasmVMErrorable = WasmVMFlavouredError{}
// WasmVMFlavouredError wrapper for sdk error that supports wasmvm error types
type WasmVMFlavouredError struct {
sdkErr *sdkErrors.Error
wasmVMErr error
}
func (m *ErrNoSuchContract) ABCICode() uint32 {
return 22
// NewWasmVMFlavouredError constructor
func NewWasmVMFlavouredError(sdkErr *sdkErrors.Error, wasmVMErr error) WasmVMFlavouredError {
return WasmVMFlavouredError{sdkErr: sdkErr, wasmVMErr: wasmVMErr}
}
func (m *ErrNoSuchContract) Codespace() string {
return DefaultCodespace
// WasmVMFlavouredErrorFactory is a factory method to build a WasmVMFlavouredError type
func WasmVMFlavouredErrorFactory[T any](sdkErr *sdkErrors.Error, wasmVMErrBuilder func(T) error) func(T) WasmVMFlavouredError {
if wasmVMErrBuilder == nil {
panic("builder function required")
}
return func(d T) WasmVMFlavouredError {
return WasmVMFlavouredError{sdkErr: sdkErr, wasmVMErr: wasmVMErrBuilder(d)}
}
}
// ToWasmVMError implements WasmVMError-able
func (e WasmVMFlavouredError) ToWasmVMError() error {
if e.wasmVMErr != nil {
return e.wasmVMErr
}
return e.sdkErr
}
// implements stdlib error
func (e WasmVMFlavouredError) Error() string {
return e.sdkErr.Error()
}
// Unwrap implements the built-in errors.Unwrap
func (e WasmVMFlavouredError) Unwrap() error {
return e.sdkErr
}
// Cause is the same as unwrap but used by errors.abci
func (e WasmVMFlavouredError) Cause() error {
return e.Unwrap()
}
// Wrap extends this error with additional information.
// It's a handy function to call Wrap with sdk errors.
func (e WasmVMFlavouredError) Wrap(desc string) error { return sdkErrors.Wrap(e, desc) }
// Wrapf extends this error with additional information.
// It's a handy function to call Wrapf with sdk errors.
func (e WasmVMFlavouredError) Wrapf(desc string, args ...interface{}) error {
return sdkErrors.Wrapf(e, desc, args...)
}

View File

@ -0,0 +1,86 @@
package types
import (
"errors"
"testing"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestWasmVMFlavouredError(t *testing.T) {
myErr := ErrNoSuchCodeFn(1)
specs := map[string]struct {
exec func(t *testing.T)
}{
"IsOf": {
exec: func(t *testing.T) {
assert.True(t, sdkerrors.IsOf(myErr, myErr.sdkErr))
assert.Equal(t, myErr.sdkErr, myErr.Unwrap())
},
},
"unwrapped": {
exec: func(t *testing.T) {
assert.Equal(t, myErr.sdkErr, myErr.Unwrap())
},
},
"caused": {
exec: func(t *testing.T) {
assert.Equal(t, myErr.sdkErr, myErr.Cause())
},
},
"wrapped supports WasmVMErrorable": {
exec: func(t *testing.T) {
var wasmvmErr WasmVMErrorable
require.True(t, errors.As(myErr.Wrap("my description"), &wasmvmErr))
gotErr := wasmvmErr.ToWasmVMError()
assert.Equal(t, wasmvmtypes.NoSuchCode{CodeID: 1}, gotErr)
},
},
"wrappedf supports WasmVMErrorable": {
exec: func(t *testing.T) {
var wasmvmErr WasmVMErrorable
require.True(t, errors.As(myErr.Wrapf("my description: %d", 1), &wasmvmErr))
gotErr := wasmvmErr.ToWasmVMError()
assert.Equal(t, wasmvmtypes.NoSuchCode{CodeID: 1}, gotErr)
},
},
"supports WasmVMErrorable": {
exec: func(t *testing.T) {
var wasmvmErr WasmVMErrorable
require.True(t, errors.As(myErr, &wasmvmErr))
gotErr := wasmvmErr.ToWasmVMError()
assert.Equal(t, wasmvmtypes.NoSuchCode{CodeID: 1}, gotErr)
},
},
"fallback to sdk error when wasmvm error unset": {
exec: func(t *testing.T) {
var wasmvmErr WasmVMErrorable
require.True(t, errors.As(WasmVMFlavouredError{sdkErr: ErrEmpty}, &wasmvmErr))
gotErr := wasmvmErr.ToWasmVMError()
assert.Equal(t, ErrEmpty, gotErr)
},
},
"abci info": {
exec: func(t *testing.T) {
codespace, code, log := sdkerrors.ABCIInfo(myErr, false)
assert.Equal(t, DefaultCodespace, codespace)
assert.Equal(t, uint32(28), code)
assert.Equal(t, "no such code", log)
},
},
"abci info - wrapped": {
exec: func(t *testing.T) {
codespace, code, log := sdkerrors.ABCIInfo(myErr.Wrap("my description"), false)
assert.Equal(t, DefaultCodespace, codespace)
assert.Equal(t, uint32(28), code)
assert.Equal(t, "my description: no such code", log)
},
},
}
for name, spec := range specs {
t.Run(name, spec.exec)
}
}