Fix wasm simulations (#870)

* Fix wasm simulations + make functions compatible with tgrade

* Fix lint issues

* Fix simulation setup

* Make simulations store msg pass

* Use default values params to make operations succeed

* Normalize wasm store

* Add simulations to circleci config

* Run simulation in temp dir

* Store sim logs

* Increase circleci machine type

* Extract reflect contract api into helper

* Add execute msg to simulations

* Embed refect wasm contract

Co-authored-by: Pino' Surace <pino.surace@live.it>
This commit is contained in:
Alexander Peters 2022-05-31 10:07:16 +02:00 committed by GitHub
parent 34e078346b
commit 2a9c5c1ea5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 499 additions and 220 deletions

View File

@ -116,6 +116,19 @@ jobs:
cd ./benchmarks
go test -bench .
simulations:
executor: golang
parallelism: 1
resource_class: large
steps:
- checkout
- run:
name: Run simulations
command: |
make test-sim-multi-seed-short
- store_artifacts:
path: /tmp
upload-coverage:
executor: golang
steps:
@ -218,3 +231,6 @@ workflows:
- benchmark:
requires:
- test-cover
- simulations:
requires:
- setup-dependencies

View File

@ -458,7 +458,6 @@ func NewWasmApp(
transferModule := transfer.NewAppModule(app.transferKeeper)
transferIBCModule := transfer.NewIBCModule(app.transferKeeper)
_ = app.getSubspace(icahosttypes.SubModuleName)
app.icaHostKeeper = icahostkeeper.NewKeeper(
appCodec,
keys[icahosttypes.StoreKey],

View File

@ -20,6 +20,7 @@ const (
DefaultWeightCommunitySpendProposal int = 5
DefaultWeightTextProposal int = 5
DefaultWeightParamChangeProposal int = 5
DefaultWeightMsgStoreCode int = 100
DefaultWeightMsgStoreCode int = 50
DefaultWeightMsgInstantiateContract int = 100
DefaultWeightMsgExecuteContract int = 100
)

View File

@ -6,11 +6,17 @@ import (
"os"
"path/filepath"
"testing"
"time"
"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/store/prefix"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/kv"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
@ -118,7 +124,7 @@ func TestAppImportExport(t *testing.T) {
t,
os.Stdout,
app.BaseApp,
simapp.AppStateFn(app.AppCodec(), app.SimulationManager()),
AppStateFn(app.AppCodec(), app.SimulationManager()),
simtypes.RandomAccounts,
simapp.SimulationOperations(app, app.AppCodec(), config),
app.ModuleAccountAddrs(),
@ -190,6 +196,37 @@ func TestAppImportExport(t *testing.T) {
// delete persistent tx counter value
ctxA.KVStore(app.keys[wasm.StoreKey]).Delete(wasmtypes.TXCounterPrefix)
// reset contract code index in source DB for comparison with dest DB
dropContractHistory := func(s store.KVStore, keys ...[]byte) {
for _, key := range keys {
prefixStore := prefix.NewStore(s, key)
iter := prefixStore.Iterator(nil, nil)
for ; iter.Valid(); iter.Next() {
prefixStore.Delete(iter.Key())
}
iter.Close()
}
}
prefixes := [][]byte{wasmtypes.ContractCodeHistoryElementPrefix, wasmtypes.ContractByCodeIDAndCreatedSecondaryIndexPrefix}
dropContractHistory(ctxA.KVStore(app.keys[wasm.StoreKey]), prefixes...)
dropContractHistory(ctxB.KVStore(newApp.keys[wasm.StoreKey]), prefixes...)
normalizeContractInfo := func(ctx sdk.Context, app *WasmApp) {
var index uint64
app.wasmKeeper.IterateContractInfo(ctx, func(address sdk.AccAddress, info wasmtypes.ContractInfo) bool {
created := &wasmtypes.AbsoluteTxPosition{
BlockHeight: uint64(0),
TxIndex: index,
}
info.Created = created
store := ctx.KVStore(app.keys[wasm.StoreKey])
store.Set(wasmtypes.GetContractAddressKey(address), app.appCodec.MustMarshal(&info))
index++
return false
})
}
normalizeContractInfo(ctxA, app)
normalizeContractInfo(ctxB, newApp)
// diff both stores
for _, skp := range storeKeysPrefixes {
storeA := ctxA.KVStore(skp.A)
@ -215,7 +252,7 @@ func TestFullAppSimulation(t *testing.T) {
require.NoError(t, os.RemoveAll(dir))
}()
encConf := MakeEncodingConfig()
app := NewWasmApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue,
app := NewWasmApp(logger, db, nil, true, map[int64]bool{}, t.TempDir(), simapp.FlagPeriodValue,
encConf, wasm.EnableAllProposals, simapp.EmptyAppOptions{}, nil, fauxMerkleModeOpt)
require.Equal(t, "WasmApp", app.Name())
@ -224,7 +261,7 @@ func TestFullAppSimulation(t *testing.T) {
t,
os.Stdout,
app.BaseApp,
simapp.AppStateFn(app.appCodec, app.SimulationManager()),
AppStateFn(app.appCodec, app.SimulationManager()),
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
simapp.SimulationOperations(app, app.AppCodec(), config),
app.ModuleAccountAddrs(),
@ -241,3 +278,15 @@ func TestFullAppSimulation(t *testing.T) {
simapp.PrintStats(db)
}
}
// AppStateFn returns the initial application state using a genesis or the simulation parameters.
// It panics if the user provides files for both of them.
// If a file is not given for the genesis or the sim params, it creates a randomized one.
func AppStateFn(codec codec.Codec, manager *module.SimulationManager) simtypes.AppStateFn {
// quick hack to setup app state genesis with our app modules
simapp.ModuleBasics = ModuleBasics
if simapp.FlagGenesisTimeValue == 0 { // always set to have a block time
simapp.FlagGenesisTimeValue = time.Now().Unix()
}
return simapp.AppStateFn(codec, manager)
}

View File

@ -6,8 +6,6 @@ import (
"strings"
"testing"
"github.com/golang/protobuf/proto"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
@ -16,62 +14,17 @@ import (
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/CosmWasm/wasmd/x/wasm/keeper/testdata"
"github.com/CosmWasm/wasmd/x/wasm/types"
)
// ReflectInitMsg is {}
// ReflectHandleMsg is used to encode handle messages
type ReflectHandleMsg struct {
Reflect *reflectPayload `json:"reflect_msg,omitempty"`
ReflectSubMsg *reflectSubPayload `json:"reflect_sub_msg,omitempty"`
Change *ownerPayload `json:"change_owner,omitempty"`
}
type ownerPayload struct {
Owner sdk.Address `json:"owner"`
}
type reflectPayload struct {
Msgs []wasmvmtypes.CosmosMsg `json:"msgs"`
}
type reflectSubPayload struct {
Msgs []wasmvmtypes.SubMsg `json:"msgs"`
}
// ReflectQueryMsg is used to encode query messages
type ReflectQueryMsg struct {
Owner *struct{} `json:"owner,omitempty"`
Capitalized *Text `json:"capitalized,omitempty"`
Chain *ChainQuery `json:"chain,omitempty"`
SubMsgResult *SubCall `json:"sub_msg_result,omitempty"`
}
type ChainQuery struct {
Request *wasmvmtypes.QueryRequest `json:"request,omitempty"`
}
type Text struct {
Text string `json:"text"`
}
type SubCall struct {
ID uint64 `json:"id"`
}
type OwnerResponse struct {
Owner string `json:"owner,omitempty"`
}
type ChainResponse struct {
Data []byte `json:"data,omitempty"`
}
func buildReflectQuery(t *testing.T, query *ReflectQueryMsg) []byte {
func buildReflectQuery(t *testing.T, query *testdata.ReflectQueryMsg) []byte {
bz, err := json.Marshal(query)
require.NoError(t, err)
return bz
@ -94,9 +47,7 @@ func TestReflectContractSend(t *testing.T) {
_, _, bob := keyPubAddr()
// upload reflect code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
reflectID, err := keeper.Create(ctx, creator, reflectCode, nil)
reflectID, err := keeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
require.Equal(t, uint64(1), reflectID)
@ -148,8 +99,8 @@ func TestReflectContractSend(t *testing.T) {
},
},
}}
reflectSend := ReflectHandleMsg{
Reflect: &reflectPayload{
reflectSend := testdata.ReflectHandleMsg{
Reflect: &testdata.ReflectPayload{
Msgs: msgs,
},
}
@ -176,9 +127,7 @@ func TestReflectCustomMsg(t *testing.T) {
_, _, fred := keyPubAddr()
// upload code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keeper.Create(ctx, creator, reflectCode, nil)
codeID, err := keeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
require.Equal(t, uint64(1), codeID)
@ -189,8 +138,8 @@ func TestReflectCustomMsg(t *testing.T) {
require.NotEmpty(t, contractAddr)
// set owner to bob
transfer := ReflectHandleMsg{
Change: &ownerPayload{
transfer := testdata.ReflectHandleMsg{
ChangeOwner: &testdata.OwnerPayload{
Owner: bob,
},
}
@ -216,8 +165,8 @@ func TestReflectCustomMsg(t *testing.T) {
},
},
}}
reflectSend := ReflectHandleMsg{
Reflect: &reflectPayload{
reflectSend := testdata.ReflectHandleMsg{
Reflect: &testdata.ReflectPayload{
Msgs: msgs,
},
}
@ -240,8 +189,8 @@ func TestReflectCustomMsg(t *testing.T) {
}
opaque, err := toReflectRawMsg(cdc, sdkSendMsg)
require.NoError(t, err)
reflectOpaque := ReflectHandleMsg{
Reflect: &reflectPayload{
reflectOpaque := testdata.ReflectHandleMsg{
Reflect: &testdata.ReflectPayload{
Msgs: []wasmvmtypes.CosmosMsg{opaque},
},
}
@ -267,9 +216,7 @@ func TestMaskReflectCustomQuery(t *testing.T) {
creator := keepers.Faucet.NewFundedAccount(ctx, deposit...)
// upload code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keepers.ContractKeeper.Create(ctx, creator, reflectCode, nil)
codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
require.Equal(t, uint64(1), codeID)
@ -280,21 +227,21 @@ func TestMaskReflectCustomQuery(t *testing.T) {
require.NotEmpty(t, contractAddr)
// let's perform a normal query of state
ownerQuery := ReflectQueryMsg{
ownerQuery := testdata.ReflectQueryMsg{
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
var res testdata.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 := ReflectQueryMsg{
Capitalized: &Text{
customQuery := testdata.ReflectQueryMsg{
Capitalized: &testdata.Text{
Text: "all Caps noW",
},
}
@ -319,9 +266,7 @@ func TestReflectStargateQuery(t *testing.T) {
creator := keepers.Faucet.NewFundedAccount(ctx, funds...)
// upload code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keepers.ContractKeeper.Create(ctx, creator, reflectCode, nil)
codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
require.Equal(t, uint64(1), codeID)
@ -338,13 +283,13 @@ func TestReflectStargateQuery(t *testing.T) {
},
},
}
simpleQueryBz, err := json.Marshal(ReflectQueryMsg{
Chain: &ChainQuery{Request: &bankQuery},
simpleQueryBz, err := json.Marshal(testdata.ReflectQueryMsg{
Chain: &testdata.ChainQuery{Request: &bankQuery},
})
require.NoError(t, err)
simpleRes, err := keeper.QuerySmart(ctx, contractAddr, simpleQueryBz)
require.NoError(t, err)
var simpleChain ChainResponse
var simpleChain testdata.ChainResponse
mustParse(t, simpleRes, &simpleChain)
var simpleBalance wasmvmtypes.AllBalancesResponse
mustParse(t, simpleChain.Data, &simpleBalance)
@ -363,9 +308,7 @@ func TestReflectInvalidStargateQuery(t *testing.T) {
creator := keepers.Faucet.NewFundedAccount(ctx, funds...)
// upload code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keepers.ContractKeeper.Create(ctx, creator, reflectCode, nil)
codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
require.Equal(t, uint64(1), codeID)
@ -385,8 +328,8 @@ func TestReflectInvalidStargateQuery(t *testing.T) {
Data: protoQueryBin,
},
}
protoQueryBz, err := json.Marshal(ReflectQueryMsg{
Chain: &ChainQuery{Request: &protoRequest},
protoQueryBz, err := json.Marshal(testdata.ReflectQueryMsg{
Chain: &testdata.ChainQuery{Request: &protoRequest},
})
require.NoError(t, err)
@ -402,8 +345,8 @@ func TestReflectInvalidStargateQuery(t *testing.T) {
Data: []byte{},
},
}
protoQueryBz, err = json.Marshal(ReflectQueryMsg{
Chain: &ChainQuery{Request: &protoRequest},
protoQueryBz, err = json.Marshal(testdata.ReflectQueryMsg{
Chain: &testdata.ChainQuery{Request: &protoRequest},
})
require.NoError(t, err)
@ -419,8 +362,8 @@ func TestReflectInvalidStargateQuery(t *testing.T) {
Data: []byte{},
},
}
protoQueryBz, err = json.Marshal(ReflectQueryMsg{
Chain: &ChainQuery{Request: &protoRequest},
protoQueryBz, err = json.Marshal(testdata.ReflectQueryMsg{
Chain: &testdata.ChainQuery{Request: &protoRequest},
})
require.NoError(t, err)
@ -443,9 +386,7 @@ func TestMaskReflectWasmQueries(t *testing.T) {
creator := keepers.Faucet.NewFundedAccount(ctx, deposit...)
// upload reflect code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
reflectID, err := keepers.ContractKeeper.Create(ctx, creator, reflectCode, nil)
reflectID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
require.Equal(t, uint64(1), reflectID)
@ -456,10 +397,10 @@ func TestMaskReflectWasmQueries(t *testing.T) {
require.NotEmpty(t, reflectAddr)
// for control, let's make some queries directly on the reflect
ownerQuery := buildReflectQuery(t, &ReflectQueryMsg{Owner: &struct{}{}})
ownerQuery := buildReflectQuery(t, &testdata.ReflectQueryMsg{Owner: &struct{}{}})
res, err := keeper.QuerySmart(ctx, reflectAddr, ownerQuery)
require.NoError(t, err)
var ownerRes OwnerResponse
var ownerRes testdata.OwnerResponse
mustParse(t, res, &ownerRes)
require.Equal(t, ownerRes.Owner, creator.String())
@ -471,7 +412,7 @@ func TestMaskReflectWasmQueries(t *testing.T) {
require.Equal(t, stateRes.Owner, creator.String())
// now, let's reflect a smart query into the x/wasm handlers and see if we get the same result
reflectOwnerQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
reflectOwnerQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
Smart: &wasmvmtypes.SmartQuery{
ContractAddr: reflectAddr.String(),
Msg: ownerQuery,
@ -481,14 +422,14 @@ func TestMaskReflectWasmQueries(t *testing.T) {
res, err = keeper.QuerySmart(ctx, reflectAddr, reflectOwnerBin)
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response
var reflectRes ChainResponse
var reflectRes testdata.ChainResponse
mustParse(t, res, &reflectRes)
var reflectOwnerRes OwnerResponse
var reflectOwnerRes testdata.OwnerResponse
mustParse(t, reflectRes.Data, &reflectOwnerRes)
require.Equal(t, reflectOwnerRes.Owner, creator.String())
// and with queryRaw
reflectStateQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
reflectStateQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
Raw: &wasmvmtypes.RawQuery{
ContractAddr: reflectAddr.String(),
Key: configKey,
@ -498,7 +439,7 @@ func TestMaskReflectWasmQueries(t *testing.T) {
res, err = keeper.QuerySmart(ctx, reflectAddr, reflectStateBin)
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response
var reflectRawRes ChainResponse
var reflectRawRes testdata.ChainResponse
mustParse(t, res, &reflectRawRes)
// now, with the raw data, we can parse it into state
var reflectStateRes reflectState
@ -515,9 +456,7 @@ func TestWasmRawQueryWithNil(t *testing.T) {
creator := keepers.Faucet.NewFundedAccount(ctx, deposit...)
// upload reflect code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
reflectID, err := keepers.ContractKeeper.Create(ctx, creator, reflectCode, nil)
reflectID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
require.Equal(t, uint64(1), reflectID)
@ -533,7 +472,7 @@ func TestWasmRawQueryWithNil(t *testing.T) {
require.Nil(t, raw)
// and with queryRaw
reflectQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
reflectQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{
Raw: &wasmvmtypes.RawQuery{
ContractAddr: reflectAddr.String(),
Key: missingKey,
@ -544,7 +483,7 @@ func TestWasmRawQueryWithNil(t *testing.T) {
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response
var reflectRawRes ChainResponse
var reflectRawRes testdata.ChainResponse
mustParse(t, res, &reflectRawRes)
// and make sure there is no data
require.Empty(t, reflectRawRes.Data)
@ -629,8 +568,8 @@ func fromReflectRawMsg(cdc codec.Codec) CustomEncoder {
}
type reflectCustomQuery struct {
Ping *struct{} `json:"ping,omitempty"`
Capitalized *Text `json:"capitalized,omitempty"`
Ping *struct{} `json:"ping,omitempty"`
Capitalized *testdata.Text `json:"capitalized,omitempty"`
}
// this is from the go code back to the contract (capitalized or ping)

View File

@ -5,8 +5,6 @@ import (
"io/ioutil"
"testing"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
@ -21,6 +19,9 @@ import (
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/CosmWasm/wasmd/x/wasm/keeper/testdata"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
)
type StakingInitMsg struct {
@ -35,12 +36,12 @@ type StakingInitMsg struct {
// StakingHandleMsg is used to encode handle messages
type StakingHandleMsg struct {
Transfer *transferPayload `json:"transfer,omitempty"`
Bond *struct{} `json:"bond,omitempty"`
Unbond *unbondPayload `json:"unbond,omitempty"`
Claim *struct{} `json:"claim,omitempty"`
Reinvest *struct{} `json:"reinvest,omitempty"`
Change *ownerPayload `json:"change_owner,omitempty"`
Transfer *transferPayload `json:"transfer,omitempty"`
Bond *struct{} `json:"bond,omitempty"`
Unbond *unbondPayload `json:"unbond,omitempty"`
Claim *struct{} `json:"claim,omitempty"`
Reinvest *struct{} `json:"reinvest,omitempty"`
Change *testdata.OwnerPayload `json:"change_owner,omitempty"`
}
type transferPayload struct {
@ -446,9 +447,7 @@ func TestQueryStakingInfo(t *testing.T) {
creator := initInfo.faucet.NewFundedAccount(ctx, deposit...)
// upload mask code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
maskID, err := initInfo.contractKeeper.Create(ctx, creator, maskCode, nil)
maskID, err := initInfo.contractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
require.Equal(t, uint64(2), maskID)
@ -459,21 +458,21 @@ func TestQueryStakingInfo(t *testing.T) {
// STEP 3: now, let's reflect some queries.
// let's get the bonded denom
reflectBondedQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
reflectBondedQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
BondedDenom: &struct{}{},
}}}}
reflectBondedBin := buildReflectQuery(t, &reflectBondedQuery)
res, err := keeper.QuerySmart(ctx, maskAddr, reflectBondedBin)
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response
var reflectRes ChainResponse
var reflectRes testdata.ChainResponse
mustParse(t, res, &reflectRes)
var bondedRes wasmvmtypes.BondedDenomResponse
mustParse(t, reflectRes.Data, &bondedRes)
assert.Equal(t, "stake", bondedRes.Denom)
// now, let's reflect a smart query into the x/wasm handlers and see if we get the same result
reflectAllValidatorsQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
reflectAllValidatorsQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
AllValidators: &wasmvmtypes.AllValidatorsQuery{},
}}}}
reflectAllValidatorsBin := buildReflectQuery(t, &reflectAllValidatorsQuery)
@ -492,7 +491,7 @@ func TestQueryStakingInfo(t *testing.T) {
require.Contains(t, valInfo.MaxChangeRate, "0.010")
// find a validator
reflectValidatorQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
reflectValidatorQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
Validator: &wasmvmtypes.ValidatorQuery{
Address: valAddr.String(),
},
@ -514,7 +513,7 @@ func TestQueryStakingInfo(t *testing.T) {
// missing validator
noVal := sdk.ValAddress(secp256k1.GenPrivKey().PubKey().Address())
reflectNoValidatorQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
reflectNoValidatorQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
Validator: &wasmvmtypes.ValidatorQuery{
Address: noVal.String(),
},
@ -529,7 +528,7 @@ func TestQueryStakingInfo(t *testing.T) {
require.Nil(t, noValidatorRes.Validator)
// test to get all my delegations
reflectAllDelegationsQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
reflectAllDelegationsQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
AllDelegations: &wasmvmtypes.AllDelegationsQuery{
Delegator: contractAddr.String(),
},
@ -552,7 +551,7 @@ func TestQueryStakingInfo(t *testing.T) {
require.Equal(t, funds[0].Amount.String(), delInfo.Amount.Amount)
// test to get one delegations
reflectDelegationQuery := ReflectQueryMsg{Chain: &ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
reflectDelegationQuery := testdata.ReflectQueryMsg{Chain: &testdata.ChainQuery{Request: &wasmvmtypes.QueryRequest{Staking: &wasmvmtypes.StakingQuery{
Delegation: &wasmvmtypes.DelegationQuery{
Validator: valAddr.String(),
Delegator: contractAddr.String(),

View File

@ -7,13 +7,13 @@ import (
"strconv"
"testing"
"github.com/CosmWasm/wasmd/x/wasm/types"
"github.com/stretchr/testify/assert"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/CosmWasm/wasmd/x/wasm/keeper/testdata"
"github.com/CosmWasm/wasmd/x/wasm/types"
)
// test handing of submessages, very closely related to the reflect_test
@ -31,9 +31,7 @@ func TestDispatchSubMsgSuccessCase(t *testing.T) {
_, _, fred := keyPubAddr()
// upload code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keepers.ContractKeeper.Create(ctx, creator, reflectCode, nil)
codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
require.Equal(t, uint64(1), codeID)
@ -59,8 +57,8 @@ func TestDispatchSubMsgSuccessCase(t *testing.T) {
},
},
}
reflectSend := ReflectHandleMsg{
ReflectSubMsg: &reflectSubPayload{
reflectSend := testdata.ReflectHandleMsg{
ReflectSubMsg: &testdata.ReflectSubPayload{
Msgs: []wasmvmtypes.SubMsg{{
ID: 7,
Msg: msg,
@ -80,8 +78,8 @@ func TestDispatchSubMsgSuccessCase(t *testing.T) {
checkAccount(t, ctx, accKeeper, bankKeeper, creator, creatorBalance)
// query the reflect state to ensure the result was stored
query := ReflectQueryMsg{
SubMsgResult: &SubCall{ID: 7},
query := testdata.ReflectQueryMsg{
SubMsgResult: &testdata.SubCall{ID: 7},
}
queryBz, err := json.Marshal(query)
require.NoError(t, err)
@ -122,9 +120,7 @@ func TestDispatchSubMsgErrorHandling(t *testing.T) {
uploader := keepers.Faucet.NewFundedAccount(ctx, contractStart.Add(contractStart...)...)
// upload code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
reflectID, err := keepers.ContractKeeper.Create(ctx, uploader, reflectCode, nil)
reflectID, err := keepers.ContractKeeper.Create(ctx, uploader, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
// create hackatom contract for testing (for infinite loop)
@ -300,8 +296,8 @@ func TestDispatchSubMsgErrorHandling(t *testing.T) {
require.NoError(t, err)
msg := tc.msg(contractAddr.String(), empty.String())
reflectSend := ReflectHandleMsg{
ReflectSubMsg: &reflectSubPayload{
reflectSend := testdata.ReflectHandleMsg{
ReflectSubMsg: &testdata.ReflectSubPayload{
Msgs: []wasmvmtypes.SubMsg{{
ID: tc.submsgID,
Msg: msg,
@ -331,8 +327,8 @@ func TestDispatchSubMsgErrorHandling(t *testing.T) {
require.NoError(t, err)
// query the reply
query := ReflectQueryMsg{
SubMsgResult: &SubCall{ID: tc.submsgID},
query := testdata.ReflectQueryMsg{
SubMsgResult: &testdata.SubCall{ID: tc.submsgID},
}
queryBz, err := json.Marshal(query)
require.NoError(t, err)
@ -381,9 +377,7 @@ func TestDispatchSubMsgEncodeToNoSdkMsg(t *testing.T) {
_, _, fred := keyPubAddr()
// upload code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keepers.ContractKeeper.Create(ctx, creator, reflectCode, nil)
codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
// creator instantiates a contract and gives it tokens
@ -403,8 +397,8 @@ func TestDispatchSubMsgEncodeToNoSdkMsg(t *testing.T) {
},
},
}
reflectSend := ReflectHandleMsg{
ReflectSubMsg: &reflectSubPayload{
reflectSend := testdata.ReflectHandleMsg{
ReflectSubMsg: &testdata.ReflectSubPayload{
Msgs: []wasmvmtypes.SubMsg{{
ID: 7,
Msg: msg,
@ -418,8 +412,8 @@ func TestDispatchSubMsgEncodeToNoSdkMsg(t *testing.T) {
require.NoError(t, err)
// query the reflect state to ensure the result was stored
query := ReflectQueryMsg{
SubMsgResult: &SubCall{ID: 7},
query := testdata.ReflectQueryMsg{
SubMsgResult: &testdata.SubCall{ID: 7},
}
queryBz, err := json.Marshal(query)
require.NoError(t, err)
@ -449,9 +443,7 @@ func TestDispatchSubMsgConditionalReplyOn(t *testing.T) {
_, _, fred := keyPubAddr()
// upload code
reflectCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keepers.ContractKeeper.Create(ctx, creator, reflectCode, nil)
codeID, err := keepers.ContractKeeper.Create(ctx, creator, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
// creator instantiates a contract and gives it tokens
@ -529,8 +521,8 @@ func TestDispatchSubMsgConditionalReplyOn(t *testing.T) {
subMsg.ReplyOn = wasmvmtypes.ReplyError
}
reflectSend := ReflectHandleMsg{
ReflectSubMsg: &reflectSubPayload{
reflectSend := testdata.ReflectHandleMsg{
ReflectSubMsg: &testdata.ReflectSubPayload{
Msgs: []wasmvmtypes.SubMsg{subMsg},
},
}
@ -545,8 +537,8 @@ func TestDispatchSubMsgConditionalReplyOn(t *testing.T) {
}
// query the reflect state to check if the result was stored
query := ReflectQueryMsg{
SubMsgResult: &SubCall{ID: id},
query := testdata.ReflectQueryMsg{
SubMsgResult: &testdata.SubCall{ID: id},
}
queryBz, err := json.Marshal(query)
require.NoError(t, err)

View File

@ -8,6 +8,8 @@ import (
"testing"
"time"
"github.com/CosmWasm/wasmd/x/wasm/keeper/testdata"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/std"
@ -72,7 +74,7 @@ import (
"github.com/CosmWasm/wasmd/x/wasm/types"
)
var ModuleBasics = module.NewBasicManager(
var moduleBasics = module.NewBasicManager(
auth.AppModuleBasic{},
bank.AppModuleBasic{},
capability.AppModuleBasic{},
@ -103,8 +105,8 @@ func MakeEncodingConfig(_ testing.TB) wasmappparams.EncodingConfig {
std.RegisterInterfaces(interfaceRegistry)
std.RegisterLegacyAminoCodec(amino)
ModuleBasics.RegisterLegacyAminoCodec(amino)
ModuleBasics.RegisterInterfaces(interfaceRegistry)
moduleBasics.RegisterLegacyAminoCodec(amino)
moduleBasics.RegisterInterfaces(interfaceRegistry)
// add wasmd types
types.RegisterInterfaces(interfaceRegistry)
types.RegisterLegacyAminoCodec(amino)
@ -545,11 +547,8 @@ func StoreIBCReflectContract(t testing.TB, ctx sdk.Context, keepers TestKeepers)
}
func StoreReflectContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) uint64 {
wasmCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
_, _, creatorAddr := keyPubAddr()
codeID, err := keepers.ContractKeeper.Create(ctx, creatorAddr, wasmCode, nil)
codeID, err := keepers.ContractKeeper.Create(ctx, creatorAddr, testdata.ReflectContractWasm(), nil)
require.NoError(t, err)
return codeID
}

62
x/wasm/keeper/testdata/reflect.go vendored Normal file
View File

@ -0,0 +1,62 @@
package testdata
import (
_ "embed"
typwasmvmtypes "github.com/CosmWasm/wasmvm/types"
"github.com/cosmos/cosmos-sdk/types"
)
//go:embed reflect.wasm
var reflectContract []byte
func ReflectContractWasm() []byte {
return reflectContract
}
// ReflectHandleMsg is used to encode handle messages
type ReflectHandleMsg struct {
Reflect *ReflectPayload `json:"reflect_msg,omitempty"`
ReflectSubMsg *ReflectSubPayload `json:"reflect_sub_msg,omitempty"`
ChangeOwner *OwnerPayload `json:"change_owner,omitempty"`
}
type OwnerPayload struct {
Owner types.Address `json:"owner"`
}
type ReflectPayload struct {
Msgs []typwasmvmtypes.CosmosMsg `json:"msgs"`
}
type ReflectSubPayload struct {
Msgs []typwasmvmtypes.SubMsg `json:"msgs"`
}
// ReflectQueryMsg is used to encode query messages
type ReflectQueryMsg struct {
Owner *struct{} `json:"owner,omitempty"`
Capitalized *Text `json:"capitalized,omitempty"`
Chain *ChainQuery `json:"chain,omitempty"`
SubMsgResult *SubCall `json:"sub_msg_result,omitempty"`
}
type ChainQuery struct {
Request *typwasmvmtypes.QueryRequest `json:"request,omitempty"`
}
type Text struct {
Text string `json:"text"`
}
type SubCall struct {
ID uint64 `json:"id"`
}
type OwnerResponse struct {
Owner string `json:"owner,omitempty"`
}
type ChainResponse struct {
Data []byte `json:"data,omitempty"`
}

View File

@ -13,7 +13,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
simKeeper "github.com/cosmos/cosmos-sdk/x/simulation"
"github.com/gorilla/mux"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/spf13/cast"
@ -105,7 +104,7 @@ type AppModule struct {
keeper *Keeper
validatorSetSource keeper.ValidatorSetSource
accountKeeper types.AccountKeeper // for simulation
bankKeeper simKeeper.BankKeeper
bankKeeper simulation.BankKeeper
}
// ConsensusVersion is a sequence number for state-breaking change of the
@ -120,7 +119,7 @@ func NewAppModule(
keeper *Keeper,
validatorSetSource keeper.ValidatorSetSource,
ak types.AccountKeeper,
bk simKeeper.BankKeeper,
bk simulation.BankKeeper,
) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},

View File

@ -6,6 +6,8 @@ import (
"io/ioutil"
"testing"
"github.com/CosmWasm/wasmd/x/wasm/keeper/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
@ -66,7 +68,7 @@ var (
_, _, addrAcc1 = keyPubAddr()
addr1 = addrAcc1.String()
testContract = mustLoad("./keeper/testdata/hackatom.wasm")
maskContract = mustLoad("./keeper/testdata/reflect.wasm")
maskContract = testdata.ReflectContractWasm()
oldContract = mustLoad("./testdata/escrow_0.7.wasm")
)

View File

@ -8,7 +8,7 @@ import (
// RandomizeGenState generates a random GenesisState for wasm
func RandomizedGenState(simstate *module.SimulationState) {
params := RandomParams(simstate.Rand)
params := types.DefaultParams()
wasmGenesis := types.GenesisState{
Params: params,
Codes: nil,

View File

@ -1,17 +1,22 @@
package simulation
import (
"encoding/json"
"io/ioutil"
"math/rand"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
"github.com/cosmos/cosmos-sdk/baseapp"
simappparams "github.com/cosmos/cosmos-sdk/simapp/params"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/x/simulation"
"github.com/CosmWasm/wasmd/app/params"
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
"github.com/CosmWasm/wasmd/x/wasm/keeper/testdata"
"github.com/CosmWasm/wasmd/x/wasm/types"
)
@ -20,6 +25,7 @@ import (
const (
OpWeightMsgStoreCode = "op_weight_msg_store_code"
OpWeightMsgInstantiateContract = "op_weight_msg_instantiate_contract"
OpWeightMsgExecuteContract = "op_weight_msg_execute_contract"
OpReflectContractPath = "op_reflect_contract_path"
)
@ -27,18 +33,26 @@ const (
type WasmKeeper interface {
GetParams(ctx sdk.Context) types.Params
IterateCodeInfos(ctx sdk.Context, cb func(uint64, types.CodeInfo) bool)
IterateContractInfo(ctx sdk.Context, cb func(sdk.AccAddress, types.ContractInfo) bool)
QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error)
PeekAutoIncrementID(ctx sdk.Context, lastIDKey []byte) uint64
}
type BankKeeper interface {
simulation.BankKeeper
IsSendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool
}
// WeightedOperations returns all the operations from the module with their respective weights
func WeightedOperations(
simstate *module.SimulationState,
ak types.AccountKeeper,
bk simulation.BankKeeper,
bk BankKeeper,
wasmKeeper WasmKeeper,
) simulation.WeightedOperations {
var (
weightMsgStoreCode int
weightMsgInstantiateContract int
weightMsgExecuteContract int
wasmContractPath string
)
@ -53,32 +67,53 @@ func WeightedOperations(
weightMsgInstantiateContract = params.DefaultWeightMsgInstantiateContract
},
)
simstate.AppParams.GetOrGenerate(simstate.Cdc, OpWeightMsgExecuteContract, &weightMsgInstantiateContract, nil,
func(_ *rand.Rand) {
weightMsgExecuteContract = params.DefaultWeightMsgExecuteContract
},
)
simstate.AppParams.GetOrGenerate(simstate.Cdc, OpReflectContractPath, &wasmContractPath, nil,
func(_ *rand.Rand) {
// simulations are run from the `app` folder
wasmContractPath = "../x/wasm/keeper/testdata/reflect.wasm"
wasmContractPath = ""
},
)
wasmBz, err := ioutil.ReadFile(wasmContractPath)
if err != nil {
panic(err)
var wasmBz []byte
if wasmContractPath == "" {
wasmBz = testdata.ReflectContractWasm()
} else {
var err error
wasmBz, err = ioutil.ReadFile(wasmContractPath)
if err != nil {
panic(err)
}
}
return simulation.WeightedOperations{
simulation.NewWeightedOperation(
weightMsgStoreCode,
SimulateMsgStoreCode(ak, bk, wasmKeeper, wasmBz),
SimulateMsgStoreCode(ak, bk, wasmKeeper, wasmBz, 5_000_000),
),
simulation.NewWeightedOperation(
weightMsgInstantiateContract,
SimulateMsgInstantiateContract(ak, bk, wasmKeeper),
SimulateMsgInstantiateContract(ak, bk, wasmKeeper, DefaultSimulationCodeIDSelector),
),
simulation.NewWeightedOperation(
weightMsgExecuteContract,
SimulateMsgExecuteContract(
ak,
bk,
wasmKeeper,
DefaultSimulationExecuteContractSelector,
DefaultSimulationExecuteSenderSelector,
DefaultSimulationExecutePayloader,
),
),
}
}
// SimulateMsgStoreCode generates a MsgStoreCode with random values
func SimulateMsgStoreCode(ak types.AccountKeeper, bk simulation.BankKeeper, wasmKeeper WasmKeeper, wasmBz []byte) simtypes.Operation {
func SimulateMsgStoreCode(ak types.AccountKeeper, bk simulation.BankKeeper, wasmKeeper WasmKeeper, wasmBz []byte, gas uint64) simtypes.Operation {
return func(
r *rand.Rand,
app *baseapp.BaseApp,
@ -90,15 +125,15 @@ func SimulateMsgStoreCode(ak types.AccountKeeper, bk simulation.BankKeeper, wasm
return simtypes.NoOpMsg(types.ModuleName, types.MsgStoreCode{}.Type(), "no chain permission"), nil, nil
}
config := &types.AccessConfig{
Permission: types.AccessTypeEverybody,
}
simAccount, _ := simtypes.RandomAcc(r, accs)
permission := wasmKeeper.GetParams(ctx).InstantiateDefaultPermission
config := permission.With(simAccount.Address)
msg := types.MsgStoreCode{
Sender: simAccount.Address.String(),
WASMByteCode: wasmBz,
InstantiatePermission: config,
InstantiatePermission: &config,
}
txCtx := simulation.OperationInput{
@ -115,12 +150,28 @@ func SimulateMsgStoreCode(ak types.AccountKeeper, bk simulation.BankKeeper, wasm
ModuleName: types.ModuleName,
}
return simulation.GenAndDeliverTxWithRandFees(txCtx)
return GenAndDeliverTxWithRandFees(txCtx, gas)
}
}
// CodeIDSelector returns code id to be used in simulations
type CodeIDSelector = func(ctx sdk.Context, wasmKeeper WasmKeeper) uint64
// DefaultSimulationCodeIDSelector picks the first code id
func DefaultSimulationCodeIDSelector(ctx sdk.Context, wasmKeeper WasmKeeper) uint64 {
var codeID uint64
wasmKeeper.IterateCodeInfos(ctx, func(u uint64, info types.CodeInfo) bool {
if info.InstantiateConfig.Permission != types.AccessTypeEverybody {
return false
}
codeID = u
return true
})
return codeID
}
// SimulateMsgInstantiateContract generates a MsgInstantiateContract with random values
func SimulateMsgInstantiateContract(ak types.AccountKeeper, bk simulation.BankKeeper, wasmKeeper WasmKeeper) simtypes.Operation {
func SimulateMsgInstantiateContract(ak types.AccountKeeper, bk BankKeeper, wasmKeeper WasmKeeper, codeSelector CodeIDSelector) simtypes.Operation {
return func(
r *rand.Rand,
app *baseapp.BaseApp,
@ -130,20 +181,17 @@ func SimulateMsgInstantiateContract(ak types.AccountKeeper, bk simulation.BankKe
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
simAccount, _ := simtypes.RandomAcc(r, accs)
var codeID uint64
wasmKeeper.IterateCodeInfos(ctx, func(u uint64, info types.CodeInfo) bool {
if info.InstantiateConfig.Permission != types.AccessTypeEverybody {
return false
}
codeID = u
return true
})
codeID := codeSelector(ctx, wasmKeeper)
if codeID == 0 {
return simtypes.NoOpMsg(types.ModuleName, types.MsgInstantiateContract{}.Type(), "no codes with permission available"), nil, nil
}
spendable := bk.SpendableCoins(ctx, simAccount.Address)
deposit := sdk.Coins{}
spendableCoins := bk.SpendableCoins(ctx, simAccount.Address)
for _, v := range spendableCoins {
if bk.IsSendEnabledCoin(ctx, v) {
deposit = deposit.Add(simtypes.RandSubsetCoins(r, sdk.NewCoins(v))...)
}
}
msg := types.MsgInstantiateContract{
Sender: simAccount.Address.String(),
@ -151,23 +199,154 @@ func SimulateMsgInstantiateContract(ak types.AccountKeeper, bk simulation.BankKe
CodeID: codeID,
Label: simtypes.RandStringOfLength(r, 10),
Msg: []byte(`{}`),
Funds: simtypes.RandSubsetCoins(r, spendable),
Funds: deposit,
}
txCtx := simulation.OperationInput{
R: r,
App: app,
TxGen: simappparams.MakeTestEncodingConfig().TxConfig,
Cdc: nil,
Msg: &msg,
MsgType: msg.Type(),
Context: ctx,
SimAccount: simAccount,
AccountKeeper: ak,
Bankkeeper: bk,
ModuleName: types.ModuleName,
R: r,
App: app,
TxGen: simappparams.MakeTestEncodingConfig().TxConfig,
Cdc: nil,
Msg: &msg,
MsgType: msg.Type(),
Context: ctx,
SimAccount: simAccount,
AccountKeeper: ak,
Bankkeeper: bk,
ModuleName: types.ModuleName,
CoinsSpentInMsg: deposit,
}
return simulation.GenAndDeliverTxWithRandFees(txCtx)
}
}
// MsgExecuteContractSelector returns contract address to be used in simulations
type MsgExecuteContractSelector = func(ctx sdk.Context, wasmKeeper WasmKeeper) sdk.AccAddress
// MsgExecutePayloader extension point to modify msg with custom payload
type MsgExecutePayloader func(msg *types.MsgExecuteContract) error
// MsgExecuteSenderSelector extension point that returns the sender address
type MsgExecuteSenderSelector func(wasmKeeper WasmKeeper, ctx sdk.Context, contractAddr sdk.AccAddress, accs []simtypes.Account) (simtypes.Account, error)
// SimulateMsgExecuteContract create a execute message a reflect contract instance
func SimulateMsgExecuteContract(
ak types.AccountKeeper,
bk BankKeeper,
wasmKeeper WasmKeeper,
contractSelector MsgExecuteContractSelector,
senderSelector MsgExecuteSenderSelector,
payloader MsgExecutePayloader,
) simtypes.Operation {
return func(
r *rand.Rand,
app *baseapp.BaseApp,
ctx sdk.Context,
accs []simtypes.Account,
chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
contractAddr := contractSelector(ctx, wasmKeeper)
if contractAddr == nil {
return simtypes.NoOpMsg(types.ModuleName, types.MsgExecuteContract{}.Type(), "no contract instance available"), nil, nil
}
simAccount, err := senderSelector(wasmKeeper, ctx, contractAddr, accs)
if err != nil {
return simtypes.NoOpMsg(types.ModuleName, types.MsgExecuteContract{}.Type(), "query contract owner"), nil, err
}
deposit := sdk.Coins{}
spendableCoins := bk.SpendableCoins(ctx, simAccount.Address)
for _, v := range spendableCoins {
if bk.IsSendEnabledCoin(ctx, v) {
deposit = deposit.Add(simtypes.RandSubsetCoins(r, sdk.NewCoins(v))...)
}
}
if deposit.IsZero() {
return simtypes.NoOpMsg(types.ModuleName, types.MsgExecuteContract{}.Type(), "broke account"), nil, nil
}
msg := types.MsgExecuteContract{
Sender: simAccount.Address.String(),
Contract: contractAddr.String(),
Funds: deposit,
}
if err := payloader(&msg); err != nil {
return simtypes.NoOpMsg(types.ModuleName, types.MsgExecuteContract{}.Type(), "contract execute payload"), nil, err
}
txCtx := simulation.OperationInput{
R: r,
App: app,
TxGen: simappparams.MakeTestEncodingConfig().TxConfig,
Cdc: nil,
Msg: &msg,
MsgType: msg.Type(),
Context: ctx,
SimAccount: simAccount,
AccountKeeper: ak,
Bankkeeper: bk,
ModuleName: types.ModuleName,
CoinsSpentInMsg: deposit,
}
return simulation.GenAndDeliverTxWithRandFees(txCtx)
}
}
// DefaultSimulationExecuteContractSelector picks the first contract address
func DefaultSimulationExecuteContractSelector(ctx sdk.Context, wasmKeeper WasmKeeper) sdk.AccAddress {
var r sdk.AccAddress
wasmKeeper.IterateContractInfo(ctx, func(address sdk.AccAddress, info types.ContractInfo) bool {
r = address
return true
})
return r
}
// DefaultSimulationExecuteSenderSelector queries reflect contract for owner address and selects accounts
func DefaultSimulationExecuteSenderSelector(wasmKeeper WasmKeeper, ctx sdk.Context, contractAddr sdk.AccAddress, accs []simtypes.Account) (simtypes.Account, error) {
var none simtypes.Account
bz, err := json.Marshal(testdata.ReflectQueryMsg{Owner: &struct{}{}})
if err != nil {
return none, sdkerrors.Wrap(err, "build smart query")
}
got, err := wasmKeeper.QuerySmart(ctx, contractAddr, bz)
if err != nil {
return none, sdkerrors.Wrap(err, "exec smart query")
}
var ownerRes testdata.OwnerResponse
if err := json.Unmarshal(got, &ownerRes); err != nil || ownerRes.Owner == "" {
return none, sdkerrors.Wrap(err, "parse smart query response")
}
ownerAddr, err := sdk.AccAddressFromBech32(ownerRes.Owner)
if err != nil {
return none, sdkerrors.Wrap(err, "parse contract owner address")
}
simAccount, ok := simtypes.FindAccount(accs, ownerAddr)
if !ok {
return none, sdkerrors.Wrap(err, "unknown contract owner address")
}
return simAccount, nil
}
// DefaultSimulationExecutePayloader implements a bank msg to send the
// tokens from contract account back to original sender
func DefaultSimulationExecutePayloader(msg *types.MsgExecuteContract) error {
reflectSend := testdata.ReflectHandleMsg{
Reflect: &testdata.ReflectPayload{
Msgs: []wasmvmtypes.CosmosMsg{{
Bank: &wasmvmtypes.BankMsg{
Send: &wasmvmtypes.SendMsg{
ToAddress: msg.Sender, //
Amount: wasmkeeper.ConvertSdkCoinsToWasmCoins(msg.Funds),
},
},
}},
},
}
reflectSendBz, err := json.Marshal(reflectSend)
if err != nil {
return err
}
msg.Msg = reflectSendBz
return nil
}

View File

@ -12,7 +12,7 @@ import (
)
func ParamChanges(r *rand.Rand, cdc codec.Codec) []simtypes.ParamChange {
params := RandomParams(r)
params := types.DefaultParams()
return []simtypes.ParamChange{
simulation.NewSimParamChange(types.ModuleName, string(types.ParamStoreKeyUploadAccess),
func(r *rand.Rand) string {
@ -30,13 +30,3 @@ func ParamChanges(r *rand.Rand, cdc codec.Codec) []simtypes.ParamChange {
),
}
}
func RandomParams(r *rand.Rand) types.Params {
permissionType := types.AccessType(simtypes.RandIntBetween(r, 1, 3))
account, _ := simtypes.RandomAcc(r, simtypes.RandomAccounts(r, 10))
accessConfig := permissionType.With(account.Address)
return types.Params{
CodeUploadAccess: accessConfig,
InstantiateDefaultPermission: accessConfig.Permission,
}
}

View File

@ -0,0 +1,53 @@
package simulation
import (
"github.com/cosmos/cosmos-sdk/simapp/helpers"
sdk "github.com/cosmos/cosmos-sdk/types"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/x/simulation"
)
// GenAndDeliverTxWithRandFees generates a transaction with a random fee and delivers it.
func GenAndDeliverTxWithRandFees(txCtx simulation.OperationInput, gas uint64) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
account := txCtx.AccountKeeper.GetAccount(txCtx.Context, txCtx.SimAccount.Address)
spendable := txCtx.Bankkeeper.SpendableCoins(txCtx.Context, account.GetAddress())
var fees sdk.Coins
var err error
coins, hasNeg := spendable.SafeSub(txCtx.CoinsSpentInMsg)
if hasNeg {
return simtypes.NoOpMsg(txCtx.ModuleName, txCtx.MsgType, "message doesn't leave room for fees"), nil, err
}
fees, err = simtypes.RandomFees(txCtx.R, txCtx.Context, coins)
if err != nil {
return simtypes.NoOpMsg(txCtx.ModuleName, txCtx.MsgType, "unable to generate fees"), nil, err
}
return GenAndDeliverTx(txCtx, fees, gas)
}
// GenAndDeliverTx generates a transactions and delivers it.
func GenAndDeliverTx(txCtx simulation.OperationInput, fees sdk.Coins, gas uint64) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
account := txCtx.AccountKeeper.GetAccount(txCtx.Context, txCtx.SimAccount.Address)
tx, err := helpers.GenTx(
txCtx.TxGen,
[]sdk.Msg{txCtx.Msg},
fees,
gas,
txCtx.Context.ChainID(),
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
txCtx.SimAccount.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(txCtx.ModuleName, txCtx.MsgType, "unable to generate mock tx"), nil, err
}
_, _, err = txCtx.App.Deliver(txCtx.TxGen.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(txCtx.ModuleName, txCtx.MsgType, "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(txCtx.Msg, true, "", txCtx.Cdc), nil, nil
}