github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/core/state/state.go (about) 1 /* 2 * Copyright (C) 2019 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package state 19 20 import ( 21 "math/big" 22 "sync" 23 "time" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/jinzhu/copier" 27 "github.com/rs/zerolog/log" 28 29 "github.com/mysteriumnetwork/node/consumer/bandwidth" 30 "github.com/mysteriumnetwork/node/consumer/session" 31 "github.com/mysteriumnetwork/node/core/connection/connectionstate" 32 "github.com/mysteriumnetwork/node/core/discovery/proposal" 33 "github.com/mysteriumnetwork/node/core/service" 34 "github.com/mysteriumnetwork/node/core/service/servicestate" 35 stateEvent "github.com/mysteriumnetwork/node/core/state/event" 36 "github.com/mysteriumnetwork/node/eventbus" 37 "github.com/mysteriumnetwork/node/identity" 38 "github.com/mysteriumnetwork/node/identity/registry" 39 "github.com/mysteriumnetwork/node/market" 40 nodeSession "github.com/mysteriumnetwork/node/session" 41 sessionEvent "github.com/mysteriumnetwork/node/session/event" 42 "github.com/mysteriumnetwork/node/session/pingpong" 43 pingpongEvent "github.com/mysteriumnetwork/node/session/pingpong/event" 44 "github.com/mysteriumnetwork/node/tequilapi/contract" 45 ) 46 47 // DefaultDebounceDuration is the default time interval suggested for debouncing 48 const DefaultDebounceDuration = time.Millisecond * 200 49 50 type publisher interface { 51 Publish(topic string, data interface{}) 52 } 53 54 type serviceLister interface { 55 List(includeAll bool) []*service.Instance 56 } 57 58 type identityProvider interface { 59 GetIdentities() []identity.Identity 60 } 61 62 type channelAddressCalculator interface { 63 GetActiveChannelAddress(chainID int64, id common.Address) (common.Address, error) 64 GetActiveHermes(chainID int64) (common.Address, error) 65 } 66 67 type balanceProvider interface { 68 GetBalance(chainID int64, id identity.Identity) *big.Int 69 } 70 71 type earningsProvider interface { 72 List(chainID int64) []pingpong.HermesChannel 73 GetEarningsDetailed(chainID int64, id identity.Identity) *pingpongEvent.EarningsDetailed 74 } 75 76 // Keeper keeps track of state through eventual consistency. 77 // This should become the de-facto place to get your info about node. 78 type Keeper struct { 79 state *stateEvent.State 80 lock sync.RWMutex 81 deps KeeperDeps 82 83 // provider 84 consumeServiceStateEvent func(e interface{}) 85 consumeServiceSessionStatisticsEvent func(e interface{}) 86 consumeServiceSessionEarningsEvent func(e interface{}) 87 consumeNATStatusUpdateEvent func(e interface{}) 88 // consumer 89 consumeConnectionStatisticsEvent func(interface{}) 90 consumeConnectionThroughputEvent func(interface{}) 91 consumeConnectionSpendingEvent func(interface{}) 92 93 announceStateChanges func(e interface{}) 94 } 95 96 // KeeperDeps to construct the state.Keeper. 97 type KeeperDeps struct { 98 Publisher publisher 99 ServiceLister serviceLister 100 IdentityProvider identityProvider 101 IdentityRegistry registry.IdentityRegistry 102 IdentityChannelCalculator channelAddressCalculator 103 BalanceProvider balanceProvider 104 EarningsProvider earningsProvider 105 ChainID int64 106 ProposalPricer proposalPricer 107 } 108 109 type proposalPricer interface { 110 EnrichProposalWithPrice(in market.ServiceProposal) (proposal.PricedServiceProposal, error) 111 } 112 113 // NewKeeper returns a new instance of the keeper. 114 func NewKeeper(deps KeeperDeps, debounceDuration time.Duration) *Keeper { 115 k := &Keeper{ 116 state: &stateEvent.State{ 117 Sessions: make([]session.History, 0), 118 Connections: make(map[string]stateEvent.Connection), 119 }, 120 deps: deps, 121 } 122 k.state.Identities = k.fetchIdentities() 123 k.state.ProviderChannels = k.deps.EarningsProvider.List(deps.ChainID) 124 125 // provider 126 k.consumeServiceStateEvent = debounce(k.updateServiceState, debounceDuration) 127 k.consumeServiceSessionStatisticsEvent = debounce(k.updateSessionStats, debounceDuration) 128 k.consumeServiceSessionEarningsEvent = debounce(k.updateSessionEarnings, debounceDuration) 129 130 // consumer 131 k.consumeConnectionStatisticsEvent = debounce(k.updateConnectionStats, debounceDuration) 132 k.consumeConnectionThroughputEvent = debounce(k.updateConnectionThroughput, debounceDuration) 133 k.consumeConnectionSpendingEvent = debounce(k.updateConnectionSpending, debounceDuration) 134 k.announceStateChanges = debounce(k.announceState, debounceDuration) 135 136 return k 137 } 138 139 func (k *Keeper) fetchIdentities() []stateEvent.Identity { 140 ids := k.deps.IdentityProvider.GetIdentities() 141 identities := make([]stateEvent.Identity, len(ids)) 142 for idx, id := range ids { 143 status, err := k.deps.IdentityRegistry.GetRegistrationStatus(k.deps.ChainID, id) 144 if err != nil { 145 log.Warn().Err(err).Msgf("Could not get registration status for %s", id.Address) 146 status = registry.Unknown 147 } 148 hermesID, err := k.deps.IdentityChannelCalculator.GetActiveHermes(k.deps.ChainID) 149 if err != nil { 150 log.Warn().Err(err).Msgf("Could not retrieve hermesID for %s", id.Address) 151 } 152 channelAddress, err := k.deps.IdentityChannelCalculator.GetActiveChannelAddress(k.deps.ChainID, id.ToCommonAddress()) 153 if err != nil { 154 log.Warn().Err(err).Msgf("Could not calculate channel address for %s", id.Address) 155 } 156 earnings := k.deps.EarningsProvider.GetEarningsDetailed(k.deps.ChainID, id) 157 balance := k.deps.BalanceProvider.GetBalance(k.deps.ChainID, id) 158 159 stateIdentity := stateEvent.Identity{ 160 Address: id.Address, 161 RegistrationStatus: status, 162 ChannelAddress: channelAddress, 163 Balance: balance, 164 Earnings: earnings.Total.UnsettledBalance, 165 EarningsTotal: earnings.Total.LifetimeBalance, 166 HermesID: hermesID, 167 EarningsPerHermes: earnings.PerHermes, 168 } 169 identities[idx] = stateIdentity 170 } 171 return identities 172 } 173 174 // Subscribe subscribes to the event bus. 175 func (k *Keeper) Subscribe(bus eventbus.Subscriber) error { 176 if err := bus.SubscribeAsync(servicestate.AppTopicServiceStatus, k.consumeServiceStateEvent); err != nil { 177 return err 178 } 179 if err := bus.SubscribeAsync(sessionEvent.AppTopicSession, k.consumeServiceSessionEvent); err != nil { 180 return err 181 } 182 if err := bus.SubscribeAsync(sessionEvent.AppTopicDataTransferred, k.consumeServiceSessionStatisticsEvent); err != nil { 183 return err 184 } 185 if err := bus.SubscribeAsync(sessionEvent.AppTopicTokensEarned, k.consumeServiceSessionEarningsEvent); err != nil { 186 return err 187 } 188 if err := bus.SubscribeAsync(connectionstate.AppTopicConnectionState, k.consumeConnectionStateEvent); err != nil { 189 return err 190 } 191 if err := bus.SubscribeAsync(connectionstate.AppTopicConnectionStatistics, k.consumeConnectionStatisticsEvent); err != nil { 192 return err 193 } 194 if err := bus.SubscribeAsync(bandwidth.AppTopicConnectionThroughput, k.consumeConnectionThroughputEvent); err != nil { 195 return err 196 } 197 if err := bus.SubscribeAsync(pingpongEvent.AppTopicInvoicePaid, k.consumeConnectionSpendingEvent); err != nil { 198 return err 199 } 200 if err := bus.SubscribeAsync(identity.AppTopicIdentityCreated, k.consumeIdentityCreatedEvent); err != nil { 201 return err 202 } 203 if err := bus.SubscribeAsync(registry.AppTopicIdentityRegistration, k.consumeIdentityRegistrationEvent); err != nil { 204 return err 205 } 206 if err := bus.SubscribeAsync(pingpongEvent.AppTopicBalanceChanged, k.consumeBalanceChangedEvent); err != nil { 207 return err 208 } 209 if err := bus.SubscribeAsync(pingpongEvent.AppTopicEarningsChanged, k.consumeEarningsChangedEvent); err != nil { 210 return err 211 } 212 return nil 213 } 214 215 func (k *Keeper) announceState(_ interface{}) { 216 var state stateEvent.State 217 func() { 218 k.lock.RLock() 219 defer k.lock.RUnlock() 220 if err := copier.CopyWithOption(&state, *k.state, copier.Option{DeepCopy: true}); err != nil { 221 panic(err) 222 } 223 }() 224 k.deps.Publisher.Publish(stateEvent.AppTopicState, state) 225 } 226 227 func (k *Keeper) updateServiceState(_ interface{}) { 228 k.lock.Lock() 229 defer k.lock.Unlock() 230 k.updateServices() 231 go k.announceStateChanges(nil) 232 } 233 234 func (k *Keeper) updateServices() { 235 services := k.deps.ServiceLister.List(false) 236 result := make([]contract.ServiceInfoDTO, len(services)) 237 238 i := 0 239 for _, v := range services { 240 // merge in the connection statistics 241 match, _ := k.getServiceByID(string(v.ID)) 242 243 proposal := v.CopyProposal() 244 priced, err := k.deps.ProposalPricer.EnrichProposalWithPrice(proposal) 245 if err != nil { 246 log.Warn().Msgf("could not load price for proposal %v(%v)", proposal.ProviderID, proposal.ServiceType) 247 } 248 249 prop := contract.NewProposalDTO(priced) 250 if match.ConnectionStatistics == nil { 251 match.ConnectionStatistics = &contract.ServiceStatisticsDTO{} 252 } 253 result[i] = contract.ServiceInfoDTO{ 254 ID: string(v.ID), 255 ProviderID: v.ProviderID.Address, 256 Type: v.Type, 257 Options: v.Options, 258 Status: string(v.State()), 259 Proposal: &prop, 260 ConnectionStatistics: match.ConnectionStatistics, 261 } 262 i++ 263 } 264 265 k.state.Services = result 266 } 267 268 func (k *Keeper) getServiceByID(id string) (se contract.ServiceInfoDTO, found bool) { 269 for i := range k.state.Services { 270 if k.state.Services[i].ID == id { 271 se = k.state.Services[i] 272 found = true 273 return 274 } 275 } 276 return 277 } 278 279 // consumeServiceSessionEvent consumes the session change events 280 func (k *Keeper) consumeServiceSessionEvent(e sessionEvent.AppEventSession) { 281 k.lock.Lock() 282 defer k.lock.Unlock() 283 284 switch e.Status { 285 case sessionEvent.CreatedStatus: 286 k.addSession(e) 287 k.incrementConnectCount(e.Service.ID, false) 288 case sessionEvent.RemovedStatus: 289 k.removeSession(e) 290 case sessionEvent.AcknowledgedStatus: 291 k.incrementConnectCount(e.Service.ID, true) 292 } 293 294 go k.announceStateChanges(nil) 295 } 296 297 func (k *Keeper) addSession(e sessionEvent.AppEventSession) { 298 k.state.Sessions = append(k.state.Sessions, session.History{ 299 SessionID: nodeSession.ID(e.Session.ID), 300 Direction: session.DirectionProvided, 301 ConsumerID: e.Session.ConsumerID, 302 HermesID: e.Session.HermesID.Hex(), 303 ProviderID: identity.FromAddress(e.Session.Proposal.ProviderID), 304 ServiceType: e.Session.Proposal.ServiceType, 305 ConsumerCountry: e.Session.ConsumerLocation.Country, 306 ProviderCountry: e.Session.Proposal.Location.Country, 307 Started: e.Session.StartedAt, 308 Status: session.StatusNew, 309 Tokens: big.NewInt(0), 310 }) 311 } 312 313 func (k *Keeper) removeSession(e sessionEvent.AppEventSession) { 314 found := false 315 for i := range k.state.Sessions { 316 if string(k.state.Sessions[i].SessionID) == e.Session.ID { 317 k.state.Sessions = append(k.state.Sessions[:i], k.state.Sessions[i+1:]...) 318 found = true 319 break 320 } 321 } 322 if !found { 323 log.Warn().Msgf("Couldn't find a matching session for session remove: %s", e.Session.ID) 324 } 325 } 326 327 // updates the data transfer info on the session 328 func (k *Keeper) updateSessionStats(e interface{}) { 329 k.lock.Lock() 330 defer k.lock.Unlock() 331 332 evt, ok := e.(sessionEvent.AppEventDataTransferred) 333 if !ok { 334 log.Warn().Msg("Received a wrong kind of event for session state update") 335 return 336 } 337 338 var session *session.History 339 for i := range k.state.Sessions { 340 if string(k.state.Sessions[i].SessionID) == evt.ID { 341 session = &k.state.Sessions[i] 342 } 343 } 344 if session == nil { 345 log.Warn().Msgf("Couldn't find a matching session for data transferred change: %+v", evt) 346 return 347 } 348 349 // From a server perspective, bytes up are the actual bytes the client downloaded(aka the bytes we pushed to the consumer) 350 // To lessen the confusion, I suggest having the bytes reversed on the session instance. 351 // This way, the session will show that it downloaded the bytes in a manner that is easier to comprehend. 352 session.DataReceived = evt.Up 353 session.DataSent = evt.Down 354 go k.announceStateChanges(nil) 355 } 356 357 // updates total tokens earned during the session. 358 func (k *Keeper) updateSessionEarnings(e interface{}) { 359 k.lock.Lock() 360 defer k.lock.Unlock() 361 362 evt, ok := e.(sessionEvent.AppEventTokensEarned) 363 if !ok { 364 log.Warn().Msg("Received a wrong kind of event for connection state update") 365 return 366 } 367 368 var session *session.History 369 for i := range k.state.Sessions { 370 if string(k.state.Sessions[i].SessionID) == evt.SessionID { 371 session = &k.state.Sessions[i] 372 } 373 } 374 if session == nil { 375 log.Warn().Msgf("Couldn't find a matching session for earnings change: %s", evt.SessionID) 376 return 377 } 378 379 session.Tokens = evt.Total 380 go k.announceStateChanges(nil) 381 } 382 383 func (k *Keeper) consumeConnectionStateEvent(e interface{}) { 384 k.lock.Lock() 385 defer k.lock.Unlock() 386 evt, ok := e.(connectionstate.AppEventConnectionState) 387 if !ok { 388 log.Warn().Msg("Received a wrong kind of event for connection state update") 389 return 390 } 391 392 if evt.State == connectionstate.NotConnected { 393 delete(k.state.Connections, evt.UUID) 394 } else { 395 conn := k.state.Connections[evt.UUID] 396 conn.Session = evt.SessionInfo 397 k.state.Connections[evt.UUID] = conn 398 399 log.Info().Msgf("Session %s", conn.String()) 400 } 401 402 go k.announceStateChanges(nil) 403 } 404 405 func (k *Keeper) updateConnectionStats(e interface{}) { 406 k.lock.Lock() 407 defer k.lock.Unlock() 408 evt, ok := e.(connectionstate.AppEventConnectionStatistics) 409 if !ok { 410 log.Warn().Msg("Received a wrong kind of event for connection state update") 411 return 412 } 413 414 conn := k.state.Connections[string(evt.UUID)] 415 conn.Statistics = evt.Stats 416 k.state.Connections[evt.UUID] = conn 417 418 go k.announceStateChanges(nil) 419 } 420 421 func (k *Keeper) updateConnectionThroughput(e interface{}) { 422 k.lock.Lock() 423 defer k.lock.Unlock() 424 evt, ok := e.(bandwidth.AppEventConnectionThroughput) 425 if !ok { 426 log.Warn().Msg("Received a wrong kind of event for connection state update") 427 return 428 } 429 430 conn := k.state.Connections[evt.UUID] 431 conn.Throughput = evt.Throughput 432 k.state.Connections[evt.UUID] = conn 433 434 go k.announceStateChanges(nil) 435 } 436 437 func (k *Keeper) updateConnectionSpending(e interface{}) { 438 k.lock.Lock() 439 defer k.lock.Unlock() 440 evt, ok := e.(pingpongEvent.AppEventInvoicePaid) 441 if !ok { 442 log.Warn().Msg("Received a wrong kind of event for connection state update") 443 return 444 } 445 446 conn := k.state.Connections[evt.UUID] 447 conn.Invoice = evt.Invoice 448 k.state.Connections[evt.UUID] = conn 449 450 log.Info().Msgf("Session %s", conn.String()) 451 452 go k.announceStateChanges(nil) 453 } 454 455 func (k *Keeper) consumeBalanceChangedEvent(e interface{}) { 456 k.lock.Lock() 457 defer k.lock.Unlock() 458 evt, ok := e.(pingpongEvent.AppEventBalanceChanged) 459 if !ok { 460 log.Warn().Msg("Received a wrong kind of event for balance change") 461 return 462 } 463 var id *stateEvent.Identity 464 for i := range k.state.Identities { 465 if k.state.Identities[i].Address == evt.Identity.Address { 466 id = &k.state.Identities[i] 467 break 468 } 469 } 470 if id == nil { 471 log.Warn().Msgf("Couldn't find a matching identity for balance change: %s", evt.Identity.Address) 472 return 473 } 474 id.Balance = evt.Current 475 go k.announceStateChanges(nil) 476 } 477 478 func (k *Keeper) consumeEarningsChangedEvent(e interface{}) { 479 k.lock.Lock() 480 defer k.lock.Unlock() 481 evt, ok := e.(pingpongEvent.AppEventEarningsChanged) 482 if !ok { 483 log.Warn().Msg("Received a wrong kind of event for earnings change") 484 return 485 } 486 487 k.state.ProviderChannels = k.deps.EarningsProvider.List(k.deps.ChainID) 488 489 var id *stateEvent.Identity 490 for i := range k.state.Identities { 491 if k.state.Identities[i].Address == evt.Identity.Address { 492 id = &k.state.Identities[i] 493 break 494 } 495 } 496 if id == nil { 497 log.Warn().Msgf("Couldn't find a matching identity for earnings change: %s", evt.Identity.Address) 498 return 499 } 500 501 id.Earnings = evt.Current.Total.UnsettledBalance 502 id.EarningsTotal = evt.Current.Total.LifetimeBalance 503 id.EarningsPerHermes = evt.Current.PerHermes 504 505 go k.announceStateChanges(nil) 506 } 507 508 func (k *Keeper) consumeIdentityCreatedEvent(_ interface{}) { 509 k.lock.Lock() 510 defer k.lock.Unlock() 511 k.state.Identities = k.fetchIdentities() 512 go k.announceStateChanges(nil) 513 } 514 515 func (k *Keeper) consumeIdentityRegistrationEvent(e interface{}) { 516 k.lock.Lock() 517 defer k.lock.Unlock() 518 evt, ok := e.(registry.AppEventIdentityRegistration) 519 if !ok { 520 log.Warn().Msg("Received a wrong kind of event for identity registration") 521 } 522 var id *stateEvent.Identity 523 for i := range k.state.Identities { 524 if k.state.Identities[i].Address == evt.ID.Address { 525 id = &k.state.Identities[i] 526 break 527 } 528 } 529 if id == nil { 530 log.Warn().Msgf("Couldn't find a matching identity for balance change: %s", evt.ID.Address) 531 return 532 } 533 id.RegistrationStatus = evt.Status 534 go k.announceStateChanges(nil) 535 } 536 537 func (k *Keeper) incrementConnectCount(serviceID string, isSuccess bool) { 538 for i := range k.state.Services { 539 if k.state.Services[i].ID == serviceID { 540 if isSuccess { 541 k.state.Services[i].ConnectionStatistics.Successful++ 542 } else { 543 k.state.Services[i].ConnectionStatistics.Attempted++ 544 } 545 break 546 } 547 } 548 } 549 550 // GetState returns the current state 551 func (k *Keeper) GetState() (res stateEvent.State) { 552 k.lock.RLock() 553 defer k.lock.RUnlock() 554 555 if err := copier.CopyWithOption(&res, *k.state, copier.Option{DeepCopy: true}); err != nil { 556 panic(err) 557 } 558 return 559 } 560 561 // GetConnection returns the connection state. 562 func (k *Keeper) GetConnection(id string) (state stateEvent.Connection) { 563 k.lock.RLock() 564 defer k.lock.RUnlock() 565 566 for _, state := range k.state.Connections { 567 if len(id) == 0 || id == string(state.Session.SessionID) { 568 return state 569 } 570 } 571 572 state.Session.State = connectionstate.NotConnected 573 574 return state 575 } 576 577 // Debounce takes in the f and makes sure that it only gets called once if multiple calls are executed in the given interval d. 578 // It returns the debounced instance of the function. 579 func debounce(f func(interface{}), d time.Duration) func(interface{}) { 580 incoming := make(chan interface{}) 581 582 go func() { 583 var e interface{} 584 585 t := time.NewTimer(d) 586 t.Stop() 587 588 for { 589 select { 590 case e = <-incoming: 591 t.Reset(d) 592 case <-t.C: 593 go f(e) 594 } 595 } 596 }() 597 598 return func(e interface{}) { 599 go func() { incoming <- e }() 600 } 601 }