Open iicc1 opened 2 years ago
Hello, any idea? Thanks
Any help would be appreciated
It seems like you are not using TypeScript, which makes your life very hard you don't get errors when inserting the wrong type.
validatorDelegations takes an optional pagination key argument of type Uint8Array | undefined
. You can leave it undefined for the first query. Then set value to the pagination.nextKey
from the response.
It seems like you are not using TypeScript, which makes your life very hard you don't get errors when inserting the wrong type.
validatorDelegations takes an optional pagination key argument of type
Uint8Array | undefined
. You can leave it undefined for the first query. Then set value to thepagination.nextKey
from the response.
Thanks for your reply. The problem is that if you set an undefined or an invalid pagination key argument, it requests the full delegators list from the RPC. Since that query is too intensive, it results in a timeout error. We would need to be able to set a pagination limit in the first request, like this LCD query does: https://cosmoshub-lcd.stakely.io/cosmos/staking/v1beta1/validators/cosmosvaloper16yupepagywvlk7uhpfchtwa0stu5f8cyhh54f2/delegations?pagination.limit=50
Ah, so that means that the default limit in the backend is too large or even unlimited? If that's the case, this is a Cosmos SDK bug. The default limit should be query specific but reasonable.
The high level query client corrently does not allow changing the limit as we did not have this issue before.
Correct, the default limit is unlimited thus for validators with over 1000 delegators it is almost impossible to request this information via RPC
This is strange. There is this code in Cosmos SDK v0.45.6:
// DefaultLimit is the default `limit` for queries
// if the `limit` is not supplied, paginate will use `DefaultLimit`
const DefaultLimit = 100
and
func FilteredPaginate(
prefixStore types.KVStore,
pageRequest *PageRequest,
onResult func(key []byte, value []byte, accumulate bool) (bool, error),
) (*PageResponse, error) {
// if the PageRequest is nil, use default PageRequest
if pageRequest == nil {
pageRequest = &PageRequest{}
}
offset := pageRequest.Offset
key := pageRequest.Key
limit := pageRequest.Limit
countTotal := pageRequest.CountTotal
reverse := pageRequest.Reverse
if offset > 0 && key != nil {
return nil, fmt.Errorf("invalid request, either offset or key is expected, got both")
}
if limit == 0 {
limit = DefaultLimit
// count total results when the limit is zero/not supplied
countTotal = true
}
which is used in
// ValidatorDelegations queries delegate info for given validator
func (k Querier) ValidatorDelegations(c context.Context, req *types.QueryValidatorDelegationsRequest) (*types.QueryValidatorDelegationsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if req.ValidatorAddr == "" {
return nil, status.Error(codes.InvalidArgument, "validator address cannot be empty")
}
var delegations []types.Delegation
ctx := sdk.UnwrapSDKContext(c)
store := ctx.KVStore(k.storeKey)
valStore := prefix.NewStore(store, types.DelegationKey)
pageRes, err := query.FilteredPaginate(valStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) {
delegation, err := types.UnmarshalDelegation(k.cdc, value)
if err != nil {
return false, err
}
valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr)
if err != nil {
return false, err
}
if !delegation.GetValidatorAddr().Equals(valAddr) {
return false, nil
}
if accumulate {
delegations = append(delegations, delegation)
}
return true, nil
})
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
delResponses, err := DelegationsToDelegationResponses(ctx, k.Keeper, delegations)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
return &types.QueryValidatorDelegationsResponse{
DelegationResponses: delResponses, Pagination: pageRes}, nil
}
But maybe thos 100 is even too much because the query implementation is inefficient?
Can you test this on your own node?
See also https://github.com/cosmos/cosmjs/issues/1209 and https://github.com/cosmos/cosmos-sdk/issues/12756
I have tried the query against one of our nodes that is deployed on a really powerful dedicated server and it times out.
To provide some clarity, setting a client-side limit won't save us here because the query loops over ALL delegations on the chain and then selects the ones whose validatorAddr
match the provided validatorAddr
in the query. It will eventually lead to an I/O timeout if you have many delegations.
The fix here would be to add a validator + delegation index on-chain (cosmos-sdk state breaking change) or to index the delegations off-chain in a centralized database somewhere and serve them to your web app via traditional web2 means (e.g., SQL database + REST API).
Since fetching all the validator delegations is a very intensive method, trying to get all values directly usually results in an error, as already noted here https://github.com/cosmos/cosmjs/issues/1049.
I'm trying to get a paginated result with Stargate, StakingExtension, and the method validatorDelegations but I don't know what should I pass as the paginationKey parameter.
According to the docs, paginationKey should be an Uint8Array, but what should I set as my first paginationKey, when I have none? I have been trying by setting paginationKey as an object with pagination information, null, '', etc. without result. All the time it tries to get all the results directly which means that the pagination is not working and the node is not able to answer this request.
This is my code:
This would be the request using the LCD, which works fine: https://cosmoshub-lcd.stakely.io/cosmos/staking/v1beta1/validators/cosmosvaloper16yupepagywvlk7uhpfchtwa0stu5f8cyhh54f2/delegations?pagination.limit=50
Thanks in advance.