From 920d837319e7c868bf767de2f32cc5d45e987d23 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Wed, 27 Oct 2021 12:56:42 +0200 Subject: [PATCH 1/6] Start poe staking query plugin --- x/poe/wasm/query_plugin.go | 118 +++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 x/poe/wasm/query_plugin.go diff --git a/x/poe/wasm/query_plugin.go b/x/poe/wasm/query_plugin.go new file mode 100644 index 00000000..7d8c8371 --- /dev/null +++ b/x/poe/wasm/query_plugin.go @@ -0,0 +1,118 @@ +package wasm + +import ( + "encoding/json" + + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/confio/tgrade/x/poe/keeper" +) + +func StakingQuerier(poeKeeper keeper.Keeper) func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) { + return func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) { + if request.BondedDenom != nil { + denom := poeKeeper.GetBondDenom(ctx) + res := wasmvmtypes.BondedDenomResponse{ + Denom: denom, + } + return json.Marshal(res) + } + if request.AllValidators != nil { + validators, err := poeKeeper.ValsetContract(ctx).ListValidators(ctx) + if err != nil { + return nil, err + } + wasmVals := make([]wasmvmtypes.Validator, len(validators)) + for i, v := range validators { + wasmVals[i] = wasmvmtypes.Validator{ + Address: v.OperatorAddress, + Commission: v.Commission.Rate.String(), + MaxCommission: v.Commission.MaxRate.String(), + MaxChangeRate: v.Commission.MaxChangeRate.String(), + } + } + res := wasmvmtypes.AllValidatorsResponse{ + Validators: wasmVals, + } + return json.Marshal(res) + } + if request.Validator != nil { + valAddr, err := sdk.AccAddressFromBech32(request.Validator.Address) + if err != nil { + return nil, err + } + v, err := poeKeeper.ValsetContract(ctx).QueryValidator(ctx, valAddr) + if err != nil { + return nil, err + } + res := wasmvmtypes.ValidatorResponse{} + if v != nil { + res.Validator = &wasmvmtypes.Validator{ + Address: v.OperatorAddress, + Commission: v.Commission.Rate.String(), + MaxCommission: v.Commission.MaxRate.String(), + MaxChangeRate: v.Commission.MaxChangeRate.String(), + } + } + return json.Marshal(res) + } + if request.AllDelegations != nil { + delegator, err := sdk.AccAddressFromBech32(request.AllDelegations.Delegator) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.AllDelegations.Delegator) + } + stakedAmount, err := poeKeeper.StakeContract(ctx).QueryStakedAmount(ctx, delegator) + if err != nil { + return nil, err + } + var res wasmvmtypes.AllDelegationsResponse + if stakedAmount != nil { + res.Delegations = append(res.Delegations, wasmvmtypes.Delegation{ + Delegator: delegator.String(), + Validator: delegator.String(), + Amount: wasmvmtypes.NewCoin(stakedAmount.Uint64(), poeKeeper.GetBondDenom(ctx)), + }) + } + return json.Marshal(res) + } + if request.Delegation != nil { + delegator, err := sdk.AccAddressFromBech32(request.Delegation.Delegator) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Delegation.Delegator) + } + validator, err := sdk.ValAddressFromBech32(request.Delegation.Validator) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Delegation.Validator) + } + + var res wasmvmtypes.DelegationResponse + if !delegator.Equals(validator) { // no match + return json.Marshal(res) + } + stakeContract := poeKeeper.StakeContract(ctx) + stakedAmount, err := stakeContract.QueryStakedAmount(ctx, delegator) + if err != nil { + return nil, err + } + if stakedAmount != nil { + stakedCoin := wasmvmtypes.NewCoin(stakedAmount.Uint64(), poeKeeper.GetBondDenom(ctx)) + res.Delegation = &wasmvmtypes.FullDelegation{ + Delegator: delegator.String(), + Validator: delegator.String(), + Amount: stakedCoin, + CanRedelegate: stakedCoin, + AccumulatedRewards: nil, + } + reward, err := poeKeeper.DistributionContract(ctx).ValidatorOutstandingReward(ctx, delegator) + if err != nil { + return nil, err + } + res.Delegation.AccumulatedRewards = wasmvmtypes.Coins{wasmvmtypes.NewCoin(reward.Amount.Uint64(), reward.Denom)} + } + return json.Marshal(res) + } + return nil, wasmvmtypes.UnsupportedRequest{Kind: "unknown Staking variant"} + } +} From 294e1fa6f34a7cee8ec518cb397d084b9a32592c Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Thu, 28 Oct 2021 10:24:13 +0200 Subject: [PATCH 2/6] Move wasm handler setup to app package --- app/app.go | 2 ++ app/wasm.go | 50 +++++++++++++++++++++++++++ x/poe/keeper/test_common.go | 25 +++++++++++++- x/poe/wasm/query_plugin.go | 9 ++++- x/twasm/keeper/handler_plugin.go | 10 +++--- x/twasm/keeper/handler_plugin_test.go | 2 +- x/twasm/keeper/keeper.go | 30 ---------------- x/twasm/keeper/test_common.go | 23 +++++++++++- 8 files changed, 113 insertions(+), 38 deletions(-) create mode 100644 app/wasm.go diff --git a/app/app.go b/app/app.go index 9010fddb..5bb4ed2d 100644 --- a/app/app.go +++ b/app/app.go @@ -280,6 +280,8 @@ func NewTgradeApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest // TODO: add tgrade here soon supportedFeatures := "staking,stargate,iterator" + wasmOpts = append(SetupWasmHandlers(appCodec, app.bankKeeper, govRouter, &app.twasmKeeper, &app.poeKeeper), wasmOpts...) + stakingAdapter := stakingKeeper app.twasmKeeper = twasmkeeper.NewKeeper( appCodec, diff --git a/app/wasm.go b/app/wasm.go new file mode 100644 index 00000000..0b2358f3 --- /dev/null +++ b/app/wasm.go @@ -0,0 +1,50 @@ +package app + +import ( + "github.com/CosmWasm/wasmd/x/wasm" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + poewasm "github.com/confio/tgrade/x/poe/wasm" + twasmkeeper "github.com/confio/tgrade/x/twasm/keeper" + twasmtypes "github.com/confio/tgrade/x/twasm/types" +) + +func SetupWasmHandlers(cdc codec.Marshaler, + bankKeeper twasmtypes.BankKeeper, + govRouter govtypes.Router, + result twasmkeeper.TgradeWasmHandlerKeeper, + poeKeeper poewasm.ViewKeeper, +) []wasmkeeper.Option { + extMessageHandlerOpt := wasmkeeper.WithMessageHandlerDecorator(func(nested wasmkeeper.Messenger) wasmkeeper.Messenger { + return wasmkeeper.NewMessageHandlerChain( + // disable staking messages + wasmkeeper.MessageHandlerFunc(func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { + if msg.Staking != nil { + return nil, nil, sdkerrors.Wrap(wasmtypes.ErrExecuteFailed, "not supported, yet") + } + return nil, nil, wasmtypes.ErrUnknownMsg + }), + nested, + // append our custom message handler + twasmkeeper.NewTgradeHandler(cdc, result, bankKeeper, govRouter), + ) + }) + extQueryHandlerOpt := wasmkeeper.WithQueryHandlerDecorator(func(nested wasmkeeper.WasmVMQueryHandler) wasmkeeper.WasmVMQueryHandler { + return wasmkeeper.WasmVMQueryHandlerFn(func(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) { + if request.Staking != nil { + return poewasm.StakingQuerier(poeKeeper)(ctx, request.Staking) + } + return nested.HandleQuery(ctx, caller, request) + }) + }) + return []wasm.Option{ + extMessageHandlerOpt, + extQueryHandlerOpt, + } +} diff --git a/x/poe/keeper/test_common.go b/x/poe/keeper/test_common.go index 2935cae3..196af72c 100644 --- a/x/poe/keeper/test_common.go +++ b/x/poe/keeper/test_common.go @@ -4,6 +4,10 @@ import ( "testing" "time" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/CosmWasm/wasmd/x/wasm" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" "github.com/CosmWasm/wasmd/x/wasm/keeper/wasmtesting" @@ -202,7 +206,26 @@ func createTestInput( stakingAdapter := stakingadapter.NewStakingAdapter(nil, nil) twasmSubspace := paramsKeeper.Subspace(twasmtypes.DefaultParamspace) - twasmKeeper := twasmkeeper.NewKeeper( + + var twasmKeeper twasmkeeper.Keeper + handler := wasmkeeper.WithMessageHandlerDecorator(func(nested wasmkeeper.Messenger) wasmkeeper.Messenger { + return wasmkeeper.NewMessageHandlerChain( + // disable staking messages + wasmkeeper.MessageHandlerFunc(func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { + if msg.Staking != nil { + return nil, nil, sdkerrors.Wrap(wasmtypes.ErrExecuteFailed, "not supported, yet") + } + return nil, nil, wasmtypes.ErrUnknownMsg + }), + nested, + // append our custom message handler + twasmkeeper.NewTgradeHandler(appCodec, &twasmKeeper, bankKeeper, nil), + ) + }) + + opts = append([]wasmkeeper.Option{handler}, opts...) + + twasmKeeper = twasmkeeper.NewKeeper( appCodec, keyWasm, twasmSubspace, diff --git a/x/poe/wasm/query_plugin.go b/x/poe/wasm/query_plugin.go index 7d8c8371..087109bf 100644 --- a/x/poe/wasm/query_plugin.go +++ b/x/poe/wasm/query_plugin.go @@ -10,7 +10,14 @@ import ( "github.com/confio/tgrade/x/poe/keeper" ) -func StakingQuerier(poeKeeper keeper.Keeper) func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) { +type ViewKeeper interface { + GetBondDenom(ctx sdk.Context) string + DistributionContract(ctx sdk.Context) keeper.DistributionContract + ValsetContract(ctx sdk.Context) keeper.ValsetContract + StakeContract(ctx sdk.Context) keeper.StakeContract +} + +func StakingQuerier(poeKeeper ViewKeeper) func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) { return func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) { if request.BondedDenom != nil { denom := poeKeeper.GetBondDenom(ctx) diff --git a/x/twasm/keeper/handler_plugin.go b/x/twasm/keeper/handler_plugin.go index f6b7d5e4..360568e6 100644 --- a/x/twasm/keeper/handler_plugin.go +++ b/x/twasm/keeper/handler_plugin.go @@ -13,14 +13,16 @@ import ( "github.com/confio/tgrade/x/twasm/types" ) -// tgradeKeeper defines a subset of Keeper -type tgradeKeeper interface { +// TgradeWasmHandlerKeeper defines a subset of Keeper +type TgradeWasmHandlerKeeper interface { IsPrivileged(ctx sdk.Context, contract sdk.AccAddress) bool appendToPrivilegedContracts(ctx sdk.Context, privilegeType types.PrivilegeType, contractAddress sdk.AccAddress) (uint8, error) removePrivilegeRegistration(ctx sdk.Context, privilegeType types.PrivilegeType, pos uint8, contractAddr sdk.AccAddress) bool setContractDetails(ctx sdk.Context, contract sdk.AccAddress, details *types.TgradeContractDetails) error GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *wasmtypes.ContractInfo } + +// minter is a subset of bank keeper type minter interface { MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error @@ -30,14 +32,14 @@ var _ wasmkeeper.Messenger = TgradeHandler{} // TgradeHandler is a custom message handler plugin for wasmd. type TgradeHandler struct { - keeper tgradeKeeper + keeper TgradeWasmHandlerKeeper minter minter govRouter govtypes.Router cdc codec.Marshaler } // NewTgradeHandler constructor -func NewTgradeHandler(cdc codec.Marshaler, keeper tgradeKeeper, bankKeeper minter, govRouter govtypes.Router) *TgradeHandler { +func NewTgradeHandler(cdc codec.Marshaler, keeper TgradeWasmHandlerKeeper, bankKeeper minter, govRouter govtypes.Router) *TgradeHandler { return &TgradeHandler{cdc: cdc, keeper: keeper, govRouter: govRouter, minter: bankKeeper} } diff --git a/x/twasm/keeper/handler_plugin_test.go b/x/twasm/keeper/handler_plugin_test.go index a2372604..a629ea58 100644 --- a/x/twasm/keeper/handler_plugin_test.go +++ b/x/twasm/keeper/handler_plugin_test.go @@ -601,7 +601,7 @@ func noopRegisterHook(m *handlerTgradeKeeperMock, mutators ...func(*wasmtypes.Co } } -var _ tgradeKeeper = handlerTgradeKeeperMock{} +var _ TgradeWasmHandlerKeeper = handlerTgradeKeeperMock{} type handlerTgradeKeeperMock struct { IsPrivilegedFn func(ctx sdk.Context, contract sdk.AccAddress) bool diff --git a/x/twasm/keeper/keeper.go b/x/twasm/keeper/keeper.go index 0dc5b332..0365c9ab 100644 --- a/x/twasm/keeper/keeper.go +++ b/x/twasm/keeper/keeper.go @@ -3,10 +3,8 @@ package keeper import ( "fmt" - "github.com/CosmWasm/wasmd/x/wasm" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - wasmvmtypes "github.com/CosmWasm/wasmvm/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -54,34 +52,6 @@ func NewKeeper( govRouter: govRouter, } // configure wasm keeper via options - - extMessageHandlerOpt := wasmkeeper.WithMessageHandlerDecorator(func(nested wasmkeeper.Messenger) wasmkeeper.Messenger { - return wasmkeeper.NewMessageHandlerChain( - // disable staking messages - wasmkeeper.MessageHandlerFunc(func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { - if msg.Staking != nil { - return nil, nil, sdkerrors.Wrap(wasmtypes.ErrExecuteFailed, "not supported, yet") - } - return nil, nil, wasmtypes.ErrUnknownMsg - }), - nested, - // append our custom message handler - NewTgradeHandler(cdc, &result, bankKeeper, govRouter), - ) - }) - extQueryHandlerOpt := wasmkeeper.WithQueryHandlerDecorator(func(nested wasmkeeper.WasmVMQueryHandler) wasmkeeper.WasmVMQueryHandler { - return wasmkeeper.WasmVMQueryHandlerFn(func(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) { - if request.Staking != nil { - return nil, wasmvmtypes.UnsupportedRequest{Kind: "not supported, yet"} - } - return nested.HandleQuery(ctx, caller, request) - }) - }) - opts = append([]wasm.Option{ - extMessageHandlerOpt, - extQueryHandlerOpt, - }, opts...) - result.Keeper = wasmkeeper.NewKeeper( cdc, storeKey, diff --git a/x/twasm/keeper/test_common.go b/x/twasm/keeper/test_common.go index 064136d6..b0db5c1c 100644 --- a/x/twasm/keeper/test_common.go +++ b/x/twasm/keeper/test_common.go @@ -4,6 +4,10 @@ import ( "testing" "time" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" "github.com/CosmWasm/wasmd/x/wasm/keeper/wasmtesting" "github.com/cosmos/cosmos-sdk/baseapp" @@ -257,7 +261,24 @@ func createTestInput( stakingtypes.RegisterQueryServer(querier, stakingkeeper.Querier{Keeper: stakingKeeper}) distributiontypes.RegisterQueryServer(querier, distKeeper) - keeper := NewKeeper( + var keeper Keeper + handler := wasmkeeper.WithMessageHandlerDecorator(func(nested wasmkeeper.Messenger) wasmkeeper.Messenger { + return wasmkeeper.NewMessageHandlerChain( + // disable staking messages + wasmkeeper.MessageHandlerFunc(func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { + if msg.Staking != nil { + return nil, nil, sdkerrors.Wrap(wasmtypes.ErrExecuteFailed, "not supported, yet") + } + return nil, nil, wasmtypes.ErrUnknownMsg + }), + nested, + // append our custom message handler + NewTgradeHandler(appCodec, &keeper, bankKeeper, nil), + ) + }) + + opts = append([]wasmkeeper.Option{handler}, opts...) + keeper = NewKeeper( appCodec, keyWasm, paramsKeeper.Subspace(types.DefaultParamspace), From 8feb46e84db97dbd69c806da84ac8f5412729345 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Tue, 2 Nov 2021 18:25:49 +0100 Subject: [PATCH 3/6] Start query tests --- app/wasm.go | 14 +- x/poe/keeper/poetesting/mock_contracts.go | 2 +- x/poe/wasm/query_plugin.go | 15 +- x/poe/wasm/query_plugin_test.go | 222 ++++++++++++++++++++++ 4 files changed, 236 insertions(+), 17 deletions(-) create mode 100644 x/poe/wasm/query_plugin_test.go diff --git a/app/wasm.go b/app/wasm.go index 0b2358f3..d3f94d8d 100644 --- a/app/wasm.go +++ b/app/wasm.go @@ -21,6 +21,10 @@ func SetupWasmHandlers(cdc codec.Marshaler, result twasmkeeper.TgradeWasmHandlerKeeper, poeKeeper poewasm.ViewKeeper, ) []wasmkeeper.Option { + queryPluginOpt := wasmkeeper.WithQueryPlugins(&wasmkeeper.QueryPlugins{ + Staking: poewasm.StakingQuerier(poeKeeper), + }) + extMessageHandlerOpt := wasmkeeper.WithMessageHandlerDecorator(func(nested wasmkeeper.Messenger) wasmkeeper.Messenger { return wasmkeeper.NewMessageHandlerChain( // disable staking messages @@ -35,16 +39,8 @@ func SetupWasmHandlers(cdc codec.Marshaler, twasmkeeper.NewTgradeHandler(cdc, result, bankKeeper, govRouter), ) }) - extQueryHandlerOpt := wasmkeeper.WithQueryHandlerDecorator(func(nested wasmkeeper.WasmVMQueryHandler) wasmkeeper.WasmVMQueryHandler { - return wasmkeeper.WasmVMQueryHandlerFn(func(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) { - if request.Staking != nil { - return poewasm.StakingQuerier(poeKeeper)(ctx, request.Staking) - } - return nested.HandleQuery(ctx, caller, request) - }) - }) return []wasm.Option{ + queryPluginOpt, extMessageHandlerOpt, - extQueryHandlerOpt, } } diff --git a/x/poe/keeper/poetesting/mock_contracts.go b/x/poe/keeper/poetesting/mock_contracts.go index 627adf50..e49c5603 100644 --- a/x/poe/keeper/poetesting/mock_contracts.go +++ b/x/poe/keeper/poetesting/mock_contracts.go @@ -51,7 +51,7 @@ func (m ValsetContractMock) QueryConfig(ctx types.Context) (*contract.ValsetConf return m.QueryConfigFn(ctx) } -//var _ keeper.StakeContract = StakeContractMock{} +// var _ keeper.StakeContract = StakeContractMock{} type StakeContractMock struct { QueryStakingUnbondingPeriodFn func(ctx types.Context) (time.Duration, error) diff --git a/x/poe/wasm/query_plugin.go b/x/poe/wasm/query_plugin.go index 087109bf..ba05b499 100644 --- a/x/poe/wasm/query_plugin.go +++ b/x/poe/wasm/query_plugin.go @@ -26,6 +26,7 @@ func StakingQuerier(poeKeeper ViewKeeper) func(ctx sdk.Context, request *wasmvmt } return json.Marshal(res) } + zero := sdk.ZeroDec().String() if request.AllValidators != nil { validators, err := poeKeeper.ValsetContract(ctx).ListValidators(ctx) if err != nil { @@ -35,9 +36,9 @@ func StakingQuerier(poeKeeper ViewKeeper) func(ctx sdk.Context, request *wasmvmt for i, v := range validators { wasmVals[i] = wasmvmtypes.Validator{ Address: v.OperatorAddress, - Commission: v.Commission.Rate.String(), - MaxCommission: v.Commission.MaxRate.String(), - MaxChangeRate: v.Commission.MaxChangeRate.String(), + Commission: zero, + MaxCommission: zero, + MaxChangeRate: zero, } } res := wasmvmtypes.AllValidatorsResponse{ @@ -58,9 +59,9 @@ func StakingQuerier(poeKeeper ViewKeeper) func(ctx sdk.Context, request *wasmvmt if v != nil { res.Validator = &wasmvmtypes.Validator{ Address: v.OperatorAddress, - Commission: v.Commission.Rate.String(), - MaxCommission: v.Commission.MaxRate.String(), - MaxChangeRate: v.Commission.MaxChangeRate.String(), + Commission: zero, + MaxCommission: zero, + MaxChangeRate: zero, } } return json.Marshal(res) @@ -89,7 +90,7 @@ func StakingQuerier(poeKeeper ViewKeeper) func(ctx sdk.Context, request *wasmvmt if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Delegation.Delegator) } - validator, err := sdk.ValAddressFromBech32(request.Delegation.Validator) + validator, err := sdk.AccAddressFromBech32(request.Delegation.Validator) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Delegation.Validator) } diff --git a/x/poe/wasm/query_plugin_test.go b/x/poe/wasm/query_plugin_test.go new file mode 100644 index 00000000..4113c2fb --- /dev/null +++ b/x/poe/wasm/query_plugin_test.go @@ -0,0 +1,222 @@ +package wasm + +import ( + "testing" + + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/confio/tgrade/x/poe/keeper" + "github.com/confio/tgrade/x/poe/keeper/poetesting" + poetypes "github.com/confio/tgrade/x/poe/types" +) + +func TestStakingQuerier(t *testing.T) { + specs := map[string]struct { + src wasmvmtypes.StakingQuery + mock ViewKeeper + expJson string + expErr bool + }{ + "bonded denum": { + src: wasmvmtypes.StakingQuery{BondedDenom: &struct{}{}}, + mock: ViewKeeperMock{GetBondDenomFn: func(ctx sdk.Context) string { + return "alx" + }}, + expJson: `{"denom": "alx"}`, + }, + "all validators - single": { + src: wasmvmtypes.StakingQuery{AllValidators: &wasmvmtypes.AllValidatorsQuery{}}, + mock: ViewKeeperMock{ValsetContractFn: func(ctx sdk.Context) keeper.ValsetContract { + return poetesting.ValsetContractMock{ + ListValidatorsFn: func(ctx sdk.Context) ([]stakingtypes.Validator, error) { + resp := []stakingtypes.Validator{ + poetypes.ValidatorFixture(func(m *stakingtypes.Validator) { + m.OperatorAddress = "myOperatorAddress" + }), + } + return resp, nil + }, + } + }}, + expJson: `{"validators":[{"address":"myOperatorAddress","commission":"0.000000000000000000","max_commission":"0.000000000000000000","max_change_rate":"0.000000000000000000"}]}`, + }, + "all validators - multiple": { + src: wasmvmtypes.StakingQuery{AllValidators: &wasmvmtypes.AllValidatorsQuery{}}, + mock: ViewKeeperMock{ValsetContractFn: func(ctx sdk.Context) keeper.ValsetContract { + return poetesting.ValsetContractMock{ + ListValidatorsFn: func(ctx sdk.Context) ([]stakingtypes.Validator, error) { + resp := []stakingtypes.Validator{ + poetypes.ValidatorFixture(func(m *stakingtypes.Validator) { + m.OperatorAddress = "myOperatorAddress" + }), + poetypes.ValidatorFixture(func(m *stakingtypes.Validator) { + m.OperatorAddress = "myOtherOperatorAddress" + }), + } + return resp, nil + }, + } + }}, + expJson: `{"validators":[ +{"address":"myOperatorAddress","commission":"0.000000000000000000","max_commission":"0.000000000000000000","max_change_rate":"0.000000000000000000"}, +{"address":"myOtherOperatorAddress","commission":"0.000000000000000000","max_commission":"0.000000000000000000","max_change_rate":"0.000000000000000000"} + ]}`, + }, + "query validator": { + src: wasmvmtypes.StakingQuery{Validator: &wasmvmtypes.ValidatorQuery{Address: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3"}}, + mock: ViewKeeperMock{ValsetContractFn: func(ctx sdk.Context) keeper.ValsetContract { + return poetesting.ValsetContractMock{ + QueryValidatorFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*stakingtypes.Validator, error) { + val := poetypes.ValidatorFixture(func(m *stakingtypes.Validator) { + m.OperatorAddress = "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3" + }) + return &val, nil + }, + } + }}, + expJson: `{"validator":{"address":"cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3","commission":"0.000000000000000000","max_commission":"0.000000000000000000","max_change_rate":"0.000000000000000000"}}`, + }, + "query validator - unknown address": { + src: wasmvmtypes.StakingQuery{Validator: &wasmvmtypes.ValidatorQuery{Address: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3"}}, + mock: ViewKeeperMock{ValsetContractFn: func(ctx sdk.Context) keeper.ValsetContract { + return poetesting.ValsetContractMock{ + QueryValidatorFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*stakingtypes.Validator, error) { + return nil, nil + }, + } + }}, + expJson: `{"validator": null}`, + }, + "query validator - invalid address": { + src: wasmvmtypes.StakingQuery{Validator: &wasmvmtypes.ValidatorQuery{Address: "not a valid address"}}, + expErr: true, + }, + "all delegations": { + src: wasmvmtypes.StakingQuery{AllDelegations: &wasmvmtypes.AllDelegationsQuery{Delegator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3"}}, + mock: ViewKeeperMock{StakeContractFn: func(ctx sdk.Context) keeper.StakeContract { + return poetesting.StakeContractMock{ + QueryStakedAmountFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { + myValue := sdk.OneInt() + return &myValue, nil + }, + } + }, + GetBondDenomFn: func(ctx sdk.Context) string { + return "alx" + }}, + expJson: `{"delegations":[{"delegator":"cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3","validator":"cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3","amount":{"denom":"alx","amount":"1"}}]}`, + }, + "all delegations - unknown address": { + src: wasmvmtypes.StakingQuery{AllDelegations: &wasmvmtypes.AllDelegationsQuery{Delegator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3"}}, + mock: ViewKeeperMock{StakeContractFn: func(ctx sdk.Context) keeper.StakeContract { + return poetesting.StakeContractMock{ + QueryStakedAmountFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { + return nil, nil + }, + } + }}, + expJson: `{"delegations":[]}`, + }, + "all delegations - invalid address": { + src: wasmvmtypes.StakingQuery{AllDelegations: &wasmvmtypes.AllDelegationsQuery{Delegator: "not a valid address"}}, + expErr: true, + }, + "query delegation": { + src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", Validator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3"}}, + mock: ViewKeeperMock{ + StakeContractFn: func(ctx sdk.Context) keeper.StakeContract { + return poetesting.StakeContractMock{ + QueryStakedAmountFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { + myValue := sdk.OneInt() + return &myValue, nil + }, + } + }, + GetBondDenomFn: func(ctx sdk.Context) string { + return "alx" + }, + DistributionContractFn: func(ctx sdk.Context) keeper.DistributionContract { + return poetesting.DistributionContractMock{ValidatorOutstandingRewardFn: func(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { + return sdk.NewCoin("alx", sdk.NewInt(2)), nil + }} + }, + }, + expJson: `{ + "delegation": { + "delegator": "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", + "validator": "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", + "amount": { + "denom": "alx", + "amount": "1" + }, + "accumulated_rewards": [ + { + "denom": "alx", + "amount": "2" + } + ], + "can_redelegate": { + "denom": "alx", + "amount": "1" + } + } +} +`, + // todo: + // - check address do not match + // - unknown address + // - invalid address + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + q := StakingQuerier(spec.mock) + gotRsp, gotErr := q(sdk.Context{}, &spec.src) + if spec.expErr { + require.Error(t, gotErr) + return + } + require.NoError(t, gotErr) + assert.JSONEq(t, spec.expJson, string(gotRsp), string(gotRsp)) + }) + } +} + +type ViewKeeperMock struct { + GetBondDenomFn func(ctx sdk.Context) string + DistributionContractFn func(ctx sdk.Context) keeper.DistributionContract + ValsetContractFn func(ctx sdk.Context) keeper.ValsetContract + StakeContractFn func(ctx sdk.Context) keeper.StakeContract +} + +func (m ViewKeeperMock) GetBondDenom(ctx sdk.Context) string { + if m.GetBondDenomFn == nil { + panic("not expected to be called") + } + return m.GetBondDenomFn(ctx) +} + +func (m ViewKeeperMock) DistributionContract(ctx sdk.Context) keeper.DistributionContract { + if m.DistributionContractFn == nil { + panic("not expected to be called") + } + return m.DistributionContractFn(ctx) +} + +func (m ViewKeeperMock) ValsetContract(ctx sdk.Context) keeper.ValsetContract { + if m.ValsetContractFn == nil { + panic("not expected to be called") + } + return m.ValsetContractFn(ctx) +} + +func (m ViewKeeperMock) StakeContract(ctx sdk.Context) keeper.StakeContract { + if m.StakeContractFn == nil { + panic("not expected to be called") + } + return m.StakeContractFn(ctx) +} From b1d644c1692e9fd182de7531c32f031216a44c06 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Thu, 4 Nov 2021 09:48:56 +0100 Subject: [PATCH 4/6] Complete query tests --- x/poe/wasm/query_plugin_test.go | 56 ++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/x/poe/wasm/query_plugin_test.go b/x/poe/wasm/query_plugin_test.go index 4113c2fb..62a8b67b 100644 --- a/x/poe/wasm/query_plugin_test.go +++ b/x/poe/wasm/query_plugin_test.go @@ -1,6 +1,7 @@ package wasm import ( + "github.com/confio/tgrade/x/twasm/types" "testing" wasmvmtypes "github.com/CosmWasm/wasmvm/types" @@ -15,6 +16,7 @@ import ( ) func TestStakingQuerier(t *testing.T) { + t.Log(types.RandomBech32Address(t)) specs := map[string]struct { src wasmvmtypes.StakingQuery mock ViewKeeper @@ -166,10 +168,56 @@ func TestStakingQuerier(t *testing.T) { } } `, - // todo: - // - check address do not match - // - unknown address - // - invalid address + }, + "query delegation - address do not match - return empty result": { + src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", Validator: "cosmos17emnuddq662fpxpnd43ch0396452d48vc8ufsw"}}, + mock: ViewKeeperMock{ + StakeContractFn: func(ctx sdk.Context) keeper.StakeContract { + return poetesting.StakeContractMock{ + QueryStakedAmountFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { + myValue := sdk.OneInt() + return &myValue, nil + }, + } + }, + GetBondDenomFn: func(ctx sdk.Context) string { + return "alx" + }, + DistributionContractFn: func(ctx sdk.Context) keeper.DistributionContract { + return poetesting.DistributionContractMock{ValidatorOutstandingRewardFn: func(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { + return sdk.NewCoin("alx", sdk.NewInt(2)), nil + }} + }, + }, + expJson: `{}`, + }, + "query delegation - unknown address return empty result": { + src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", Validator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3"}}, + mock: ViewKeeperMock{ + StakeContractFn: func(ctx sdk.Context) keeper.StakeContract { + return poetesting.StakeContractMock{ + QueryStakedAmountFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { + return nil, nil + }, + } + }, + GetBondDenomFn: func(ctx sdk.Context) string { + return "alx" + }, + }, + expJson: `{}`, + }, + "query delegation - invalid delegator address": { + src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "not a valid address", Validator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3"}}, + expErr: true, + }, + "query delegation - invalid validator address": { + src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", Validator: "not a valid address"}}, + expErr: true, + }, + "unknown query": { + src: wasmvmtypes.StakingQuery{}, + expErr: true, }, } for name, spec := range specs { From 956bd35871c84544c9e7c63d0b407910b864ee8b Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Thu, 4 Nov 2021 09:53:04 +0100 Subject: [PATCH 5/6] Minor cleanups --- x/poe/contract/tg4_stake.go | 5 ----- x/poe/contract/tgrade_valset.go | 2 +- x/poe/wasm/query_plugin_test.go | 3 ++- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/x/poe/contract/tg4_stake.go b/x/poe/contract/tg4_stake.go index 81d620da..24aff7a0 100644 --- a/x/poe/contract/tg4_stake.go +++ b/x/poe/contract/tg4_stake.go @@ -2,7 +2,6 @@ package contract import ( "encoding/json" - "testing" "time" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -28,10 +27,6 @@ type TG4StakeInitMsg struct { Preauths uint64 `json:"preauths,omitempty"` } -func (m TG4StakeInitMsg) Json(t *testing.T) string { - return asJson(t, m) -} - // TG4StakeExecute staking contract execute messages // See https://github.com/confio/tgrade-contracts/blob/v0.5.0-alpha/contracts/tg4-stake/src/msg.rs type TG4StakeExecute struct { diff --git a/x/poe/contract/tgrade_valset.go b/x/poe/contract/tgrade_valset.go index 055900f1..6d6202f5 100644 --- a/x/poe/contract/tgrade_valset.go +++ b/x/poe/contract/tgrade_valset.go @@ -179,7 +179,7 @@ type ValsetEpochResponse struct { // The last time we updated the validator set - block height LastUpdateHeight uint64 `json:"last_update_height"` // TODO: add this if you want it, not in current code - /// Seconds (UTC UNIX time) of next timestamp that will trigger a validator recalculation + // Seconds (UTC UNIX time) of next timestamp that will trigger a validator recalculation //NextUpdateTime int `json:"next_update_time"` } diff --git a/x/poe/wasm/query_plugin_test.go b/x/poe/wasm/query_plugin_test.go index 62a8b67b..4ad24e4a 100644 --- a/x/poe/wasm/query_plugin_test.go +++ b/x/poe/wasm/query_plugin_test.go @@ -1,9 +1,10 @@ package wasm import ( - "github.com/confio/tgrade/x/twasm/types" "testing" + "github.com/confio/tgrade/x/twasm/types" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" From 48be901ee17ececc5081a5bffc9bcebca5069d9d Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Thu, 4 Nov 2021 13:10:08 +0100 Subject: [PATCH 6/6] Review comments and better tests --- x/poe/contract/tg4_stake_integration_test.go | 60 +++++++++++++++++++ x/poe/keeper/contracts.go | 3 + x/poe/wasm/query_plugin.go | 29 ++++++---- x/poe/wasm/query_plugin_test.go | 61 ++++++++++++++------ 4 files changed, 123 insertions(+), 30 deletions(-) diff --git a/x/poe/contract/tg4_stake_integration_test.go b/x/poe/contract/tg4_stake_integration_test.go index 0785febe..c4a9e6f7 100644 --- a/x/poe/contract/tg4_stake_integration_test.go +++ b/x/poe/contract/tg4_stake_integration_test.go @@ -33,6 +33,66 @@ func TestQueryUnbondingPeriod(t *testing.T) { assert.Equal(t, configuredTime, res) } +func TestQueryStakedAmount(t *testing.T) { + // setup contracts and seed some data + ctx, example, _ := setupPoEContracts(t) + contractKeeper := example.TWasmKeeper.GetContractKeeper() + stakingContractAddr, err := example.PoEKeeper.GetPoEContractAddress(ctx, types.PoEContractTypeStaking) + require.NoError(t, err) + contractAdapter := contract.NewStakeContractAdapter(stakingContractAddr, example.TWasmKeeper, nil) + + // fund account + var myOperatorAddr sdk.AccAddress = rand.Bytes(sdk.AddrLen) + example.BankKeeper.SetBalances(ctx, myOperatorAddr, sdk.NewCoins(sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(100)))) + + var oneInt = sdk.OneInt() + specs := map[string]struct { + addr sdk.AccAddress + expAmount *sdk.Int + setup func(ctx sdk.Context) + expErr bool + }{ + "address has staked amount": { + addr: myOperatorAddr, + setup: func(ctx sdk.Context) { + err := contract.BondDelegation(ctx, stakingContractAddr, myOperatorAddr, sdk.NewCoins(sdk.NewCoin("utgd", sdk.OneInt())), contractKeeper) + require.NoError(t, err) + }, + expAmount: &oneInt, + }, + "address had formerly staked amount": { + addr: myOperatorAddr, + setup: func(ctx sdk.Context) { + err := contract.BondDelegation(ctx, stakingContractAddr, myOperatorAddr, sdk.NewCoins(sdk.NewCoin("utgd", sdk.OneInt())), contractKeeper) + require.NoError(t, err) + err = contract.UnbondDelegation(ctx, stakingContractAddr, myOperatorAddr, sdk.OneInt(), contractKeeper) + require.NoError(t, err) + }, + expAmount: nil, + }, + "unknown address": { + addr: rand.Bytes(sdk.AddrLen), + setup: func(ctx sdk.Context) {}, + expAmount: nil, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + tCtx, _ := ctx.CacheContext() + spec.setup(tCtx) + // when + gotAmount, gotErr := contractAdapter.QueryStakedAmount(tCtx, spec.addr) + // then + if spec.expErr { + require.Error(t, gotErr) + return + } + require.NoError(t, gotErr) + assert.Equal(t, spec.expAmount, gotAmount, "exp %s but got %s", spec.expAmount, gotAmount) + }) + } +} + func TestQueryValidatorUnboding(t *testing.T) { // setup contracts and seed some data ctx, example, vals := setupPoEContracts(t) diff --git a/x/poe/keeper/contracts.go b/x/poe/keeper/contracts.go index 9ce68119..8d54c10c 100644 --- a/x/poe/keeper/contracts.go +++ b/x/poe/keeper/contracts.go @@ -11,6 +11,7 @@ import ( ) type DistributionContract interface { + // ValidatorOutstandingReward returns amount or 0 for an unknown address ValidatorOutstandingReward(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) } @@ -31,8 +32,10 @@ func (k Keeper) ValsetContract(ctx sdk.Context) ValsetContract { } type StakeContract interface { + // QueryStakedAmount returns amount in default denom or nil value for an unknown address QueryStakedAmount(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) QueryStakingUnbondingPeriod(ctx sdk.Context) (time.Duration, error) + // QueryStakingUnbonding returns the unbondings or empty list for an unknown address QueryStakingUnbonding(ctx sdk.Context, opAddr sdk.AccAddress) ([]stakingtypes.UnbondingDelegationEntry, error) } diff --git a/x/poe/wasm/query_plugin.go b/x/poe/wasm/query_plugin.go index ba05b499..8eb1fc96 100644 --- a/x/poe/wasm/query_plugin.go +++ b/x/poe/wasm/query_plugin.go @@ -77,11 +77,11 @@ func StakingQuerier(poeKeeper ViewKeeper) func(ctx sdk.Context, request *wasmvmt } var res wasmvmtypes.AllDelegationsResponse if stakedAmount != nil { - res.Delegations = append(res.Delegations, wasmvmtypes.Delegation{ + res.Delegations = []wasmvmtypes.Delegation{{ Delegator: delegator.String(), Validator: delegator.String(), Amount: wasmvmtypes.NewCoin(stakedAmount.Uint64(), poeKeeper.GetBondDenom(ctx)), - }) + }} } return json.Marshal(res) } @@ -102,22 +102,27 @@ func StakingQuerier(poeKeeper ViewKeeper) func(ctx sdk.Context, request *wasmvmt stakeContract := poeKeeper.StakeContract(ctx) stakedAmount, err := stakeContract.QueryStakedAmount(ctx, delegator) if err != nil { - return nil, err + return nil, sdkerrors.Wrap(err, "query staked amount") } - if stakedAmount != nil { - stakedCoin := wasmvmtypes.NewCoin(stakedAmount.Uint64(), poeKeeper.GetBondDenom(ctx)) + reward, err := poeKeeper.DistributionContract(ctx).ValidatorOutstandingReward(ctx, delegator) + if err != nil { + return nil, sdkerrors.Wrap(err, "query outstanding reward") + } + if stakedAmount == nil { + zeroInt := sdk.ZeroInt() + stakedAmount = &zeroInt + } + // there can be unclaimed rewards while all stacked amounts were unbound + if stakedAmount.GT(sdk.ZeroInt()) || reward.Amount.GT(sdk.ZeroInt()) { + bondDenom := poeKeeper.GetBondDenom(ctx) + stakedCoin := wasmvmtypes.NewCoin(stakedAmount.Uint64(), bondDenom) res.Delegation = &wasmvmtypes.FullDelegation{ Delegator: delegator.String(), Validator: delegator.String(), Amount: stakedCoin, - CanRedelegate: stakedCoin, - AccumulatedRewards: nil, - } - reward, err := poeKeeper.DistributionContract(ctx).ValidatorOutstandingReward(ctx, delegator) - if err != nil { - return nil, err + CanRedelegate: wasmvmtypes.NewCoin(0, bondDenom), + AccumulatedRewards: wasmvmtypes.Coins{wasmvmtypes.NewCoin(reward.Amount.Uint64(), reward.Denom)}, } - res.Delegation.AccumulatedRewards = wasmvmtypes.Coins{wasmvmtypes.NewCoin(reward.Amount.Uint64(), reward.Denom)} } return json.Marshal(res) } diff --git a/x/poe/wasm/query_plugin_test.go b/x/poe/wasm/query_plugin_test.go index 4ad24e4a..4d6549c5 100644 --- a/x/poe/wasm/query_plugin_test.go +++ b/x/poe/wasm/query_plugin_test.go @@ -164,35 +164,43 @@ func TestStakingQuerier(t *testing.T) { ], "can_redelegate": { "denom": "alx", - "amount": "1" + "amount": "0" } } } `, }, "query delegation - address do not match - return empty result": { - src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", Validator: "cosmos17emnuddq662fpxpnd43ch0396452d48vc8ufsw"}}, + src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", Validator: "cosmos17emnuddq662fpxpnd43ch0396452d48vc8ufsw"}}, + expJson: `{}`, + }, + "query delegation - unknown address return empty result": { + src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", Validator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3"}}, mock: ViewKeeperMock{ StakeContractFn: func(ctx sdk.Context) keeper.StakeContract { return poetesting.StakeContractMock{ QueryStakedAmountFn: func(ctx sdk.Context, opAddr sdk.AccAddress) (*sdk.Int, error) { - myValue := sdk.OneInt() - return &myValue, nil + return nil, nil }, } }, - GetBondDenomFn: func(ctx sdk.Context) string { - return "alx" - }, DistributionContractFn: func(ctx sdk.Context) keeper.DistributionContract { return poetesting.DistributionContractMock{ValidatorOutstandingRewardFn: func(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { - return sdk.NewCoin("alx", sdk.NewInt(2)), nil + return sdk.NewCoin("alx", sdk.ZeroInt()), nil }} }, }, expJson: `{}`, }, - "query delegation - unknown address return empty result": { + "query delegation - invalid delegator address": { + src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "not a valid address", Validator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3"}}, + expErr: true, + }, + "query delegation - invalid validator address": { + src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", Validator: "not a valid address"}}, + expErr: true, + }, + "query delegation - no staking, pending rewards": { src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", Validator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3"}}, mock: ViewKeeperMock{ StakeContractFn: func(ctx sdk.Context) keeper.StakeContract { @@ -205,16 +213,33 @@ func TestStakingQuerier(t *testing.T) { GetBondDenomFn: func(ctx sdk.Context) string { return "alx" }, + DistributionContractFn: func(ctx sdk.Context) keeper.DistributionContract { + return poetesting.DistributionContractMock{ValidatorOutstandingRewardFn: func(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coin, error) { + return sdk.NewCoin("alx", sdk.NewInt(2)), nil + }} + }, }, - expJson: `{}`, - }, - "query delegation - invalid delegator address": { - src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "not a valid address", Validator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3"}}, - expErr: true, - }, - "query delegation - invalid validator address": { - src: wasmvmtypes.StakingQuery{Delegation: &wasmvmtypes.DelegationQuery{Delegator: "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", Validator: "not a valid address"}}, - expErr: true, + expJson: `{ + "delegation": { + "delegator": "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", + "validator": "cosmos1yq8zt83jznmp94jkj65yvfz9n52akmxt52ehm3", + "amount": { + "denom": "alx", + "amount": "0" + }, + "accumulated_rewards": [ + { + "denom": "alx", + "amount": "2" + } + ], + "can_redelegate": { + "denom": "alx", + "amount": "0" + } + } +} +`, }, "unknown query": { src: wasmvmtypes.StakingQuery{},