github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/session/pingpong/consumer_totals_storage.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 pingpong
    19  
    20  import (
    21  	"fmt"
    22  	"math/big"
    23  	"sync"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/rs/zerolog/log"
    27  
    28  	"github.com/mysteriumnetwork/node/eventbus"
    29  	"github.com/mysteriumnetwork/node/identity"
    30  	"github.com/mysteriumnetwork/node/session/pingpong/event"
    31  )
    32  
    33  // ConsumerTotalsStorage allows to store total promised amounts for each channel.
    34  type ConsumerTotalsStorage struct {
    35  	createLock sync.RWMutex
    36  	bus        eventbus.Publisher
    37  	data       map[string]*ConsumerTotalElement
    38  }
    39  
    40  // ConsumerTotalElement stores a grand total promised amount for a single identity, hermes and chain id
    41  type ConsumerTotalElement struct {
    42  	lock   sync.RWMutex
    43  	amount *big.Int
    44  }
    45  
    46  // NewConsumerTotalsStorage creates a new instance of consumer totals storage.
    47  func NewConsumerTotalsStorage(bus eventbus.Publisher) *ConsumerTotalsStorage {
    48  	return &ConsumerTotalsStorage{
    49  		bus:  bus,
    50  		data: make(map[string]*ConsumerTotalElement),
    51  	}
    52  }
    53  
    54  // Store stores the given amount as promised for the given channel.
    55  func (cts *ConsumerTotalsStorage) Store(chainID int64, id identity.Identity, hermesID common.Address, amount *big.Int) error {
    56  	cts.createLock.Lock()
    57  	defer cts.createLock.Unlock()
    58  
    59  	key := cts.makeKey(chainID, id, hermesID)
    60  	_, ok := cts.data[key]
    61  	if !ok {
    62  		_, ok := cts.data[key]
    63  		if !ok {
    64  			cts.data[key] = &ConsumerTotalElement{
    65  				amount: nil,
    66  			}
    67  		}
    68  	}
    69  	element, ok := cts.data[key]
    70  	if !ok {
    71  		return fmt.Errorf("key was not created properly")
    72  	}
    73  	element.lock.Lock()
    74  	defer element.lock.Unlock()
    75  	if element.amount != nil && element.amount.Cmp(amount) == 1 {
    76  		log.Warn().Fields(map[string]interface{}{
    77  			"old_value": element.amount.String(),
    78  			"new_value": amount.String(),
    79  			"identity":  id.Address,
    80  			"chain_id":  chainID,
    81  			"hermes_id": hermesID.Hex(),
    82  		}).Msg("tried to save a lower grand total amount")
    83  		return nil
    84  	}
    85  	element.amount = amount
    86  
    87  	go cts.bus.Publish(event.AppTopicGrandTotalChanged, event.AppEventGrandTotalChanged{
    88  		ChainID:    chainID,
    89  		Current:    amount,
    90  		HermesID:   hermesID,
    91  		ConsumerID: id,
    92  	})
    93  	return nil
    94  }
    95  
    96  // Get fetches the amount as promised for the given channel.
    97  func (cts *ConsumerTotalsStorage) Get(chainID int64, id identity.Identity, hermesID common.Address) (*big.Int, error) {
    98  	key := cts.makeKey(chainID, id, hermesID)
    99  	cts.createLock.RLock()
   100  	defer cts.createLock.RUnlock()
   101  
   102  	element, ok := cts.data[key]
   103  	if !ok {
   104  		return nil, ErrNotFound
   105  	}
   106  	element.lock.RLock()
   107  	defer element.lock.RUnlock()
   108  	res := element.amount
   109  	if res == nil {
   110  		return nil, ErrNotFound
   111  	}
   112  	return res, nil
   113  }
   114  
   115  // Add adds the given amount as promised for the given channel.
   116  func (cts *ConsumerTotalsStorage) Add(chainID int64, id identity.Identity, hermesID common.Address, amount *big.Int) error {
   117  	cts.createLock.Lock()
   118  	defer cts.createLock.Unlock()
   119  
   120  	key := cts.makeKey(chainID, id, hermesID)
   121  	_, ok := cts.data[key]
   122  	if !ok {
   123  		_, ok := cts.data[key]
   124  		if !ok {
   125  			cts.data[key] = &ConsumerTotalElement{
   126  				amount: nil,
   127  			}
   128  		}
   129  	}
   130  	element, ok := cts.data[key]
   131  	if !ok {
   132  		return fmt.Errorf("key was not created properly")
   133  	}
   134  	element.lock.Lock()
   135  	defer element.lock.Unlock()
   136  	oldAmount := element.amount
   137  	if oldAmount == nil {
   138  		oldAmount = big.NewInt(0)
   139  	}
   140  	newAmount := new(big.Int).Add(oldAmount, amount)
   141  	element.amount = newAmount
   142  
   143  	go cts.bus.Publish(event.AppTopicGrandTotalChanged, event.AppEventGrandTotalChanged{
   144  		ChainID:    chainID,
   145  		Current:    newAmount,
   146  		HermesID:   hermesID,
   147  		ConsumerID: id,
   148  	})
   149  	return nil
   150  }
   151  
   152  func (cts *ConsumerTotalsStorage) makeKey(chainID int64, id identity.Identity, hermesID common.Address) string {
   153  	return fmt.Sprintf("%d%s%s", chainID, id.Address, hermesID.Hex())
   154  }