Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wasm querier integration tests #273

Merged
merged 6 commits into from
Oct 6, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
110 changes: 107 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,91 @@ 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)
//_, _, bob := keyPubAddr()
ethanfrey marked this conversation as resolved.
Show resolved Hide resolved

// 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
configKey := append([]byte{0, 6}, []byte("config")...)
ethanfrey marked this conversation as resolved.
Show resolved Hide resolved
models := keeper.QueryRaw(ctx, maskAddr, configKey)
require.Equal(t, 1, len(models))
ethanfrey marked this conversation as resolved.
Show resolved Hide resolved
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.Equal(t, 1, len(reflectModels))
// 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
102 changes: 102 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,8 @@ package keeper

import (
"encoding/json"
"fmt"
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 +406,106 @@ 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)
//initPower := val.GetDelegatorShares()
ethanfrey marked this conversation as resolved.
Show resolved Hide resolved
assert.Equal(t, val.Tokens, sdk.NewInt(1000000), "%s", val.Tokens)
ethanfrey marked this conversation as resolved.
Show resolved Hide resolved

// 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.
// 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
var reflectRes ChainResponse
mustParse(t, res, &reflectRes)
var validatorRes wasmTypes.ValidatorsResponse
mustParse(t, reflectRes.Data, &validatorRes)
require.Equal(t, 1, len(validatorRes.Validators))
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.Equal(t, 1, len(allDelegationsRes.Delegations))
delInfo := allDelegationsRes.Delegations[0]
fmt.Printf("%#v\n", delInfo)
// 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)

// TODO: more delegation queries
// BondedDenom -> Atom
// Delegation -> With detailed info
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pp: there was quite some effort to setup the test. I was wondering if a staking keeper mock would be an easier fit or what was your intention?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to see how the query callbacks from the contracts really work.

I have a contract that will take the user input and use that to trigger a query via Querier. This allows us to see what a contract would see. And we need to see what would happen against a real staking keeper (as there are some bugs... we want to fix how those work, full stack for compatibility).

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