Skip to content

Commit

Permalink
Optimize P-chain GetValidators when NodeIDs are provided (ava-labs#2263)
Browse files Browse the repository at this point in the history
  • Loading branch information
abi87 authored Jan 23, 2023
1 parent 8918a43 commit b7b31a0
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 43 deletions.
123 changes: 86 additions & 37 deletions vms/platformvm/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -685,24 +685,48 @@ func (s *Service) GetCurrentValidators(_ *http.Request, args *GetCurrentValidato
// Create set of nodeIDs
nodeIDs := set.Set[ids.NodeID]{}
nodeIDs.Add(args.NodeIDs...)
includeAllNodes := nodeIDs.Len() == 0

currentStakerIterator, err := s.vm.state.GetCurrentStakerIterator()
if err != nil {
return err
}
defer currentStakerIterator.Release()

// TODO: do not iterate over all stakers when nodeIDs given. Use currentValidators.ValidatorSet for iteration
for currentStakerIterator.Next() { // Iterates in order of increasing stop time
currentStaker := currentStakerIterator.Value()
if args.SubnetID != currentStaker.SubnetID {
continue
numNodeIDs := nodeIDs.Len()
targetStakers := make([]*state.Staker, 0, numNodeIDs)
if numNodeIDs == 0 { // Include all nodes
currentStakerIterator, err := s.vm.state.GetCurrentStakerIterator()
if err != nil {
return err
}
if !includeAllNodes && !nodeIDs.Contains(currentStaker.NodeID) {
continue
for currentStakerIterator.Next() {
staker := currentStakerIterator.Value()
if args.SubnetID != staker.SubnetID {
continue
}
targetStakers = append(targetStakers, staker)
}
currentStakerIterator.Release()
} else {
for nodeID := range nodeIDs {
staker, err := s.vm.state.GetCurrentValidator(args.SubnetID, nodeID)
switch err {
case nil:
case database.ErrNotFound:
// nothing to do, continue
continue
default:
return err
}
targetStakers = append(targetStakers, staker)

delegatorsIt, err := s.vm.state.GetCurrentDelegatorIterator(args.SubnetID, nodeID)
if err != nil {
return err
}
for delegatorsIt.Next() {
staker := delegatorsIt.Value()
targetStakers = append(targetStakers, staker)
}
delegatorsIt.Release()
}
}

for _, currentStaker := range targetStakers {
tx, _, err := s.vm.state.GetTx(currentStaker.TxID)
if err != nil {
return err
Expand All @@ -717,11 +741,11 @@ func (s *Service) GetCurrentValidators(_ *http.Request, args *GetCurrentValidato
StakeAmount: &weight,
NodeID: nodeID,
}

potentialReward := json.Uint64(currentStaker.PotentialReward)
switch staker := tx.Unsigned.(type) {

switch stakerTx := tx.Unsigned.(type) {
case txs.ValidatorTx:
shares := staker.Shares()
shares := stakerTx.Shares()
delegationFee := json.Float32(100 * float32(shares) / float32(reward.PercentDenominator))

uptime, err := s.getAPIUptime(currentStaker)
Expand All @@ -734,14 +758,14 @@ func (s *Service) GetCurrentValidators(_ *http.Request, args *GetCurrentValidato
validationRewardOwner *platformapi.Owner
delegationRewardOwner *platformapi.Owner
)
validationOwner, ok := staker.ValidationRewardsOwner().(*secp256k1fx.OutputOwners)
validationOwner, ok := stakerTx.ValidationRewardsOwner().(*secp256k1fx.OutputOwners)
if ok {
validationRewardOwner, err = s.getAPIOwner(validationOwner)
if err != nil {
return err
}
}
delegationOwner, ok := staker.DelegationRewardsOwner().(*secp256k1fx.OutputOwners)
delegationOwner, ok := stakerTx.DelegationRewardsOwner().(*secp256k1fx.OutputOwners)
if ok {
delegationRewardOwner, err = s.getAPIOwner(delegationOwner)
if err != nil {
Expand All @@ -760,7 +784,7 @@ func (s *Service) GetCurrentValidators(_ *http.Request, args *GetCurrentValidato
DelegationFee: delegationFee,
}

if staker, ok := staker.(*txs.AddPermissionlessValidatorTx); ok {
if staker, ok := stakerTx.(*txs.AddPermissionlessValidatorTx); ok {
if signer, ok := staker.Signer.(*signer.ProofOfPossession); ok {
vdr.Signer = signer
}
Expand All @@ -770,7 +794,7 @@ func (s *Service) GetCurrentValidators(_ *http.Request, args *GetCurrentValidato

case txs.DelegatorTx:
var rewardOwner *platformapi.Owner
owner, ok := staker.RewardsOwner().(*secp256k1fx.OutputOwners)
owner, ok := stakerTx.RewardsOwner().(*secp256k1fx.OutputOwners)
if ok {
rewardOwner, err = s.getAPIOwner(owner)
if err != nil {
Expand Down Expand Up @@ -841,23 +865,48 @@ func (s *Service) GetPendingValidators(_ *http.Request, args *GetPendingValidato
// Create set of nodeIDs
nodeIDs := set.Set[ids.NodeID]{}
nodeIDs.Add(args.NodeIDs...)
includeAllNodes := nodeIDs.Len() == 0

pendingStakerIterator, err := s.vm.state.GetPendingStakerIterator()
if err != nil {
return err
}
defer pendingStakerIterator.Release()

for pendingStakerIterator.Next() { // Iterates in order of increasing start time
pendingStaker := pendingStakerIterator.Value()
if args.SubnetID != pendingStaker.SubnetID {
continue
numNodeIDs := nodeIDs.Len()
targetStakers := make([]*state.Staker, 0, numNodeIDs)
if numNodeIDs == 0 { // Include all nodes
pendingStakerIterator, err := s.vm.state.GetPendingStakerIterator()
if err != nil {
return err
}
if !includeAllNodes && !nodeIDs.Contains(pendingStaker.NodeID) {
continue
for pendingStakerIterator.Next() { // Iterates in order of increasing stop time
staker := pendingStakerIterator.Value()
if args.SubnetID != staker.SubnetID {
continue
}
targetStakers = append(targetStakers, staker)
}
pendingStakerIterator.Release()
} else {
for nodeID := range nodeIDs {
staker, err := s.vm.state.GetPendingValidator(args.SubnetID, nodeID)
switch err {
case nil:
case database.ErrNotFound:
// nothing to do, continue
continue
default:
return err
}
targetStakers = append(targetStakers, staker)

delegatorsIt, err := s.vm.state.GetPendingDelegatorIterator(args.SubnetID, nodeID)
if err != nil {
return err
}
for delegatorsIt.Next() {
staker := delegatorsIt.Value()
targetStakers = append(targetStakers, staker)
}
delegatorsIt.Release()
}
}

for _, pendingStaker := range targetStakers {
tx, _, err := s.vm.state.GetTx(pendingStaker.TxID)
if err != nil {
return err
Expand All @@ -873,9 +922,9 @@ func (s *Service) GetPendingValidators(_ *http.Request, args *GetPendingValidato
StakeAmount: &weight,
}

switch staker := tx.Unsigned.(type) {
switch stakerTx := tx.Unsigned.(type) {
case txs.ValidatorTx:
shares := staker.Shares()
shares := stakerTx.Shares()
delegationFee := json.Float32(100 * float32(shares) / float32(reward.PercentDenominator))

connected := s.vm.uptimeManager.IsConnected(nodeID, args.SubnetID)
Expand All @@ -885,7 +934,7 @@ func (s *Service) GetPendingValidators(_ *http.Request, args *GetPendingValidato
Connected: connected,
}

if staker, ok := staker.(*txs.AddPermissionlessValidatorTx); ok {
if staker, ok := stakerTx.(*txs.AddPermissionlessValidatorTx); ok {
if signer, ok := staker.Signer.(*signer.ProofOfPossession); ok {
vdr.Signer = signer
}
Expand Down
16 changes: 10 additions & 6 deletions vms/platformvm/txs/executor/state_changes.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@ func AdvanceTimeTo(

// Add to the staker set any pending stakers whose start time is at or
// before the new timestamp

// Note: we process pending stakers ready to be promoted to current ones and
// then we process current stakers to be demoted out of stakers set. It is
// guaranteed that no promoted stakers would be demoted immediately. A
// failure of this invariant would cause a staker to be added to
// StateChanges and be persisted among current stakers even if it already
// expired. The following invariants ensure this does not happens:
// Invariant: minimum stake duration is > 0, so staker.StartTime != staker.EndTime.
// Invariant: [newChainTime] does not skip stakers set change times.

for pendingStakerIterator.Next() {
stakerToRemove := pendingStakerIterator.Value()
if stakerToRemove.StartTime.After(newChainTime) {
Expand All @@ -130,12 +140,6 @@ func AdvanceTimeTo(
stakerToAdd.Priority = txs.PendingToCurrentPriorities[stakerToRemove.Priority]

if stakerToRemove.Priority == txs.SubnetPermissionedValidatorPendingPriority {
// Invariant: [txTimestamp] <= [nextStakerChangeTime].
// Invariant: minimum stake duration is > 0.
//
// Both of the above invariants ensure the staker we are adding here
// should never be attempted to be removed in the following loop.

changes.currentValidatorsToAdd = append(changes.currentValidatorsToAdd, &stakerToAdd)
changes.pendingValidatorsToRemove = append(changes.pendingValidatorsToRemove, stakerToRemove)
continue
Expand Down

0 comments on commit b7b31a0

Please sign in to comment.