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

Andrew7234/validator staking api #732

Merged
merged 1 commit into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .changelog/732.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
api: add validator staking history
110 changes: 101 additions & 9 deletions api/spec/v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ x-examples:
parameters-change:
- &parameters_change_1 '{"min_validators":null,"max_validators":"120","voting_power_distribution":null}'
epoch:
- &epoch_1 8048956
- &epoch_2 8048966
- &epoch_1 13402
- &epoch_2 13403
event-type:
- &event_type_1 'staking.escrow.take'
roothash-message-type:
Expand Down Expand Up @@ -508,7 +508,7 @@ paths:
required: true
schema:
allOf: [$ref: '#/components/schemas/StakingAddress']
description: The entity ID of the entity to return.
description: The address of the entity to return.
responses:
'200':
description: |
Expand All @@ -520,6 +520,43 @@ paths:
$ref: '#/components/schemas/Validator'
<<: *common_error_responses

/consensus/validators/{address}/history:
get:
summary: Returns historical information for a single validator.
parameters:
- *limit
- *offset
- in: query
name: from
schema:
type: integer
format: int64
description: A filter on minimum epoch number, inclusive.
example: *epoch_1
- in: query
name: to
schema:
type: integer
format: int64
description: A filter on maximum epoch number, inclusive.
example: *epoch_2
- in: path
name: address
required: true
schema:
allOf: [$ref: '#/components/schemas/StakingAddress']
description: The address of the entity to return.
responses:
'200':
description: |
A JSON object containing historical information for a
validator, grouped by epoch in reverse chronological order.
content:
application/json:
schema:
$ref: '#/components/schemas/ValidatorHistory'
<<: *common_error_responses

/consensus/accounts:
get:
summary: |
Expand Down Expand Up @@ -1866,29 +1903,29 @@ components:
properties:
entity_address:
type: string
description: The staking address identifying this Validator.
description: The staking address identifying this validator.
example: *staking_address_1
entity_id:
x-go-name: EntityID
type: string
description: The public key identifying this Validator.
description: The public key identifying this validator.
example: *entity_id_1
node_id:
x-go-name: NodeID
type: string
description: The public key identifying this Validator's node.
description: The public key identifying this validator's node.
example: *node_id_1
escrow:
allOf: [$ref: '#/components/schemas/Escrow']
description: The escrow account data for this validator.
voting_power:
type: integer
format: int64
description: The voting power of this Validator.
description: The voting power of this validator.
voting_power_total:
type: integer
format: int64
description: The total voting power across all Validators.
description: The total voting power across all validators.
active:
type: boolean
description: Whether the entity has a node that is registered for being a validator, node is up to date, and has successfully registered itself. It may or may not be part of validator set.
Expand All @@ -1900,7 +1937,7 @@ components:
rank:
type: integer
format: uint64
description: The rank of the Validator, determined by voting power.
description: The rank of the validator, determined by voting power.
in_validator_set:
type: boolean
description: Whether the entity is part of the validator set (top <scheduler.params.max_validators> by stake among active entities).
Expand Down Expand Up @@ -1937,6 +1974,61 @@ components:
self_delegation_shares:
allOf: [$ref: '#/components/schemas/TextBigInt']
description: The shares of tokens this validator has delegated to itself, and are NOT in the process of debonding.
active_balance_24:
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Note: This attempts to find the balance 24 epochs ago, which isn't exactly 24 hours but it's accurate enough. Also open to field name suggestions here.

allOf: [$ref: '#/components/schemas/TextBigInt']
description: The active_balance of this validator account 24 hours ago.
num_delegators:
type: integer
format: uint64
description: The number of accounts that have delegated token to this account.

ValidatorHistory:
allOf:
- $ref: '#/components/schemas/List'
- type: object
required: [history]
properties:
address:
type: string
description: The staking address of the validator.
example: *staking_address_1
history:
type: array
items:
allOf: [$ref: '#/components/schemas/ValidatorHistoryPoint']
description: Historical escrow balance data for a single address.

ValidatorHistoryPoint:
type: object
required: [epoch]
properties:
epoch:
type: integer
format: int64
description: The epoch number.
example: *epoch_1
active_balance:
allOf: [$ref: '#/components/schemas/TextBigInt']
description: |
The amount of tokens that were delegated to this validator account,
at the start of this epoch, and are NOT in the process of debonding.
active_shares:
allOf: [$ref: '#/components/schemas/TextBigInt']
description: |
The shares of tokens that were delegated to this validator account,
at the start of this epoch, and are NOT in the process of debonding.
debonding_balance:
allOf: [$ref: '#/components/schemas/TextBigInt']
description: |
The amount of tokens that were delegated to this validator account
at the start of this epoch, but are also in the process of debonding
(i.e. they will be unstaked within ~2 weeks).
debonding_shares:
allOf: [$ref: '#/components/schemas/TextBigInt']
description: |
The shares of tokens that were delegated to this validator account
at the start of this epoch, but are also in the process of debonding
(i.e. they will be unstaked within ~2 weeks).
num_delegators:
type: integer
format: uint64
Expand Down
8 changes: 8 additions & 0 deletions api/v1/strict_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,14 @@ func (srv *StrictServerImpl) GetConsensusValidatorsAddress(ctx context.Context,
return apiTypes.GetConsensusValidatorsAddress200JSONResponse(validators.Validators[0]), nil
}

func (srv *StrictServerImpl) GetConsensusValidatorsAddressHistory(ctx context.Context, request apiTypes.GetConsensusValidatorsAddressHistoryRequestObject) (apiTypes.GetConsensusValidatorsAddressHistoryResponseObject, error) {
history, err := srv.dbClient.ValidatorHistory(ctx, request.Address, request.Params)
if err != nil {
return nil, err
}
return apiTypes.GetConsensusValidatorsAddressHistory200JSONResponse(*history), nil
}

func (srv *StrictServerImpl) GetRuntimeBlocks(ctx context.Context, request apiTypes.GetRuntimeBlocksRequestObject) (apiTypes.GetRuntimeBlocksResponseObject, error) {
blocks, err := srv.dbClient.RuntimeBlocks(ctx, request.Params)
if err != nil {
Expand Down
39 changes: 39 additions & 0 deletions storage/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,7 @@ func (c *StorageClient) Validators(ctx context.Context, p apiTypes.GetConsensusV
&v.Escrow.DebondingShares,
&v.Escrow.SelfDelegationBalance,
&v.Escrow.SelfDelegationShares,
&v.Escrow.ActiveBalance24,
&v.Escrow.NumDelegators,
&v.VotingPower,
&v.VotingPowerTotal,
Expand Down Expand Up @@ -1312,6 +1313,44 @@ func (c *StorageClient) Validators(ctx context.Context, p apiTypes.GetConsensusV
return &vs, nil
}

func (c *StorageClient) ValidatorHistory(ctx context.Context, address staking.Address, p apiTypes.GetConsensusValidatorsAddressHistoryParams) (*ValidatorHistory, error) {
res, err := c.withTotalCount(
ctx,
queries.ValidatorHistory,
address.String(),
p.From,
p.To,
p.Limit,
p.Offset,
)
if err != nil {
return nil, wrapError(err)
}
defer res.rows.Close()

h := ValidatorHistory{
History: []ValidatorHistoryPoint{},
TotalCount: res.totalCount,
IsTotalCountClipped: res.isTotalCountClipped,
}
for res.rows.Next() {
b := ValidatorHistoryPoint{}
if err = res.rows.Scan(
&b.Epoch,
&b.ActiveBalance,
&b.ActiveShares,
&b.DebondingBalance,
&b.DebondingShares,
&b.NumDelegators,
); err != nil {
return nil, wrapError(err)
}
h.History = append(h.History, b)
}

return &h, nil
}

// RuntimeBlocks returns a list of runtime blocks.
func (c *StorageClient) RuntimeBlocks(ctx context.Context, p apiTypes.GetRuntimeBlocksParams) (*RuntimeBlockList, error) {
hash, err := canonicalizedHash(p.Hash)
Expand Down
23 changes: 23 additions & 0 deletions storage/client/queries/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ const (
COALESCE (
self_delegations.shares
, 0) AS self_delegation_shares,
history.validators.escrow_balance_active AS active_balance_24,
COALESCE (
delegators_count.count
, 0) AS num_delegators,
Expand All @@ -357,6 +358,11 @@ const (
JOIN chain.blocks ON chain.entities.start_block = chain.blocks.height
LEFT JOIN chain.commissions ON chain.entities.address = chain.commissions.address
LEFT JOIN self_delegations ON chain.entities.address = self_delegations.address
LEFT JOIN history.validators ON chain.entities.id = history.validators.id
-- Find the epoch id from 24 hours ago. Each epoch is ~1hr.
AND history.validators.epoch = (SELECT id - 24 from chain.epochs
ORDER BY id DESC
LIMIT 1)
LEFT JOIN delegators_count ON chain.entities.address = delegators_count.address
LEFT JOIN validator_nodes ON validator_nodes.address = entities.address
JOIN validator_rank ON chain.entities.address = validator_rank.address
Expand All @@ -369,6 +375,23 @@ const (
LIMIT $3::bigint
OFFSET $4::bigint`

ValidatorHistory = `
SELECT
epoch,
escrow_balance_active,
escrow_total_shares_active,
escrow_balance_debonding,
escrow_total_shares_debonding,
num_delegators
FROM chain.entities
JOIN history.validators ON chain.entities.id = history.validators.id
WHERE (chain.entities.address = $1::text) AND
($2::bigint IS NULL OR history.validators.epoch >= $2::bigint) AND
($3::bigint IS NULL OR history.validators.epoch <= $3::bigint)
ORDER BY epoch DESC
LIMIT $4::bigint
OFFSET $5::bigint`

RuntimeBlocks = `
SELECT round, block_hash, timestamp, num_transactions, size, gas_used
FROM chain.runtime_blocks
Expand Down
6 changes: 6 additions & 0 deletions storage/client/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ type ValidatorMedia = api.ValidatorMedia
// ValidatorCommissionBound is the commission bound for a validator.
type ValidatorCommissionBound = api.ValidatorCommissionBound

// ValidatorHistory is the storage response for GetValidatorHistory.
type ValidatorHistory = api.ValidatorHistory

// ValidatorHistoryPoint is the escrow information for a validator at a given epoch.
type ValidatorHistoryPoint = api.ValidatorHistoryPoint

// RuntimeBlockList is the storage response for RuntimeListBlocks.
type RuntimeBlockList = api.RuntimeBlockList

Expand Down
22 changes: 22 additions & 0 deletions tests/e2e_regression/damask/expected/validator_history.body
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"history": [
{
"active_balance": "75369956295476647",
"active_shares": "62390383512876854",
"debonding_balance": "683221451594757",
"debonding_shares": "683221451594757",
"epoch": 13403,
"num_delegators": 10840
},
{
"active_balance": "75368981020862238",
"active_shares": "62390367366454536",
"debonding_balance": "683221451594757",
"debonding_shares": "683221451594757",
"epoch": 13402,
"num_delegators": 10840
}
],
"is_total_count_clipped": false,
"total_count": 2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
HTTP/1.1 200 OK
Content-Type: application/json
Vary: Origin
Date: UNINTERESTING
Content-Length: UNINTERESTING

1 change: 1 addition & 0 deletions tests/e2e_regression/damask/test_cases.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ testCases=(
'epoch /v1/consensus/epochs/13403'
'tx /v1/consensus/transactions/f7a03e0912d355901ee794e5fec79a6b4c91363fc27d953596ee6de5c1492798'
'validator /v1/consensus/validators/oasis1qr3w66akc8ud9a4zsgyjw2muvcfjgfszn5ycgc0a'
'validator_history /v1/consensus/validators/oasis1qq0xmq7r0z9sdv02t5j9zs7en3n6574gtg8v9fyt/history'
'emerald_tx /v1/emerald/transactions/a6471a9c6f3307087586da9156f3c9876fbbaf4b23910cd9a2ac524a54d0aefe'
'emerald_failed_tx /v1/emerald/transactions/a7e76442c52a3cb81f719bde26c9a6179bd3415f96740d91a93ee8f205b45150'
'emerald_token_nfts /v1/emerald/evm_tokens/oasis1qqewaa87rnyshyqs7yutnnpzzetejecgeu005l8u/nfts'
Expand Down
22 changes: 22 additions & 0 deletions tests/e2e_regression/eden/expected/validator_history.body
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"history": [
{
"active_balance": "510452102234085399",
"active_shares": "409180341632413967",
"debonding_balance": "3664693487026403",
"debonding_shares": "3664693487026403",
"epoch": 28018,
"num_delegators": 10334
},
{
"active_balance": "510450642345248292",
"active_shares": "409180224607170953",
"debonding_balance": "3664847637026402",
"debonding_shares": "3664847637026402",
"epoch": 28017,
"num_delegators": 10334
}
],
"is_total_count_clipped": false,
"total_count": 2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
HTTP/1.1 200 OK
Content-Type: application/json
Vary: Origin
Date: UNINTERESTING
Content-Length: UNINTERESTING

1 change: 1 addition & 0 deletions tests/e2e_regression/eden/test_cases.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ testCases=(
'epoch /v1/consensus/epochs/28017'
'tx /v1/consensus/transactions/142d43e5194b738ab2223f8d0b42326fab06edd714a8cefc59a078b89b5de057'
'validator /v1/consensus/validators/oasis1qqekv2ymgzmd8j2s2u7g0hhc7e77e654kvwqtjwm'
'validator_history /v1/consensus/validators/oasis1qqekv2ymgzmd8j2s2u7g0hhc7e77e654kvwqtjwm/history'
'emerald_tx /v1/emerald/transactions/ec1173a69272c67f126f18012019d19cd25199e831f9417b6206fb7844406f9d'
'emerald_failed_tx /v1/emerald/transactions/35fdc8261dd81be8187c858aa9a623085494baf0565d414f48562a856147c093'
'emerald_events_by_nft /v1/emerald/events?contract_address=oasis1qz29t7nxkwfqgfk36uqqs9pzuzdt8zmrjud5mehx&nft_id=1'
Expand Down
Loading