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 }