code.vegaprotocol.io/vega@v0.79.0/core/coreapi/services/delegations.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package services
    17  
    18  import (
    19  	"context"
    20  	"math"
    21  	"strconv"
    22  	"sync"
    23  
    24  	"code.vegaprotocol.io/vega/core/events"
    25  	"code.vegaprotocol.io/vega/core/subscribers"
    26  	pb "code.vegaprotocol.io/vega/protos/vega"
    27  	eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1"
    28  )
    29  
    30  // next, current and last two.
    31  const maxEpochsToKeep = uint64(4)
    32  
    33  type delegationE interface {
    34  	events.Event
    35  	Proto() eventspb.DelegationBalanceEvent
    36  }
    37  
    38  // Delegations is a storage for keeping track of delegation state from delegation balance update events.
    39  type Delegations struct {
    40  	*subscribers.Base
    41  	ctx context.Context
    42  
    43  	mut                     sync.RWMutex
    44  	epochToPartyDelegations map[string]map[string]map[string]string // epoch -> party -> node -> amount
    45  	ch                      chan eventspb.DelegationBalanceEvent
    46  	minEpoch                uint64
    47  }
    48  
    49  func NewDelegations(ctx context.Context) (delegations *Delegations) {
    50  	defer func() { go delegations.consume() }()
    51  
    52  	return &Delegations{
    53  		Base:                    subscribers.NewBase(ctx, 1000, true),
    54  		ctx:                     ctx,
    55  		epochToPartyDelegations: map[string]map[string]map[string]string{},
    56  		ch:                      make(chan eventspb.DelegationBalanceEvent, 100),
    57  		minEpoch:                math.MaxUint64,
    58  	}
    59  }
    60  
    61  func (d *Delegations) consume() {
    62  	defer func() { close(d.ch) }()
    63  	for {
    64  		select {
    65  		case <-d.Closed():
    66  			return
    67  		case de, ok := <-d.ch:
    68  			if !ok {
    69  				// cleanup base
    70  				d.Halt()
    71  				// channel is closed
    72  				return
    73  			}
    74  			d.mut.Lock()
    75  			d.addDelegation(pb.Delegation{
    76  				NodeId:   de.NodeId,
    77  				Party:    de.Party,
    78  				EpochSeq: de.EpochSeq,
    79  				Amount:   de.Amount,
    80  			})
    81  			d.mut.Unlock()
    82  		}
    83  	}
    84  }
    85  
    86  func (d *Delegations) Push(evts ...events.Event) {
    87  	for _, e := range evts {
    88  		if ae, ok := e.(delegationE); ok {
    89  			d.ch <- ae.Proto()
    90  		}
    91  	}
    92  }
    93  
    94  func (d *Delegations) List(party, node, epoch string) []*pb.Delegation {
    95  	d.mut.RLock()
    96  	defer d.mut.RUnlock()
    97  
    98  	var delegations []*pb.Delegation
    99  	if epoch == "" && party == "" && node == "" { // all delegations for all parties all nodes across all epochs
   100  		delegations = d.getAllDelegations()
   101  	} else if epoch == "" && party == "" && node != "" { // all delegations for node from all parties across all epochs
   102  		delegations = d.getNodeDelegations(node)
   103  	} else if epoch == "" && party != "" && node == "" { // all delegations by a given party to all nodes across all epochs
   104  		delegations = d.getPartyDelegations(party)
   105  	} else if epoch == "" && party != "" && node != "" { // all delegations by a given party to a given node across all epochs
   106  		delegations = d.getPartyNodeDelegations(party, node)
   107  	} else if epoch != "" && party == "" && node == "" { // all delegations by all parties for all nodes in a given epoch
   108  		delegations = d.getAllDelegationsOnEpoch(epoch)
   109  	} else if epoch != "" && party == "" && node != "" { // all delegations to a given node on a given epoch
   110  		delegations = d.getNodeDelegationsOnEpoch(node, epoch)
   111  	} else if epoch != "" && party != "" && node == "" { // all delegations by a given party on a given epoch
   112  		delegations = d.getPartyDelegationsOnEpoch(party, epoch)
   113  	} else if epoch != "" && party != "" && node != "" { // all delegations by a given party to a given node on a given epoch
   114  		delegations = d.getPartyNodeDelegationsOnEpoch(party, node, epoch)
   115  	}
   116  
   117  	return delegations
   118  }
   119  
   120  // clearOldDelegations makes sure we only keep as many as <maxEpochsToKeep> epoch delegations.
   121  func (d *Delegations) clearOldDelegations(epochSeq string) {
   122  	epochSeqUint, err := strconv.ParseUint(epochSeq, 10, 64)
   123  	if err != nil {
   124  		return
   125  	}
   126  	// if we see an epoch younger than we've seen before - update the min epoch
   127  	if epochSeqUint <= d.minEpoch {
   128  		d.minEpoch = epochSeqUint
   129  	}
   130  	// if we haven't seen yet <maxEpochsToKeep> or we have no more than the required number of epochs - we don't have anything to do here
   131  	if epochSeqUint < maxEpochsToKeep || d.minEpoch >= (epochSeqUint-maxEpochsToKeep+1) {
   132  		return
   133  	}
   134  
   135  	// cleanup enough epochs such that we have at most <maxEpochsToKeep> epochs
   136  	for i := d.minEpoch; i < (epochSeqUint - maxEpochsToKeep + 1); i++ {
   137  		delete(d.epochToPartyDelegations, strconv.FormatUint(i, 10))
   138  	}
   139  	d.minEpoch = epochSeqUint - maxEpochsToKeep + 1
   140  }
   141  
   142  // AddDelegation is updated with new delegation update from the subscriber.
   143  func (d *Delegations) addDelegation(de pb.Delegation) {
   144  	// update party delegations
   145  	epoch, ok := d.epochToPartyDelegations[de.EpochSeq]
   146  	if !ok {
   147  		epoch = map[string]map[string]string{}
   148  		d.epochToPartyDelegations[de.EpochSeq] = epoch
   149  		d.clearOldDelegations(de.EpochSeq)
   150  	}
   151  
   152  	party, ok := epoch[de.Party]
   153  	if !ok {
   154  		party = map[string]string{}
   155  		epoch[de.Party] = party
   156  	}
   157  	party[de.NodeId] = de.Amount
   158  }
   159  
   160  // GetAllDelegations returns all delegations across all epochs, all parties, all nodes.
   161  func (d *Delegations) getAllDelegations() []*pb.Delegation {
   162  	delegations := []*pb.Delegation{}
   163  
   164  	for epoch, epochDelegations := range d.epochToPartyDelegations {
   165  		for party, partyDelegations := range epochDelegations {
   166  			for node, amount := range partyDelegations {
   167  				delegations = append(delegations, &pb.Delegation{
   168  					Party:    party,
   169  					NodeId:   node,
   170  					Amount:   amount,
   171  					EpochSeq: epoch,
   172  				})
   173  			}
   174  		}
   175  	}
   176  	return delegations
   177  }
   178  
   179  // GetAllDelegationsOnEpoch returns all delegation for the given epoch.
   180  func (d *Delegations) getAllDelegationsOnEpoch(epochSeq string) []*pb.Delegation {
   181  	delegations := []*pb.Delegation{}
   182  
   183  	epochDelegations, ok := d.epochToPartyDelegations[epochSeq]
   184  	if !ok {
   185  		return delegations
   186  	}
   187  	for party, partyDelegations := range epochDelegations {
   188  		for node, amount := range partyDelegations {
   189  			delegations = append(delegations, &pb.Delegation{
   190  				Party:    party,
   191  				NodeId:   node,
   192  				Amount:   amount,
   193  				EpochSeq: epochSeq,
   194  			})
   195  		}
   196  	}
   197  	return delegations
   198  }
   199  
   200  // GetNodeDelegations returns all the delegations made to a node across all epochs.
   201  func (d *Delegations) getNodeDelegations(nodeID string) []*pb.Delegation {
   202  	delegations := []*pb.Delegation{}
   203  
   204  	for epoch, epochDelegations := range d.epochToPartyDelegations {
   205  		for party, partyDelegations := range epochDelegations {
   206  			for node, amount := range partyDelegations {
   207  				if node != nodeID {
   208  					continue
   209  				}
   210  				delegations = append(delegations, &pb.Delegation{
   211  					Party:    party,
   212  					NodeId:   node,
   213  					Amount:   amount,
   214  					EpochSeq: epoch,
   215  				})
   216  			}
   217  		}
   218  	}
   219  	return delegations
   220  }
   221  
   222  // GetNodeDelegationsOnEpoch returns the delegations to a node by all parties at a given epoch.
   223  func (d *Delegations) getNodeDelegationsOnEpoch(nodeID string, epochSeq string) []*pb.Delegation {
   224  	delegations := []*pb.Delegation{}
   225  
   226  	epochDelegations, ok := d.epochToPartyDelegations[epochSeq]
   227  	if !ok {
   228  		return delegations
   229  	}
   230  
   231  	for party, partyDelegations := range epochDelegations {
   232  		for node, amount := range partyDelegations {
   233  			if node != nodeID {
   234  				continue
   235  			}
   236  			delegations = append(delegations, &pb.Delegation{
   237  				Party:    party,
   238  				NodeId:   node,
   239  				Amount:   amount,
   240  				EpochSeq: epochSeq,
   241  			})
   242  		}
   243  	}
   244  	return delegations
   245  }
   246  
   247  // GetPartyDelegations returns all the delegations by a party across all epochs.
   248  func (d *Delegations) getPartyDelegations(party string) []*pb.Delegation {
   249  	delegations := []*pb.Delegation{}
   250  
   251  	for epoch, epochDelegations := range d.epochToPartyDelegations {
   252  		partyDelAtEpoch, ok := epochDelegations[party]
   253  		if !ok {
   254  			continue
   255  		}
   256  
   257  		for node, amount := range partyDelAtEpoch {
   258  			delegations = append(delegations, &pb.Delegation{
   259  				Party:    party,
   260  				NodeId:   node,
   261  				Amount:   amount,
   262  				EpochSeq: epoch,
   263  			})
   264  		}
   265  	}
   266  	return delegations
   267  }
   268  
   269  // GetPartyDelegationsOnEpoch returns all delegation by party on a given epoch.
   270  func (d *Delegations) getPartyDelegationsOnEpoch(party string, epochSeq string) []*pb.Delegation {
   271  	delegations := []*pb.Delegation{}
   272  
   273  	epochDelegations, ok := d.epochToPartyDelegations[epochSeq]
   274  	if !ok {
   275  		return delegations
   276  	}
   277  
   278  	partyDelegations, ok := epochDelegations[party]
   279  	if !ok {
   280  		return delegations
   281  	}
   282  
   283  	for node, amount := range partyDelegations {
   284  		delegations = append(delegations, &pb.Delegation{
   285  			Party:    party,
   286  			NodeId:   node,
   287  			Amount:   amount,
   288  			EpochSeq: epochSeq,
   289  		})
   290  	}
   291  
   292  	return delegations
   293  }
   294  
   295  // GetPartyNodeDelegations returns the delegations from party to node across all epochs.
   296  func (d *Delegations) getPartyNodeDelegations(party string, node string) []*pb.Delegation {
   297  	delegations := []*pb.Delegation{}
   298  
   299  	for epoch, epochDelegations := range d.epochToPartyDelegations {
   300  		partyDelegations, ok := epochDelegations[party]
   301  		if !ok {
   302  			continue
   303  		}
   304  
   305  		nodeDelegations, ok := partyDelegations[node]
   306  		if !ok {
   307  			continue
   308  		}
   309  
   310  		delegations = append(delegations, &pb.Delegation{
   311  			Party:    party,
   312  			NodeId:   node,
   313  			Amount:   nodeDelegations,
   314  			EpochSeq: epoch,
   315  		})
   316  	}
   317  
   318  	return delegations
   319  }
   320  
   321  // GetPartyNodeDelegationsOnEpoch returns the delegations from party to node at epoch.
   322  func (d *Delegations) getPartyNodeDelegationsOnEpoch(party, node, epochSeq string) []*pb.Delegation {
   323  	delegations := []*pb.Delegation{}
   324  
   325  	epochDelegations, ok := d.epochToPartyDelegations[epochSeq]
   326  	if !ok {
   327  		return delegations
   328  	}
   329  
   330  	partyDelegations, ok := epochDelegations[party]
   331  	if !ok {
   332  		return delegations
   333  	}
   334  
   335  	nodeDelegations, ok := partyDelegations[node]
   336  	if !ok {
   337  		return delegations
   338  	}
   339  
   340  	delegations = append(delegations, &pb.Delegation{
   341  		Party:    party,
   342  		NodeId:   node,
   343  		Amount:   nodeDelegations,
   344  		EpochSeq: epochSeq,
   345  	})
   346  
   347  	return delegations
   348  }
   349  
   350  func (d *Delegations) Types() []events.Type {
   351  	return []events.Type{
   352  		events.DelegationBalanceEvent,
   353  	}
   354  }