Skip to content

Commit 52459a7

Browse files
ceyonurmeaghanfitzgeraldStephenButtolph
authored
Add L1 validators to getCurrentValidators response (#3843)
Signed-off-by: Meaghan FitzGerald <[email protected]> Signed-off-by: Ceyhun Onur <[email protected]> Co-authored-by: Meaghan FitzGerald <[email protected]> Co-authored-by: Stephen Buttolph <[email protected]>
1 parent 6e04bc0 commit 52459a7

File tree

6 files changed

+161
-92
lines changed

6 files changed

+161
-92
lines changed

RELEASES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Release Notes
22

3+
## Pending Release
4+
5+
- Added L1 validators to `platformvm.GetCurrentValidators` client implementation
6+
37
## [v1.13.1](https://github.com/ava-labs/avalanchego/releases/tag/v1.13.1)
48

59
This version is backwards compatible to [v1.13.0](https://github.com/ava-labs/avalanchego/releases/tag/v1.13.0). It is optional, but encouraged. The supported plugin version is `39`.

vms/platformvm/api/static_service.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/ava-labs/avalanchego/vms/platformvm/txs"
2323
"github.com/ava-labs/avalanchego/vms/platformvm/txs/txheap"
2424
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
25+
"github.com/ava-labs/avalanchego/vms/types"
2526
)
2627

2728
// Note that since an Avalanche network has exactly one Platform Chain,
@@ -72,16 +73,11 @@ func (utxo UTXO) Compare(other UTXO) int {
7273
return utxoAddr.Compare(otherAddr)
7374
}
7475

75-
// TODO: Refactor APIStaker, APIValidators and merge them together for
76-
// PermissionedValidators + PermissionlessValidators.
77-
78-
// APIStaker is the representation of a staker sent via APIs.
76+
// Staker is the representation of a staker sent via APIs.
7977
// [TxID] is the txID of the transaction that added this staker.
80-
// [Amount] is the amount of tokens being staked.
81-
// [StartTime] is the Unix time when they start staking
8278
// [Endtime] is the Unix time repr. of when they are done staking
8379
// [NodeID] is the node ID of the staker
84-
// [Uptime] is the observed uptime of this staker
80+
// [Weight] is the validator weight (stake) when sampling validators
8581
type Staker struct {
8682
TxID ids.ID `json:"txID"`
8783
StartTime json.Uint64 `json:"startTime"`
@@ -100,13 +96,33 @@ type Owner struct {
10096
Addresses []string `json:"addresses"`
10197
}
10298

99+
// APIL1Validator is the representation of a L1 validator sent over APIs.
100+
type APIL1Validator struct {
101+
NodeID ids.NodeID `json:"nodeID"`
102+
Weight json.Uint64 `json:"weight"`
103+
StartTime json.Uint64 `json:"startTime"`
104+
BaseL1Validator
105+
}
106+
107+
// BaseL1Validator is the representation of a base L1 validator without the common parts with a staker.
108+
type BaseL1Validator struct {
109+
ValidationID *ids.ID `json:"validationID,omitempty"`
110+
// PublicKey is the compressed BLS public key of the validator
111+
PublicKey *types.JSONByteSlice `json:"publicKey,omitempty"`
112+
RemainingBalanceOwner *Owner `json:"remainingBalanceOwner,omitempty"`
113+
DeactivationOwner *Owner `json:"deactivationOwner,omitempty"`
114+
MinNonce *json.Uint64 `json:"minNonce,omitempty"`
115+
// Balance is the remaining amount of AVAX this L1 validator has for paying
116+
// the continuous fee, according to the last accepted state. If the
117+
// validator is inactive, the balance will be 0.
118+
Balance *json.Uint64 `json:"balance,omitempty"`
119+
}
120+
103121
// PermissionlessValidator is the repr. of a permissionless validator sent over
104122
// APIs.
105123
type PermissionlessValidator struct {
106124
Staker
107-
// Deprecated: RewardOwner has been replaced by ValidationRewardOwner and
108-
// DelegationRewardOwner.
109-
RewardOwner *Owner `json:"rewardOwner,omitempty"`
125+
BaseL1Validator
110126
// The owner of the rewards from the validation period, if applicable.
111127
ValidationRewardOwner *Owner `json:"validationRewardOwner,omitempty"`
112128
// The owner of the rewards from delegations during the validation period,

vms/platformvm/client.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -360,10 +360,12 @@ func (c *client) GetL1Validator(
360360
if err != nil {
361361
return L1Validator{}, 0, err
362362
}
363-
364-
pk, err := bls.PublicKeyFromCompressedBytes(res.PublicKey)
365-
if err != nil {
366-
return L1Validator{}, 0, err
363+
var pk *bls.PublicKey
364+
if res.PublicKey != nil {
365+
pk, err = bls.PublicKeyFromCompressedBytes(*res.PublicKey)
366+
if err != nil {
367+
return L1Validator{}, 0, err
368+
}
367369
}
368370
remainingBalanceOwnerAddrs, err := address.ParseToIDs(res.RemainingBalanceOwner.Addresses)
369371
if err != nil {
@@ -374,6 +376,15 @@ func (c *client) GetL1Validator(
374376
return L1Validator{}, 0, err
375377
}
376378

379+
var minNonce uint64
380+
if res.MinNonce != nil {
381+
minNonce = uint64(*res.MinNonce)
382+
}
383+
var balance uint64
384+
if res.Balance != nil {
385+
balance = uint64(*res.Balance)
386+
}
387+
377388
return L1Validator{
378389
SubnetID: res.SubnetID,
379390
NodeID: res.NodeID,
@@ -390,8 +401,8 @@ func (c *client) GetL1Validator(
390401
},
391402
StartTime: uint64(res.StartTime),
392403
Weight: uint64(res.Weight),
393-
MinNonce: uint64(res.MinNonce),
394-
Balance: uint64(res.Balance),
404+
MinNonce: minNonce,
405+
Balance: balance,
395406
}, uint64(res.Height), err
396407
}
397408

vms/platformvm/client_permissionless_validator.go

Lines changed: 80 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,19 @@ type ClientOwner struct {
3333
Addresses []ids.ShortID
3434
}
3535

36+
type ClientL1Validator struct {
37+
ValidationID *ids.ID
38+
RemainingBalanceOwner *ClientOwner
39+
DeactivationOwner *ClientOwner
40+
MinNonce *uint64
41+
Balance *uint64
42+
}
43+
3644
// ClientPermissionlessValidator is the repr. of a permissionless validator sent
3745
// over client
3846
type ClientPermissionlessValidator struct {
3947
ClientStaker
48+
ClientL1Validator
4049
ValidationRewardOwner *ClientOwner
4150
DelegationRewardOwner *ClientOwner
4251
PotentialReward *uint64
@@ -99,47 +108,85 @@ func getClientPermissionlessValidators(validatorsSliceIntf []interface{}) ([]Cli
99108
return nil, err
100109
}
101110

102-
validationRewardOwner, err := apiOwnerToClientOwner(apiValidator.ValidationRewardOwner)
111+
clientValidator, err := getClientPrimaryOrSubnetValidator(apiValidator)
103112
if err != nil {
104113
return nil, err
105114
}
106115

107-
delegationRewardOwner, err := apiOwnerToClientOwner(apiValidator.DelegationRewardOwner)
108-
if err != nil {
109-
return nil, err
116+
// If the validator is a L1 validator, we need to set the L1 fields as well
117+
if apiValidator.ValidationID != nil {
118+
l1Validator, err := getClientL1Validator(apiValidator)
119+
if err != nil {
120+
return nil, err
121+
}
122+
clientValidator.ClientL1Validator = l1Validator
110123
}
111124

112-
var clientDelegators []ClientDelegator
113-
if apiValidator.Delegators != nil {
114-
clientDelegators = make([]ClientDelegator, len(*apiValidator.Delegators))
115-
for j, apiDelegator := range *apiValidator.Delegators {
116-
rewardOwner, err := apiOwnerToClientOwner(apiDelegator.RewardOwner)
117-
if err != nil {
118-
return nil, err
119-
}
120-
121-
clientDelegators[j] = ClientDelegator{
122-
ClientStaker: apiStakerToClientStaker(apiDelegator.Staker),
123-
RewardOwner: rewardOwner,
124-
PotentialReward: (*uint64)(apiDelegator.PotentialReward),
125-
}
125+
clientValidators[i] = clientValidator
126+
}
127+
return clientValidators, nil
128+
}
129+
130+
func getClientL1Validator(apiValidator api.PermissionlessValidator) (ClientL1Validator, error) {
131+
remainingBalanceOwner, err := apiOwnerToClientOwner(apiValidator.RemainingBalanceOwner)
132+
if err != nil {
133+
return ClientL1Validator{}, err
134+
}
135+
136+
deactivationOwner, err := apiOwnerToClientOwner(apiValidator.DeactivationOwner)
137+
if err != nil {
138+
return ClientL1Validator{}, err
139+
}
140+
141+
return ClientL1Validator{
142+
ValidationID: apiValidator.ValidationID,
143+
RemainingBalanceOwner: remainingBalanceOwner,
144+
DeactivationOwner: deactivationOwner,
145+
MinNonce: (*uint64)(apiValidator.MinNonce),
146+
Balance: (*uint64)(apiValidator.Balance),
147+
}, nil
148+
}
149+
150+
func getClientPrimaryOrSubnetValidator(apiValidator api.PermissionlessValidator) (ClientPermissionlessValidator, error) {
151+
validationRewardOwner, err := apiOwnerToClientOwner(apiValidator.ValidationRewardOwner)
152+
if err != nil {
153+
return ClientPermissionlessValidator{}, err
154+
}
155+
156+
delegationRewardOwner, err := apiOwnerToClientOwner(apiValidator.DelegationRewardOwner)
157+
if err != nil {
158+
return ClientPermissionlessValidator{}, err
159+
}
160+
161+
var clientDelegators []ClientDelegator
162+
if apiValidator.Delegators != nil {
163+
clientDelegators = make([]ClientDelegator, len(*apiValidator.Delegators))
164+
for j, apiDelegator := range *apiValidator.Delegators {
165+
rewardOwner, err := apiOwnerToClientOwner(apiDelegator.RewardOwner)
166+
if err != nil {
167+
return ClientPermissionlessValidator{}, err
126168
}
127-
}
128169

129-
clientValidators[i] = ClientPermissionlessValidator{
130-
ClientStaker: apiStakerToClientStaker(apiValidator.Staker),
131-
ValidationRewardOwner: validationRewardOwner,
132-
DelegationRewardOwner: delegationRewardOwner,
133-
PotentialReward: (*uint64)(apiValidator.PotentialReward),
134-
AccruedDelegateeReward: (*uint64)(apiValidator.AccruedDelegateeReward),
135-
DelegationFee: float32(apiValidator.DelegationFee),
136-
Uptime: (*float32)(apiValidator.Uptime),
137-
Connected: apiValidator.Connected,
138-
Signer: apiValidator.Signer,
139-
DelegatorCount: (*uint64)(apiValidator.DelegatorCount),
140-
DelegatorWeight: (*uint64)(apiValidator.DelegatorWeight),
141-
Delegators: clientDelegators,
170+
clientDelegators[j] = ClientDelegator{
171+
ClientStaker: apiStakerToClientStaker(apiDelegator.Staker),
172+
RewardOwner: rewardOwner,
173+
PotentialReward: (*uint64)(apiDelegator.PotentialReward),
174+
}
142175
}
143176
}
144-
return clientValidators, nil
177+
178+
return ClientPermissionlessValidator{
179+
ClientStaker: apiStakerToClientStaker(apiValidator.Staker),
180+
ValidationRewardOwner: validationRewardOwner,
181+
DelegationRewardOwner: delegationRewardOwner,
182+
PotentialReward: (*uint64)(apiValidator.PotentialReward),
183+
AccruedDelegateeReward: (*uint64)(apiValidator.AccruedDelegateeReward),
184+
DelegationFee: float32(apiValidator.DelegationFee),
185+
Uptime: (*float32)(apiValidator.Uptime),
186+
Connected: apiValidator.Connected,
187+
Signer: apiValidator.Signer,
188+
DelegatorCount: (*uint64)(apiValidator.DelegatorCount),
189+
DelegatorWeight: (*uint64)(apiValidator.DelegatorWeight),
190+
Delegators: clientDelegators,
191+
}, nil
145192
}

vms/platformvm/service.go

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -897,7 +897,6 @@ func (s *Service) getPrimaryOrSubnetValidators(subnetID ids.ID, nodeIDs set.Set[
897897
Connected: connected,
898898
PotentialReward: &potentialReward,
899899
AccruedDelegateeReward: &jsonDelegateeReward,
900-
RewardOwner: validationRewardOwner,
901900
ValidationRewardOwner: validationRewardOwner,
902901
DelegationRewardOwner: delegationRewardOwner,
903902
DelegationFee: delegationFee,
@@ -973,24 +972,8 @@ type GetL1ValidatorArgs struct {
973972
ValidationID ids.ID `json:"validationID"`
974973
}
975974

976-
type APIL1Validator struct {
977-
ValidationID ids.ID `json:"validationID"`
978-
NodeID ids.NodeID `json:"nodeID"`
979-
// PublicKey is the compressed BLS public key of the validator
980-
PublicKey types.JSONByteSlice `json:"publicKey"`
981-
RemainingBalanceOwner platformapi.Owner `json:"remainingBalanceOwner"`
982-
DeactivationOwner platformapi.Owner `json:"deactivationOwner"`
983-
StartTime avajson.Uint64 `json:"startTime"`
984-
Weight avajson.Uint64 `json:"weight"`
985-
MinNonce avajson.Uint64 `json:"minNonce"`
986-
// Balance is the remaining amount of AVAX this L1 validator has for paying
987-
// the continuous fee, according to the last accepted state. If the
988-
// validator is inactive, the balance will be 0.
989-
Balance avajson.Uint64 `json:"balance"`
990-
}
991-
992975
type GetL1ValidatorReply struct {
993-
APIL1Validator
976+
platformapi.APIL1Validator
994977
SubnetID ids.ID `json:"subnetID"`
995978
// Height is the height of the last accepted block
996979
Height avajson.Uint64 `json:"height"`
@@ -1028,46 +1011,54 @@ func (s *Service) GetL1Validator(r *http.Request, args *GetL1ValidatorArgs, repl
10281011
return nil
10291012
}
10301013

1031-
func (s *Service) convertL1ValidatorToAPI(vdr state.L1Validator) (APIL1Validator, error) {
1014+
func (s *Service) convertL1ValidatorToAPI(vdr state.L1Validator) (platformapi.APIL1Validator, error) {
10321015
var remainingBalanceOwner message.PChainOwner
10331016
if _, err := txs.Codec.Unmarshal(vdr.RemainingBalanceOwner, &remainingBalanceOwner); err != nil {
1034-
return APIL1Validator{}, fmt.Errorf("failed unmarshalling remaining balance owner: %w", err)
1017+
return platformapi.APIL1Validator{}, fmt.Errorf("failed unmarshalling remaining balance owner: %w", err)
10351018
}
10361019
remainingBalanceAPIOwner, err := s.getAPIOwner(&secp256k1fx.OutputOwners{
10371020
Threshold: remainingBalanceOwner.Threshold,
10381021
Addrs: remainingBalanceOwner.Addresses,
10391022
})
10401023
if err != nil {
1041-
return APIL1Validator{}, fmt.Errorf("failed formatting remaining balance owner: %w", err)
1024+
return platformapi.APIL1Validator{}, fmt.Errorf("failed formatting remaining balance owner: %w", err)
10421025
}
10431026

10441027
var deactivationOwner message.PChainOwner
10451028
if _, err := txs.Codec.Unmarshal(vdr.DeactivationOwner, &deactivationOwner); err != nil {
1046-
return APIL1Validator{}, fmt.Errorf("failed unmarshalling deactivation owner: %w", err)
1029+
return platformapi.APIL1Validator{}, fmt.Errorf("failed unmarshalling deactivation owner: %w", err)
10471030
}
10481031
deactivationAPIOwner, err := s.getAPIOwner(&secp256k1fx.OutputOwners{
10491032
Threshold: deactivationOwner.Threshold,
10501033
Addrs: deactivationOwner.Addresses,
10511034
})
10521035
if err != nil {
1053-
return APIL1Validator{}, fmt.Errorf("failed formatting deactivation owner: %w", err)
1054-
}
1055-
1056-
apiVdr := APIL1Validator{
1057-
ValidationID: vdr.ValidationID,
1058-
NodeID: vdr.NodeID,
1059-
PublicKey: bls.PublicKeyToCompressedBytes(
1060-
bls.PublicKeyFromValidUncompressedBytes(vdr.PublicKey),
1061-
),
1062-
RemainingBalanceOwner: *remainingBalanceAPIOwner,
1063-
DeactivationOwner: *deactivationAPIOwner,
1064-
StartTime: avajson.Uint64(vdr.StartTime),
1065-
Weight: avajson.Uint64(vdr.Weight),
1066-
MinNonce: avajson.Uint64(vdr.MinNonce),
1067-
}
1036+
return platformapi.APIL1Validator{}, fmt.Errorf("failed formatting deactivation owner: %w", err)
1037+
}
1038+
1039+
pubKey := types.JSONByteSlice(bls.PublicKeyToCompressedBytes(
1040+
bls.PublicKeyFromValidUncompressedBytes(vdr.PublicKey),
1041+
))
1042+
minNonce := avajson.Uint64(vdr.MinNonce)
1043+
1044+
apiVdr := platformapi.APIL1Validator{
1045+
NodeID: vdr.NodeID,
1046+
StartTime: avajson.Uint64(vdr.StartTime),
1047+
Weight: avajson.Uint64(vdr.Weight),
1048+
BaseL1Validator: platformapi.BaseL1Validator{
1049+
ValidationID: &vdr.ValidationID,
1050+
PublicKey: &pubKey,
1051+
RemainingBalanceOwner: remainingBalanceAPIOwner,
1052+
DeactivationOwner: deactivationAPIOwner,
1053+
MinNonce: &minNonce,
1054+
},
1055+
}
1056+
zero := avajson.Uint64(0)
1057+
apiVdr.Balance = &zero
10681058
if vdr.EndAccumulatedFee != 0 {
10691059
accruedFees := s.vm.state.GetAccruedFees()
1070-
apiVdr.Balance = avajson.Uint64(vdr.EndAccumulatedFee - accruedFees)
1060+
balance := avajson.Uint64(vdr.EndAccumulatedFee - accruedFees)
1061+
apiVdr.Balance = &balance
10711062
}
10721063
return apiVdr, nil
10731064
}

vms/platformvm/service_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,18 +1524,18 @@ func TestGetCurrentValidatorsForL1(t *testing.T) {
15241524
require.Equal(avajson.Uint64(staker.Weight), v.Weight)
15251525
require.Equal(staker.StartTime.Unix(), int64(v.StartTime))
15261526
return v.NodeID
1527-
case APIL1Validator:
1528-
validator, exists := l1ValidatorsByVID[v.ValidationID]
1527+
case pchainapi.APIL1Validator:
1528+
validator, exists := l1ValidatorsByVID[*v.ValidationID]
15291529
require.True(exists, "unexpected validator: %s", vdr)
15301530
require.Equal(validator.NodeID, v.NodeID)
15311531
require.Equal(avajson.Uint64(validator.Weight), v.Weight)
15321532
require.Equal(validator.StartTime, uint64(v.StartTime))
15331533
accruedFees := service.vm.state.GetAccruedFees()
1534-
require.Equal(avajson.Uint64(validator.EndAccumulatedFee-accruedFees), v.Balance)
1535-
require.Equal(avajson.Uint64(validator.MinNonce), v.MinNonce)
1534+
require.Equal(avajson.Uint64(validator.EndAccumulatedFee-accruedFees), *v.Balance)
1535+
require.Equal(avajson.Uint64(validator.MinNonce), *v.MinNonce)
15361536
require.Equal(
15371537
types.JSONByteSlice(bls.PublicKeyToCompressedBytes(bls.PublicKeyFromValidUncompressedBytes(validator.PublicKey))),
1538-
v.PublicKey)
1538+
*v.PublicKey)
15391539
var expectedRemainingBalanceOwner message.PChainOwner
15401540
_, err := txs.Codec.Unmarshal(validator.RemainingBalanceOwner, &expectedRemainingBalanceOwner)
15411541
require.NoError(err)

0 commit comments

Comments
 (0)