mirror of https://github.com/certusone/wasmd.git
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:
parent
a925a9ed61
commit
957b38e0a5
|
@ -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
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
# Wasm Zone
|
||||
|
||||
[](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 |
|
||||
|
|
|
@ -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
4
go.mod
|
@ -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
4
go.sum
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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"}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
@ -1 +1 @@
|
|||
v1.0.0-beta
|
||||
v1.2.0
|
||||
|
|
|
@ -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...)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue