mirror of https://github.com/certusone/wasmd.git
Merge pull request #278 from CosmWasm/fix-staking-queries
Better calculation of Delegation data
This commit is contained in:
commit
3571625d9a
|
@ -299,7 +299,10 @@ func NewWasmApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b
|
|||
// The last arguments can contain custom message handlers, and custom query handlers,
|
||||
// if we want to allow any custom callbacks
|
||||
supportedFeatures := "staking"
|
||||
app.wasmKeeper = wasm.NewKeeper(app.cdc, keys[wasm.StoreKey], app.subspaces[wasm.ModuleName], app.accountKeeper, app.bankKeeper, app.stakingKeeper, wasmRouter, wasmDir, wasmConfig, supportedFeatures, nil, nil)
|
||||
app.wasmKeeper = wasm.NewKeeper(
|
||||
app.cdc, keys[wasm.StoreKey], app.subspaces[wasm.ModuleName], app.accountKeeper,
|
||||
app.bankKeeper, app.stakingKeeper, app.distrKeeper, wasmRouter, wasmDir, wasmConfig,
|
||||
supportedFeatures, nil, nil)
|
||||
|
||||
// The gov proposal types can be individually enabled
|
||||
if len(enabledProposals) != 0 {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
@ -496,7 +497,7 @@ func setupKeeper(t *testing.T) (Keeper, sdk.Context, []sdk.StoreKey, func()) {
|
|||
cdc := MakeTestCodec()
|
||||
pk := params.NewKeeper(cdc, keyParams, tkeyParams)
|
||||
wasmConfig := wasmTypes.DefaultWasmConfig()
|
||||
srcKeeper := NewKeeper(cdc, keyWasm, pk.Subspace(wasmTypes.DefaultParamspace), auth.AccountKeeper{}, nil, staking.Keeper{}, nil, tempDir, wasmConfig, "", nil, nil)
|
||||
srcKeeper := NewKeeper(cdc, keyWasm, pk.Subspace(wasmTypes.DefaultParamspace), auth.AccountKeeper{}, nil, staking.Keeper{}, distribution.Keeper{}, nil, tempDir, wasmConfig, "", nil, nil)
|
||||
srcKeeper.setParams(ctx, wasmTypes.DefaultParams())
|
||||
|
||||
return srcKeeper, ctx, []sdk.StoreKey{keyWasm, keyParams}, cleanup
|
||||
|
|
|
@ -3,6 +3,7 @@ package keeper
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/params/subspace"
|
||||
|
@ -61,7 +62,7 @@ type Keeper struct {
|
|||
// NewKeeper creates a new contract Keeper instance
|
||||
// If customEncoders is non-nil, we can use this to override some of the message handler, especially custom
|
||||
func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace params.Subspace, accountKeeper auth.AccountKeeper, bankKeeper bank.Keeper,
|
||||
stakingKeeper staking.Keeper,
|
||||
stakingKeeper staking.Keeper, distKeeper distribution.Keeper,
|
||||
router sdk.Router, homeDir string, wasmConfig types.WasmConfig, supportedFeatures string, customEncoders *MessageEncoders, customPlugins *QueryPlugins) Keeper {
|
||||
wasmer, err := wasm.NewWasmer(filepath.Join(homeDir, "wasm"), supportedFeatures)
|
||||
if err != nil {
|
||||
|
@ -84,7 +85,7 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace params.Subspa
|
|||
authZPolicy: DefaultAuthorizationPolicy{},
|
||||
paramSpace: paramSpace,
|
||||
}
|
||||
keeper.queryPlugins = DefaultQueryPlugins(bankKeeper, stakingKeeper, &keeper).Merge(customPlugins)
|
||||
keeper.queryPlugins = DefaultQueryPlugins(bankKeeper, stakingKeeper, distKeeper, &keeper).Merge(customPlugins)
|
||||
return keeper
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,9 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
type QueryHandler struct {
|
||||
|
@ -55,11 +57,11 @@ type QueryPlugins struct {
|
|||
Wasm func(ctx sdk.Context, request *wasmTypes.WasmQuery) ([]byte, error)
|
||||
}
|
||||
|
||||
func DefaultQueryPlugins(bank bank.ViewKeeper, staking staking.Keeper, wasm *Keeper) QueryPlugins {
|
||||
func DefaultQueryPlugins(bank bank.ViewKeeper, staking staking.Keeper, distKeeper distribution.Keeper, wasm *Keeper) QueryPlugins {
|
||||
return QueryPlugins{
|
||||
Bank: BankQuerier(bank),
|
||||
Custom: NoCustomQuerier,
|
||||
Staking: StakingQuerier(staking),
|
||||
Staking: StakingQuerier(staking, distKeeper),
|
||||
Wasm: WasmQuerier(wasm),
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +122,7 @@ func NoCustomQuerier(sdk.Context, json.RawMessage) ([]byte, error) {
|
|||
return nil, wasmTypes.UnsupportedRequest{Kind: "custom"}
|
||||
}
|
||||
|
||||
func StakingQuerier(keeper staking.Keeper) func(ctx sdk.Context, request *wasmTypes.StakingQuery) ([]byte, error) {
|
||||
func StakingQuerier(keeper staking.Keeper, distKeeper distribution.Keeper) func(ctx sdk.Context, request *wasmTypes.StakingQuery) ([]byte, error) {
|
||||
return func(ctx sdk.Context, request *wasmTypes.StakingQuery) ([]byte, error) {
|
||||
if request.BondedDenom != nil {
|
||||
denom := keeper.BondDenom(ctx)
|
||||
|
@ -174,7 +176,7 @@ func StakingQuerier(keeper staking.Keeper) func(ctx sdk.Context, request *wasmTy
|
|||
var res wasmTypes.DelegationResponse
|
||||
d, found := keeper.GetDelegation(ctx, delegator, validator)
|
||||
if found {
|
||||
res.Delegation, err = sdkToFullDelegation(ctx, keeper, d)
|
||||
res.Delegation, err = sdkToFullDelegation(ctx, keeper, distKeeper, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -198,11 +200,6 @@ func sdkToDelegations(ctx sdk.Context, keeper staking.Keeper, delegations []stak
|
|||
}
|
||||
amount := sdk.NewCoin(bondDenom, val.TokensFromShares(d.Shares).TruncateInt())
|
||||
|
||||
// Accumulated Rewards???
|
||||
|
||||
// can relegate? other query for redelegations?
|
||||
// keeper.GetRedelegation
|
||||
|
||||
result[i] = wasmTypes.Delegation{
|
||||
Delegator: d.DelegatorAddress.String(),
|
||||
Validator: d.ValidatorAddress.String(),
|
||||
|
@ -212,7 +209,7 @@ func sdkToDelegations(ctx sdk.Context, keeper staking.Keeper, delegations []stak
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func sdkToFullDelegation(ctx sdk.Context, keeper staking.Keeper, delegation staking.Delegation) (*wasmTypes.FullDelegation, error) {
|
||||
func sdkToFullDelegation(ctx sdk.Context, keeper staking.Keeper, distKeeper distribution.Keeper, delegation staking.Delegation) (*wasmTypes.FullDelegation, error) {
|
||||
val, found := keeper.GetValidator(ctx, delegation.ValidatorAddress)
|
||||
if !found {
|
||||
return nil, sdkerrors.Wrap(staking.ErrNoValidatorFound, "can't load validator for delegation")
|
||||
|
@ -220,20 +217,75 @@ func sdkToFullDelegation(ctx sdk.Context, keeper staking.Keeper, delegation stak
|
|||
bondDenom := keeper.BondDenom(ctx)
|
||||
amount := sdk.NewCoin(bondDenom, val.TokensFromShares(delegation.Shares).TruncateInt())
|
||||
|
||||
// can relegate? other query for redelegations?
|
||||
// keeper.GetRedelegation
|
||||
delegationCoins := convertSdkCoinToWasmCoin(amount)
|
||||
|
||||
// FIXME: this is very rough but better than nothing...
|
||||
// https://github.com/CosmWasm/wasmd/issues/282
|
||||
// if this (val, delegate) pair is receiving a redelegation, it cannot redelegate more
|
||||
// otherwise, it can redelegate the full amount
|
||||
// (there are cases of partial funds redelegated, but this is a start)
|
||||
redelegateCoins := wasmTypes.NewCoin(0, bondDenom)
|
||||
if !keeper.HasReceivingRedelegation(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) {
|
||||
redelegateCoins = delegationCoins
|
||||
}
|
||||
|
||||
// FIXME: make a cleaner way to do this (modify the sdk)
|
||||
// we need the info from `distKeeper.calculateDelegationRewards()`, but it is not public
|
||||
// neither is `queryDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper)`
|
||||
// so we go through the front door of the querier....
|
||||
accRewards, err := getAccumulatedRewards(ctx, distKeeper, delegation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &wasmTypes.FullDelegation{
|
||||
Delegator: delegation.DelegatorAddress.String(),
|
||||
Validator: delegation.ValidatorAddress.String(),
|
||||
Amount: convertSdkCoinToWasmCoin(amount),
|
||||
// TODO: AccumulatedRewards
|
||||
AccumulatedRewards: wasmTypes.Coins{},
|
||||
// TODO: Determine redelegate
|
||||
CanRedelegate: wasmTypes.NewCoin(0, bondDenom),
|
||||
Delegator: delegation.DelegatorAddress.String(),
|
||||
Validator: delegation.ValidatorAddress.String(),
|
||||
Amount: delegationCoins,
|
||||
AccumulatedRewards: accRewards,
|
||||
CanRedelegate: redelegateCoins,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FIXME: simplify this enormously when
|
||||
// https://github.com/cosmos/cosmos-sdk/issues/7466 is merged
|
||||
func getAccumulatedRewards(ctx sdk.Context, distKeeper distribution.Keeper, delegation staking.Delegation) ([]wasmTypes.Coin, error) {
|
||||
// Try to get *delegator* reward info!
|
||||
params := distribution.QueryDelegationRewardsParams{
|
||||
DelegatorAddress: delegation.DelegatorAddress,
|
||||
ValidatorAddress: delegation.ValidatorAddress,
|
||||
}
|
||||
data, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req := abci.RequestQuery{Data: data}
|
||||
|
||||
// just to be safe... ensure we do not accidentally write in the querier (which does some funky things)
|
||||
cache, _ := ctx.CacheContext()
|
||||
qres, err := distribution.NewQuerier(distKeeper)(cache, []string{distribution.QueryDelegationRewards}, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var decRewards sdk.DecCoins
|
||||
err = json.Unmarshal(qres, &decRewards)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// **** all this above should be ONE method call
|
||||
|
||||
// now we have it, convert it into wasmTypes
|
||||
rewards := make([]wasmTypes.Coin, len(decRewards))
|
||||
for i, r := range decRewards {
|
||||
rewards[i] = wasmTypes.Coin{
|
||||
Denom: r.Denom,
|
||||
Amount: r.Amount.TruncateInt().String(),
|
||||
}
|
||||
}
|
||||
return rewards, nil
|
||||
}
|
||||
|
||||
func WasmQuerier(wasm *Keeper) func(ctx sdk.Context, request *wasmTypes.WasmQuery) ([]byte, error) {
|
||||
return func(ctx sdk.Context, request *wasmTypes.WasmQuery) ([]byte, error) {
|
||||
if request.Smart != nil {
|
||||
|
|
|
@ -369,7 +369,7 @@ func TestReinvest(t *testing.T) {
|
|||
// we get 1/6, our share should be 40k minus 10% commission = 36k
|
||||
setValidatorRewards(ctx, stakingKeeper, distKeeper, valAddr, "240000")
|
||||
|
||||
// this should withdraw our outstanding 40k of rewards and reinvest them in the same delegation
|
||||
// this should withdraw our outstanding 36k of rewards and reinvest them in the same delegation
|
||||
reinvest := StakingHandleMsg{
|
||||
Reinvest: &struct{}{},
|
||||
}
|
||||
|
@ -438,6 +438,9 @@ func TestQueryStakingInfo(t *testing.T) {
|
|||
// we get 1/6, our share should be 40k minus 10% commission = 36k
|
||||
setValidatorRewards(ctx, stakingKeeper, distKeeper, valAddr, "240000")
|
||||
|
||||
// see what the current rewards are
|
||||
origReward := distKeeper.GetValidatorCurrentRewards(ctx, valAddr)
|
||||
|
||||
// STEP 2: Prepare the mask contract
|
||||
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
|
||||
creator := createFakeFundedAccount(ctx, accKeeper, deposit)
|
||||
|
@ -534,9 +537,82 @@ func TestQueryStakingInfo(t *testing.T) {
|
|||
// 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"))
|
||||
|
||||
require.Equal(t, wasmTypes.NewCoin(200000, "stake"), delInfo2.CanRedelegate)
|
||||
require.Len(t, delInfo2.AccumulatedRewards, 1)
|
||||
// see bonding above to see how we calculate 36000 (240000 / 6 - 10% commission)
|
||||
require.Equal(t, wasmTypes.NewCoin(36000, "stake"), delInfo2.AccumulatedRewards[0])
|
||||
|
||||
// ensure rewards did not change when querying (neither amount nor period)
|
||||
finalReward := distKeeper.GetValidatorCurrentRewards(ctx, valAddr)
|
||||
require.Equal(t, origReward, finalReward)
|
||||
}
|
||||
|
||||
func TestQueryStakingPlugin(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")
|
||||
|
||||
// see what the current rewards are
|
||||
origReward := distKeeper.GetValidatorCurrentRewards(ctx, valAddr)
|
||||
|
||||
// Step 2: Try out the query plugins
|
||||
query := wasmTypes.StakingQuery{
|
||||
Delegation: &wasmTypes.DelegationQuery{
|
||||
Delegator: contractAddr.String(),
|
||||
Validator: valAddr.String(),
|
||||
},
|
||||
}
|
||||
raw, err := StakingQuerier(stakingKeeper, distKeeper)(ctx, &query)
|
||||
require.NoError(t, err)
|
||||
var res wasmTypes.DelegationResponse
|
||||
mustParse(t, raw, &res)
|
||||
assert.NotEmpty(t, res.Delegation)
|
||||
delInfo := res.Delegation
|
||||
// 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)
|
||||
|
||||
require.Equal(t, wasmTypes.NewCoin(200000, "stake"), delInfo.CanRedelegate)
|
||||
require.Len(t, delInfo.AccumulatedRewards, 1)
|
||||
// see bonding above to see how we calculate 36000 (240000 / 6 - 10% commission)
|
||||
require.Equal(t, wasmTypes.NewCoin(36000, "stake"), delInfo.AccumulatedRewards[0])
|
||||
|
||||
// ensure rewards did not change when querying (neither amount nor period)
|
||||
finalReward := distKeeper.GetValidatorCurrentRewards(ctx, valAddr)
|
||||
require.Equal(t, origReward, finalReward)
|
||||
}
|
||||
|
||||
// adds a few validators and returns a list of validators that are registered
|
||||
|
|
|
@ -171,7 +171,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, tempDir string, supportedFeat
|
|||
// Load default wasm config
|
||||
wasmConfig := wasmtypes.DefaultWasmConfig()
|
||||
keeper := NewKeeper(cdc, keyContract, paramsKeeper.Subspace(wasmtypes.DefaultParamspace),
|
||||
accountKeeper, bankKeeper, stakingKeeper, router, tempDir, wasmConfig,
|
||||
accountKeeper, bankKeeper, stakingKeeper, distKeeper, router, tempDir, wasmConfig,
|
||||
supportedFeatures, encoders, queriers,
|
||||
)
|
||||
keeper.setParams(ctx, wasmtypes.DefaultParams())
|
||||
|
|
Loading…
Reference in New Issue