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

chore: shared staking queries #212

Merged
merged 27 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9846ea7
feat: pool commission & stake fraction
troykessler Dec 13, 2024
d68da89
chore: implemented effective stake
troykessler Dec 13, 2024
61e1114
chore: allow zero for max pool stake
troykessler Dec 13, 2024
ea3b51c
chore: added required stakers
troykessler Dec 13, 2024
aeeb146
chore: continued with tests
troykessler Dec 16, 2024
2487d71
test: fixed existing zero delegation tests
troykessler Dec 16, 2024
29ff7b0
test: added effective stake tests
troykessler Dec 16, 2024
36f8f16
test: fixed existing tests
troykessler Dec 16, 2024
dd4b962
chore: export effective stake method
troykessler Dec 17, 2024
28de85b
test: round robin with stake fractions and max voting power
troykessler Dec 17, 2024
cdb78fe
test: added more unit tests
troykessler Dec 18, 2024
2b06b70
Merge branch 'main' into troy/max-pool-stake
troykessler Dec 18, 2024
ef69783
chore: remove unused code
troykessler Dec 18, 2024
9037988
chore: add missing changes
troykessler Dec 18, 2024
373ec77
chore: reimplemented stakers query
troykessler Dec 18, 2024
48445cd
chore: added staker_by_pool query
troykessler Dec 18, 2024
405b806
chore: sorty by pool stake
troykessler Dec 18, 2024
513fadd
chore: added staker status
troykessler Dec 18, 2024
7a81510
fix: make all
troykessler Dec 18, 2024
08de954
chore: implemented setter method for max pool stake
troykessler Dec 20, 2024
117440d
chore: use new total stake method
troykessler Dec 20, 2024
2df7b52
Merge branch 'main' into troy/shared-staking-queries
troykessler Dec 20, 2024
0afe6e0
chore: sync merge
troykessler Dec 20, 2024
1e3c457
chore: added missing changes
troykessler Dec 20, 2024
87df706
chore: improved staker sorting
troykessler Dec 20, 2024
bbdd2f6
chore: added comment
troykessler Dec 20, 2024
68c40f4
chore: fixed tests
troykessler Dec 20, 2024
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
2,964 changes: 2,082 additions & 882 deletions docs/static/openapi.yml

Large diffs are not rendered by default.

117 changes: 55 additions & 62 deletions proto/kyve/query/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package kyve.query.v1beta1;

import "amino/amino.proto";
import "cosmos/base/v1beta1/coin.proto";
import "cosmos/staking/v1beta1/staking.proto";
import "gogoproto/gogo.proto";
import "kyve/pool/v1beta1/pool.proto";

Expand Down Expand Up @@ -57,8 +58,8 @@ message BasicPool {
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];

// total_delegation of the pool
uint64 total_delegation = 8;
// total_stake of the pool
uint64 total_stake = 8;

// status of the pool if pool is able
// to produce bundles, etc.
Expand All @@ -72,80 +73,38 @@ message FullStaker {
// address of the staker
string address = 1;

// metadata as logo, moniker, etc.
StakerMetadata metadata = 2;
cosmos.staking.v1beta1.Validator validator = 2;

// amount the staker has delegated to himself
uint64 self_delegation = 3;

// unbonding_amount is the amount the staker is currently unbonding
// from the self-delegation.
// This amount can be larger than `amount` when the staker
// got slashed during unbonding. However, at the end of
// the unbonding period this amount is double checked with the
// remaining amount.
uint64 self_delegation_unbonding = 4;

// total_delegation returns the sum of all $KYVE users
// have delegated to this staker
uint64 total_delegation = 5;

// delegator_count is the total number of individual
// delegator addresses for that user.
uint64 delegator_count = 6;
// total_pool_stake returns the amount the validator has in total
// staked in all his pools
uint64 total_pool_stake = 3;

// pools is a list of all pools the staker is currently
// participating, i.e. allowed to vote and upload data.
repeated PoolMembership pools = 7;
repeated PoolMembership pools = 4;
}

// StakerMetadata contains static information for a staker
message StakerMetadata {
// commission is the percentage of the rewards that will
// get transferred to the staker before the remaining
// rewards are split across all delegators
// CommissionChangeEntry shows when the old commission
// of a staker will change to the new commission
message CommissionChangeEntry {
// commission is the new commission that will
// become active once the change-time is over
string commission = 1 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];

// moniker is a human-readable name for displaying
// the staker in the UI
string moniker = 2;

// website is a https-link to the website of the staker
string website = 3;

// identity from keybase.io
string identity = 4;

// security_contact ...
string security_contact = 5;

// details ...
string details = 6;

// pending_commission_change shows if the staker plans
// to change its commission. Delegators will see a warning in
// the UI. A Commission change takes some time until
// the commission is applied. Users have time to redelegate
// if they not agree with the new commission.
CommissionChangeEntry pending_commission_change = 7;

// commission_rewards are the rewards through commission and storage cost
repeated cosmos.base.v1beta1.Coin commission_rewards = 8 [
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// creation_date is the UNIX-timestamp (in seconds)
// of when the entry was created.
int64 creation_date = 2;
}

// CommissionChangeEntry shows when the old commission
// of a staker will change to the new commission
message CommissionChangeEntry {
// commission is the new commission that will
// StakeFractionChangeEntry shows when the old stake fraction
// of a staker will change to the new stake fraction
message StakeFractionChangeEntry {
// stake_fraction is the new stake_fraction that will
// become active once the change-time is over
string commission = 1 [
string stake_fraction = 1 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
Expand Down Expand Up @@ -181,4 +140,38 @@ message PoolMembership {
// whether or not the valaccount needs additional funds to
// pay for gas fees
uint64 balance = 5;

// commission is the commission the validator has chosen for
// this specific pool
string commission = 6 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];

// pending_commission_change shows if the staker plans
// to change its commission. Delegators will see a warning in
// the UI. A Commission change takes some time until
// the commission is applied. Users have time to redelegate
// if they not agree with the new commission.
CommissionChangeEntry pending_commission_change = 7;

// stake fraction is a percentage the validator has chosen for
// this pool. It is the fraction of how much of his total stake
// the validator wants to stake in this specific pool
string stake_fraction = 8 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];

// pending_stake_fraction_change shows if the staker plans
// to change its stake fraction. Delegators will see a warning in
// the UI. A stake fraction change takes some time until
// the stake fraction is applied. Users have time to redelegate
// if they not agree with the new stake fraction.
StakeFractionChangeEntry pending_stake_fraction_change = 9;

// pool stake shows the actual amount the validator has staked
// in this pool. It can be lower than the specified stake fraction
// because of the max voting power limit
uint64 pool_stake = 10;
}
25 changes: 10 additions & 15 deletions proto/kyve/query/v1beta1/stakers.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import "cosmos/base/query/v1beta1/pagination.proto";
import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "kyve/query/v1beta1/query.proto";
import "kyve/stakers/v1beta1/stakers.proto";

option go_package = "github.com/KYVENetwork/chain/x/query/types";

Expand Down Expand Up @@ -56,10 +55,14 @@ enum StakerStatus {

// STAKER_STATUS_UNSPECIFIED ...
STAKER_STATUS_UNSPECIFIED = 0;
// STAKER_STATUS_ACTIVE ...
STAKER_STATUS_ACTIVE = 1;
// STAKER_STATUS_INACTIVE ...
STAKER_STATUS_INACTIVE = 2;
// STAKER_STATUS_PROTOCOL_ACTIVE ...
STAKER_STATUS_PROTOCOL_ACTIVE = 1;
// STAKER_STATUS_PROTOCOL_INACTIVE ...
STAKER_STATUS_PROTOCOL_INACTIVE = 2;
// STAKER_STATUS_CHAIN_ACTIVE ...
STAKER_STATUS_CHAIN_ACTIVE = 3;
// STAKER_STATUS_CHAIN_INACTIVE ...
STAKER_STATUS_CHAIN_INACTIVE = 4;
}

// QueryStakersResponse is the response type for the Query/Stakers RPC method.
Expand Down Expand Up @@ -99,19 +102,11 @@ message QueryStakersByPoolRequest {
// QueryStakersByPoolResponse is the response type for the Query/Staker RPC method.
message QueryStakersByPoolResponse {
// stakers ...
repeated StakerPoolResponse stakers = 1 [(gogoproto.nullable) = false];
}

// StakerPoolResponse ...
message StakerPoolResponse {
// staker ...
FullStaker staker = 1;
// valaccount ...
kyve.stakers.v1beta1.Valaccount valaccount = 2;
repeated FullStaker stakers = 1 [(gogoproto.nullable) = false];
}

// =========================
// stakers_by_pool/{pool_id}
// stakers_by_pool_count
// =========================

// QueryStakersByPoolCountRequest ...
Expand Down
16 changes: 8 additions & 8 deletions testutil/integration/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package integration

import (
"fmt"
"sort"
"time"

"github.com/KYVENetwork/chain/util"
Expand Down Expand Up @@ -123,19 +124,18 @@ func (suite *KeeperTestSuite) VerifyPoolQueries() {

// test stakers by pool
valaccounts := suite.App().StakersKeeper.GetAllValaccountsOfPool(suite.Ctx(), poolsState[i].Id)
stakersByPoolState := make([]querytypes.StakerPoolResponse, 0)
stakersByPoolState := make([]querytypes.FullStaker, 0)

for _, valaccount := range valaccounts {
_, stakerFound := suite.App().StakersKeeper.GetValidator(suite.Ctx(), valaccount.Staker)

if stakerFound {
stakersByPoolState = append(stakersByPoolState, querytypes.StakerPoolResponse{
Staker: suite.App().QueryKeeper.GetFullStaker(suite.Ctx(), valaccount.Staker),
Valaccount: valaccount,
})
if _, stakerFound := suite.App().StakersKeeper.GetValidator(suite.Ctx(), valaccount.Staker); stakerFound {
stakersByPoolState = append(stakersByPoolState, *suite.App().QueryKeeper.GetFullStaker(suite.Ctx(), valaccount.Staker))
}
}

sort.SliceStable(stakersByPoolState, func(a, b int) bool {
return suite.App().StakersKeeper.GetValidatorPoolStake(suite.Ctx(), stakersByPoolState[a].Address, poolsState[i].Id) > suite.App().StakersKeeper.GetValidatorPoolStake(suite.Ctx(), stakersByPoolState[b].Address, poolsState[i].Id)
})

stakersByPoolQuery, stakersByPoolQueryErr := suite.App().QueryKeeper.StakersByPool(suite.Ctx(), &querytypes.QueryStakersByPoolRequest{
PoolId: poolsState[i].Id,
})
Expand Down
6 changes: 6 additions & 0 deletions testutil/integration/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,9 @@ func (suite *KeeperTestSuite) GetNextUploader() (nextStaker string, nextValaddre

return
}

func (suite *KeeperTestSuite) SetMaxVotingPower(maxVotingPower string) {
params := suite.App().PoolKeeper.GetParams(suite.Ctx())
params.MaxVotingPowerPerPool = math.LegacyMustNewDecFromStr(maxVotingPower)
suite.App().PoolKeeper.SetParams(suite.Ctx(), params)
}
4 changes: 1 addition & 3 deletions x/bundles/keeper/keeper_suite_invalid_bundles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,7 @@ var _ = Describe("invalid bundles", Ordered, func() {
}
s.RunTxPoolSuccess(msg)

params := s.App().PoolKeeper.GetParams(s.Ctx())
params.MaxVotingPowerPerPool = math.LegacyMustNewDecFromStr("1")
s.App().PoolKeeper.SetParams(s.Ctx(), params)
s.SetMaxVotingPower("1")

s.RunTxFundersSuccess(&funderstypes.MsgCreateFunder{
Creator: i.ALICE,
Expand Down
4 changes: 1 addition & 3 deletions x/bundles/keeper/keeper_suite_valid_bundles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -966,9 +966,7 @@ var _ = Describe("valid bundles", Ordered, func() {

It("Produce a valid bundle with multiple validators and foreign delegation although some voted invalid with maximum voting power", func() {
// ARRANGE
params := s.App().PoolKeeper.GetParams(s.Ctx())
params.MaxVotingPowerPerPool = math.LegacyMustNewDecFromStr("0.4")
s.App().PoolKeeper.SetParams(s.Ctx(), params)
s.SetMaxVotingPower("0.4")

s.CreateValidator(i.STAKER_2, "Staker-2", int64(100*i.KYVE))

Expand Down
8 changes: 2 additions & 6 deletions x/bundles/keeper/logic_bundles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,7 @@ var _ = Describe("logic_bundles.go", Ordered, func() {

It("Assert pool can run while voting power of one node exceeds 40%", func() {
// ARRANGE
params := s.App().PoolKeeper.GetParams(s.Ctx())
params.MaxVotingPowerPerPool = math.LegacyMustNewDecFromStr("0.4")
s.App().PoolKeeper.SetParams(s.Ctx(), params)
s.SetMaxVotingPower("0.4")

msg := &pooltypes.MsgCreatePool{
Authority: gov,
Expand Down Expand Up @@ -371,9 +369,7 @@ var _ = Describe("logic_bundles.go", Ordered, func() {

It("Assert pool can run with a single staker while voting power is 100%", func() {
// ARRANGE
params := s.App().PoolKeeper.GetParams(s.Ctx())
params.MaxVotingPowerPerPool = math.LegacyMustNewDecFromStr("1")
s.App().PoolKeeper.SetParams(s.Ctx(), params)
s.SetMaxVotingPower("1")

msg := &pooltypes.MsgCreatePool{
Authority: gov,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,7 @@ var _ = Describe("logic_end_block_handle_upload_timeout.go", Ordered, func() {
PoolId: 0,
})

params := s.App().PoolKeeper.GetParams(s.Ctx())
params.MaxVotingPowerPerPool = math.LegacyMustNewDecFromStr("0.2")
s.App().PoolKeeper.SetParams(s.Ctx(), params)
s.SetMaxVotingPower("0.2")

// ACT
s.CommitAfterSeconds(1)
Expand Down
8 changes: 2 additions & 6 deletions x/bundles/keeper/logic_round_robin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,7 @@ var _ = Describe("logic_round_robin.go", Ordered, func() {
}
s.App().PoolKeeper.SetPool(s.Ctx(), pool)

params := s.App().PoolKeeper.GetParams(s.Ctx())
params.MaxVotingPowerPerPool = math.LegacyMustNewDecFromStr("1")
s.App().PoolKeeper.SetParams(s.Ctx(), params)
s.SetMaxVotingPower("1")
})

AfterEach(func() {
Expand Down Expand Up @@ -269,9 +267,7 @@ var _ = Describe("logic_round_robin.go", Ordered, func() {

It("Frequency analysis with maximum voting power cap", func() {
// ARRANGE
params := s.App().PoolKeeper.GetParams(s.Ctx())
params.MaxVotingPowerPerPool = math.LegacyMustNewDecFromStr("0.5")
s.App().PoolKeeper.SetParams(s.Ctx(), params)
s.SetMaxVotingPower("0.5")

// NOTE that dummy with index 2 has more than 50% voting power, so his effective stake
// will be lower
Expand Down
2 changes: 1 addition & 1 deletion x/query/keeper/grpc_account_funded.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (k Keeper) AccountFundedList(goCtx context.Context, req *types.QueryAccount
InflationShareWeight: pool.InflationShareWeight,
UploadInterval: pool.UploadInterval,
TotalFunds: k.fundersKeeper.GetTotalActiveFunding(ctx, pool.Id),
TotalDelegation: k.delegationKeeper.GetDelegationOfPool(ctx, pool.Id),
TotalStake: k.stakerKeeper.GetTotalStakeOfPool(ctx, pool.Id),
Status: k.GetPoolStatus(ctx, &pool),
},
})
Expand Down
4 changes: 2 additions & 2 deletions x/query/keeper/grpc_query_staker.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (k Keeper) Stakers(c context.Context, req *types.QueryStakersRequest) (*typ
fullStaker := k.GetFullStaker(ctx, address)

searchAddress := strings.ToLower(fullStaker.Address)
searchMoniker := strings.ToLower(fullStaker.Metadata.Moniker)
searchMoniker := strings.ToLower(fullStaker.Validator.GetMoniker())

if strings.Contains(searchAddress, req.Search) || strings.Contains(searchMoniker, req.Search) {
if accumulate {
Expand All @@ -41,7 +41,7 @@ func (k Keeper) Stakers(c context.Context, req *types.QueryStakersRequest) (*typ
var pageRes *query.PageResponse
var err error

pageRes, err = k.stakerKeeper.GetPaginatedStakersByDelegation(ctx, req.Pagination, accumulator)
pageRes, err = k.stakerKeeper.GetPaginatedStakersByPoolStake(ctx, req.Pagination, req.Status, accumulator)
troykessler marked this conversation as resolved.
Show resolved Hide resolved

if err != nil {
return nil, err
Expand Down
23 changes: 12 additions & 11 deletions x/query/keeper/grpc_query_stakers_by_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"context"
"sort"

"github.com/KYVENetwork/chain/x/query/types"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -15,24 +16,24 @@ func (k Keeper) StakersByPool(c context.Context, req *types.QueryStakersByPoolRe
return nil, status.Error(codes.InvalidArgument, "invalid request")
}

data := make([]types.StakerPoolResponse, 0)

ctx := sdk.UnwrapSDKContext(c)

_, found := k.poolKeeper.GetPool(ctx, req.PoolId)
if !found {
if _, found := k.poolKeeper.GetPool(ctx, req.PoolId); !found {
return nil, sdkerrors.ErrKeyNotFound
}

stakers := make([]types.FullStaker, 0)

valaccounts := k.stakerKeeper.GetAllValaccountsOfPool(ctx, req.PoolId)
for _, valaccount := range valaccounts {
if _, exist := k.stakerKeeper.GetValidator(ctx, valaccount.Staker); exist {
data = append(data, types.StakerPoolResponse{
Staker: k.GetFullStaker(ctx, valaccount.Staker),
Valaccount: valaccount,
})
}
stakers = append(stakers, *k.GetFullStaker(ctx, valaccount.Staker))
}

return &types.QueryStakersByPoolResponse{Stakers: data}, nil
stakes := k.stakerKeeper.GetValidatorPoolStakes(ctx, req.PoolId)

sort.SliceStable(stakers, func(i, j int) bool {
return stakes[stakers[i].Address] > stakes[stakers[j].Address]
})

return &types.QueryStakersByPoolResponse{Stakers: stakers}, nil
}
Loading
Loading