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 }