Skip to content

Commit

Permalink
Merge pull request #273 from CosmWasm/wasm-querier-integration-tests
Browse files Browse the repository at this point in the history
Wasm querier integration tests
  • Loading branch information
ethanfrey authored Oct 6, 2020
2 parents 3b479c3 + 273a192 commit b0add1f
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 7 deletions.
8 changes: 4 additions & 4 deletions x/wasm/internal/keeper/recurse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type recurseWrapper struct {
Recurse Recurse `json:"recurse"`
}

func buildQuery(t *testing.T, msg Recurse) []byte {
func buildRecurseQuery(t *testing.T, msg Recurse) []byte {
wrapper := recurseWrapper{Recurse: msg}
bz, err := json.Marshal(wrapper)
require.NoError(t, err)
Expand Down Expand Up @@ -147,7 +147,7 @@ func TestGasCostOnQuery(t *testing.T) {
// do the query
recurse := tc.msg
recurse.Contract = contractAddr
msg := buildQuery(t, recurse)
msg := buildRecurseQuery(t, recurse)
data, err := keeper.QuerySmart(ctx, contractAddr, msg)
require.NoError(t, err)

Expand Down Expand Up @@ -219,7 +219,7 @@ func TestGasOnExternalQuery(t *testing.T) {

recurse := tc.msg
recurse.Contract = contractAddr
msg := buildQuery(t, recurse)
msg := buildRecurseQuery(t, recurse)

// do the query
path := []string{QueryGetContractState, contractAddr.String(), QueryMethodContractStateSmart}
Expand Down Expand Up @@ -310,7 +310,7 @@ func TestLimitRecursiveQueryGas(t *testing.T) {
// prepare the query
recurse := tc.msg
recurse.Contract = contractAddr
msg := buildQuery(t, recurse)
msg := buildRecurseQuery(t, recurse)

// if we expect out of gas, make sure this panics
if tc.expectOutOfGas {
Expand Down
109 changes: 106 additions & 3 deletions x/wasm/internal/keeper/reflect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,13 @@ type reflectPayload struct {

// MaskQueryMsg is used to encode query messages
type MaskQueryMsg struct {
Owner *struct{} `json:"owner,omitempty"`
Capitalized *Text `json:"capitalized,omitempty"`
Chain *wasmTypes.QueryRequest `json:"chain,omitempty"`
Owner *struct{} `json:"owner,omitempty"`
Capitalized *Text `json:"capitalized,omitempty"`
Chain *ChainQuery `json:"chain,omitempty"`
}

type ChainQuery struct {
Request *wasmTypes.QueryRequest `json:"request,omitempty"`
}

type Text struct {
Expand All @@ -50,6 +54,21 @@ type OwnerResponse struct {
Owner string `json:"owner,omitempty"`
}

type ChainResponse struct {
Data []byte `json:"data,omitempty"`
}

func buildMaskQuery(t *testing.T, query *MaskQueryMsg) []byte {
bz, err := json.Marshal(query)
require.NoError(t, err)
return bz
}

func mustParse(t *testing.T, data []byte, res interface{}) {
err := json.Unmarshal(data, res)
require.NoError(t, err)
}

const MaskFeatures = "staking,mask"

func TestMaskReflectContractSend(t *testing.T) {
Expand Down Expand Up @@ -284,6 +303,90 @@ func TestMaskReflectCustomQuery(t *testing.T) {
assert.Equal(t, resp.Text, "ALL CAPS NOW")
}

type maskState struct {
Owner []byte `json:"owner"`
}

func TestMaskReflectWasmQueries(t *testing.T) {
tempDir, err := ioutil.TempDir("", "wasm")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
ctx, keepers := CreateTestInput(t, false, tempDir, MaskFeatures, maskEncoders(MakeTestCodec()), nil)
accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper

deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
creator := createFakeFundedAccount(ctx, accKeeper, deposit)

// upload mask code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
maskID, err := keeper.Create(ctx, creator, maskCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), maskID)

// creator instantiates a contract and gives it tokens
maskStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
maskAddr, err := keeper.Instantiate(ctx, maskID, creator, nil, []byte("{}"), "mask contract 2", maskStart)
require.NoError(t, err)
require.NotEmpty(t, maskAddr)

// for control, let's make some queries directly on the mask
ownerQuery := buildMaskQuery(t, &MaskQueryMsg{Owner: &struct{}{}})
res, err := keeper.QuerySmart(ctx, maskAddr, ownerQuery)
require.NoError(t, err)
var ownerRes OwnerResponse
mustParse(t, res, &ownerRes)
require.Equal(t, ownerRes.Owner, creator.String())

// and a raw query: cosmwasm_storage::Singleton uses 2 byte big-endian length-prefixed to store data
configKey := append([]byte{0, 6}, []byte("config")...)
models := keeper.QueryRaw(ctx, maskAddr, configKey)
require.Len(t, models, 1)
var stateRes maskState
mustParse(t, models[0].Value, &stateRes)
require.Equal(t, stateRes.Owner, []byte(creator))

// now, let's reflect a smart query into the x/wasm handlers and see if we get the same result
reflectOwnerQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmTypes.QueryRequest{Wasm: &wasmTypes.WasmQuery{
Smart: &wasmTypes.SmartQuery{
ContractAddr: maskAddr.String(),
Msg: ownerQuery,
},
}}}}
reflectOwnerBin := buildMaskQuery(t, &reflectOwnerQuery)
res, err = keeper.QuerySmart(ctx, maskAddr, reflectOwnerBin)
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response
var reflectRes ChainResponse
mustParse(t, res, &reflectRes)
var reflectOwnerRes OwnerResponse
mustParse(t, reflectRes.Data, &reflectOwnerRes)
require.Equal(t, reflectOwnerRes.Owner, creator.String())

// and with queryRaw
reflectStateQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmTypes.QueryRequest{Wasm: &wasmTypes.WasmQuery{
Raw: &wasmTypes.RawQuery{
ContractAddr: maskAddr.String(),
Key: configKey,
},
}}}}
reflectStateBin := buildMaskQuery(t, &reflectStateQuery)
res, err = keeper.QuerySmart(ctx, maskAddr, reflectStateBin)
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response
var reflectRawRes ChainResponse
mustParse(t, res, &reflectRawRes)
// this returns []Model... we need to parse this to actually get the key-value info
var reflectModels []types.Model
mustParse(t, reflectRawRes.Data, &reflectModels)
require.Len(t, reflectModels, 1)
// now, with the raw data, we can parse it into state
var reflectStateRes maskState
mustParse(t, reflectModels[0].Value, &reflectStateRes)
require.Equal(t, reflectStateRes.Owner, []byte(creator))

}

func checkAccount(t *testing.T, ctx sdk.Context, accKeeper auth.AccountKeeper, addr sdk.AccAddress, expected sdk.Coins) {
acct := accKeeper.GetAccount(ctx, addr)
if expected == nil {
Expand Down
135 changes: 135 additions & 0 deletions x/wasm/internal/keeper/staking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"encoding/json"
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/staking"
Expand Down Expand Up @@ -404,6 +405,140 @@ func TestReinvest(t *testing.T) {
assertSupply(t, ctx, keeper, contractAddr, "200000", sdk.NewInt64Coin("stake", 236000))
}

func TestQueryStakingInfo(t *testing.T) {
// STEP 1: take a lot of setup from TestReinvest so we have non-zero info
initInfo := initializeStaking(t)
defer initInfo.cleanup()
ctx, valAddr, contractAddr := initInfo.ctx, initInfo.valAddr, initInfo.contractAddr
keeper, stakingKeeper, accKeeper := initInfo.wasmKeeper, initInfo.stakingKeeper, initInfo.accKeeper
distKeeper := initInfo.distKeeper

// initial checks of bonding state
val, found := stakingKeeper.GetValidator(ctx, valAddr)
require.True(t, found)
assert.Equal(t, sdk.NewInt(1000000), val.Tokens)

// full is 2x funds, 1x goes to the contract, other stays on his wallet
full := sdk.NewCoins(sdk.NewInt64Coin("stake", 400000))
funds := sdk.NewCoins(sdk.NewInt64Coin("stake", 200000))
bob := createFakeFundedAccount(ctx, accKeeper, full)

// we will stake 200k to a validator with 1M self-bond
// this means we should get 1/6 of the rewards
bond := StakingHandleMsg{
Bond: &struct{}{},
}
bondBz, err := json.Marshal(bond)
require.NoError(t, err)
_, err = keeper.Execute(ctx, contractAddr, bob, bondBz, funds)
require.NoError(t, err)

// update height a bit to solidify the delegation
ctx = nextBlock(ctx, stakingKeeper)
// we get 1/6, our share should be 40k minus 10% commission = 36k
setValidatorRewards(ctx, stakingKeeper, distKeeper, valAddr, "240000")

// STEP 2: Prepare the mask contract
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
creator := createFakeFundedAccount(ctx, accKeeper, deposit)

// upload mask code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
maskID, err := keeper.Create(ctx, creator, maskCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(2), maskID)

// creator instantiates a contract and gives it tokens
maskAddr, err := keeper.Instantiate(ctx, maskID, creator, nil, []byte("{}"), "mask contract 2", nil)
require.NoError(t, err)
require.NotEmpty(t, maskAddr)

// STEP 3: now, let's reflect some queries.
// let's get the bonded denom
reflectBondedQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmTypes.QueryRequest{Staking: &wasmTypes.StakingQuery{
BondedDenom: &struct{}{},
}}}}
reflectBondedBin := buildMaskQuery(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
mustParse(t, res, &reflectRes)
var bondedRes wasmTypes.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
reflectValidatorsQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmTypes.QueryRequest{Staking: &wasmTypes.StakingQuery{
Validators: &wasmTypes.ValidatorsQuery{},
}}}}
reflectValidatorsBin := buildMaskQuery(t, &reflectValidatorsQuery)
res, err = keeper.QuerySmart(ctx, maskAddr, reflectValidatorsBin)
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response
mustParse(t, res, &reflectRes)
var validatorRes wasmTypes.ValidatorsResponse
mustParse(t, reflectRes.Data, &validatorRes)
require.Len(t, validatorRes.Validators, 1)
valInfo := validatorRes.Validators[0]
// Note: this ValAddress not AccAddress, may change with #264
require.Equal(t, valAddr.String(), valInfo.Address)
require.Contains(t, valInfo.Commission, "0.100")
require.Contains(t, valInfo.MaxCommission, "0.200")
require.Contains(t, valInfo.MaxChangeRate, "0.010")

// test to get all my delegations
reflectAllDelegationsQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmTypes.QueryRequest{Staking: &wasmTypes.StakingQuery{
AllDelegations: &wasmTypes.AllDelegationsQuery{
Delegator: contractAddr.String(),
},
}}}}
reflectAllDelegationsBin := buildMaskQuery(t, &reflectAllDelegationsQuery)
res, err = keeper.QuerySmart(ctx, maskAddr, reflectAllDelegationsBin)
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response
mustParse(t, res, &reflectRes)
var allDelegationsRes wasmTypes.AllDelegationsResponse
mustParse(t, reflectRes.Data, &allDelegationsRes)
require.Len(t, allDelegationsRes.Delegations, 1)
delInfo := allDelegationsRes.Delegations[0]
// Note: this ValAddress not AccAddress, may change with #264
require.Equal(t, valAddr.String(), delInfo.Validator)
// note this is not bob (who staked to the contract), but the contract itself
require.Equal(t, contractAddr.String(), delInfo.Delegator)
// this is a different Coin type, with String not BigInt, compare field by field
require.Equal(t, funds[0].Denom, delInfo.Amount.Denom)
require.Equal(t, funds[0].Amount.String(), delInfo.Amount.Amount)

// test to get one delegations
reflectDelegationQuery := MaskQueryMsg{Chain: &ChainQuery{Request: &wasmTypes.QueryRequest{Staking: &wasmTypes.StakingQuery{
Delegation: &wasmTypes.DelegationQuery{
Validator: valAddr.String(),
Delegator: contractAddr.String(),
},
}}}}
reflectDelegationBin := buildMaskQuery(t, &reflectDelegationQuery)
res, err = keeper.QuerySmart(ctx, maskAddr, reflectDelegationBin)
require.NoError(t, err)
// first we pull out the data from chain response, before parsing the original response
mustParse(t, res, &reflectRes)
var delegationRes wasmTypes.DelegationResponse
mustParse(t, reflectRes.Data, &delegationRes)
assert.NotEmpty(t, delegationRes.Delegation)
delInfo2 := delegationRes.Delegation
// Note: this ValAddress not AccAddress, may change with #264
require.Equal(t, valAddr.String(), delInfo2.Validator)
// note this is not bob (who staked to the contract), but the contract itself
require.Equal(t, contractAddr.String(), delInfo2.Delegator)
// this is a different Coin type, with String not BigInt, compare field by field
require.Equal(t, funds[0].Denom, delInfo2.Amount.Denom)
require.Equal(t, funds[0].Amount.String(), delInfo2.Amount.Amount)
// TODO: fix this - these should return real values!!! Issue #263
require.Len(t, delInfo2.AccumulatedRewards, 0)
require.Equal(t, delInfo2.CanRedelegate, wasmTypes.NewCoin(0, "stake"))
}

// adds a few validators and returns a list of validators that are registered
func addValidator(ctx sdk.Context, stakingKeeper staking.Keeper, accountKeeper auth.AccountKeeper, value sdk.Coin) sdk.ValAddress {
_, pub, accAddr := keyPubAddr()
Expand Down

0 comments on commit b0add1f

Please sign in to comment.