github.com/ethersphere/bee/v2@v2.2.0/pkg/accounting/mock/accounting.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package mock provides a mock implementation for the 6 // accounting interface. 7 package mock 8 9 import ( 10 "context" 11 "math/big" 12 "sync" 13 14 "github.com/ethersphere/bee/v2/pkg/accounting" 15 "github.com/ethersphere/bee/v2/pkg/swarm" 16 ) 17 18 // Service is the mock Accounting service. 19 type Service struct { 20 lock sync.Mutex 21 balances map[string]*big.Int 22 prepareDebitFunc func(peer swarm.Address, price uint64) (accounting.Action, error) 23 prepareCreditFunc func(peer swarm.Address, price uint64, originated bool) (accounting.Action, error) 24 balanceFunc func(swarm.Address) (*big.Int, error) 25 shadowBalanceFunc func(swarm.Address) (*big.Int, error) 26 balancesFunc func() (map[string]*big.Int, error) 27 compensatedBalanceFunc func(swarm.Address) (*big.Int, error) 28 compensatedBalancesFunc func() (map[string]*big.Int, error) 29 peerAccountingFunc func() (map[string]accounting.PeerInfo, error) 30 balanceSurplusFunc func(swarm.Address) (*big.Int, error) 31 } 32 33 type debitAction struct { 34 accounting *Service 35 price *big.Int 36 peer swarm.Address 37 applied bool 38 } 39 40 type creditAction struct { 41 accounting *Service 42 price *big.Int 43 peer swarm.Address 44 applied bool 45 } 46 47 // WithDebitFunc sets the mock Debit function 48 func WithPrepareDebitFunc(f func(peer swarm.Address, price uint64) (accounting.Action, error)) Option { 49 return optionFunc(func(s *Service) { 50 s.prepareDebitFunc = f 51 }) 52 } 53 54 // WithDebitFunc sets the mock Debit function 55 func WithPrepareCreditFunc(f func(peer swarm.Address, price uint64, originated bool) (accounting.Action, error)) Option { 56 return optionFunc(func(s *Service) { 57 s.prepareCreditFunc = f 58 }) 59 } 60 61 // WithBalanceFunc sets the mock Balance function 62 func WithBalanceFunc(f func(swarm.Address) (*big.Int, error)) Option { 63 return optionFunc(func(s *Service) { 64 s.balanceFunc = f 65 }) 66 } 67 68 // WithBalancesFunc sets the mock Balances function 69 func WithBalancesFunc(f func() (map[string]*big.Int, error)) Option { 70 return optionFunc(func(s *Service) { 71 s.balancesFunc = f 72 }) 73 } 74 75 // WithCompensatedBalanceFunc sets the mock Balance function 76 func WithCompensatedBalanceFunc(f func(swarm.Address) (*big.Int, error)) Option { 77 return optionFunc(func(s *Service) { 78 s.compensatedBalanceFunc = f 79 }) 80 } 81 82 // WithCompensatedBalancesFunc sets the mock Balances function 83 func WithCompensatedBalancesFunc(f func() (map[string]*big.Int, error)) Option { 84 return optionFunc(func(s *Service) { 85 s.compensatedBalancesFunc = f 86 }) 87 } 88 89 // WithBalanceSurplusFunc sets the mock SurplusBalance function 90 func WithBalanceSurplusFunc(f func(swarm.Address) (*big.Int, error)) Option { 91 return optionFunc(func(s *Service) { 92 s.balanceSurplusFunc = f 93 }) 94 } 95 96 func WithPeerAccountingFunc(f func() (map[string]accounting.PeerInfo, error)) Option { 97 return optionFunc(func(s *Service) { 98 s.peerAccountingFunc = f 99 }) 100 } 101 102 // NewAccounting creates the mock accounting implementation 103 func NewAccounting(opts ...Option) *Service { 104 mock := new(Service) 105 mock.balances = make(map[string]*big.Int) 106 for _, o := range opts { 107 o.apply(mock) 108 } 109 return mock 110 } 111 112 func (s *Service) MakeCreditAction(peer swarm.Address, price uint64) accounting.Action { 113 return &creditAction{ 114 accounting: s, 115 price: new(big.Int).SetUint64(price), 116 peer: peer, 117 applied: false, 118 } 119 } 120 121 // Debit is the mock function wrapper that calls the set implementation 122 func (s *Service) PrepareDebit(_ context.Context, peer swarm.Address, price uint64) (accounting.Action, error) { 123 if s.prepareDebitFunc != nil { 124 return s.prepareDebitFunc(peer, price) 125 } 126 127 bigPrice := new(big.Int).SetUint64(price) 128 return &debitAction{ 129 accounting: s, 130 price: bigPrice, 131 peer: peer, 132 applied: false, 133 }, nil 134 } 135 136 func (s *Service) PrepareCredit(_ context.Context, peer swarm.Address, price uint64, originated bool) (accounting.Action, error) { 137 if s.prepareCreditFunc != nil { 138 return s.prepareCreditFunc(peer, price, originated) 139 } 140 141 return s.MakeCreditAction(peer, price), nil 142 } 143 144 func (a *debitAction) Apply() error { 145 a.accounting.lock.Lock() 146 defer a.accounting.lock.Unlock() 147 148 if bal, ok := a.accounting.balances[a.peer.String()]; ok { 149 a.accounting.balances[a.peer.String()] = new(big.Int).Add(bal, new(big.Int).Set(a.price)) 150 } else { 151 a.accounting.balances[a.peer.String()] = new(big.Int).Set(a.price) 152 } 153 154 return nil 155 } 156 157 func (a *creditAction) Cleanup() {} 158 159 func (a *creditAction) Apply() error { 160 a.accounting.lock.Lock() 161 defer a.accounting.lock.Unlock() 162 163 if bal, ok := a.accounting.balances[a.peer.String()]; ok { 164 a.accounting.balances[a.peer.String()] = new(big.Int).Sub(bal, new(big.Int).Set(a.price)) 165 } else { 166 a.accounting.balances[a.peer.String()] = new(big.Int).Neg(a.price) 167 } 168 169 return nil 170 } 171 172 func (a *debitAction) Cleanup() {} 173 174 // Balance is the mock function wrapper that calls the set implementation 175 func (s *Service) Balance(peer swarm.Address) (*big.Int, error) { 176 if s.balanceFunc != nil { 177 return s.balanceFunc(peer) 178 } 179 s.lock.Lock() 180 defer s.lock.Unlock() 181 if bal, ok := s.balances[peer.String()]; ok { 182 return bal, nil 183 } else { 184 return big.NewInt(0), nil 185 } 186 } 187 188 func (s *Service) ShadowBalance(peer swarm.Address) (*big.Int, error) { 189 if s.shadowBalanceFunc != nil { 190 return s.shadowBalanceFunc(peer) 191 } 192 s.lock.Lock() 193 defer s.lock.Unlock() 194 if bal, ok := s.balances[peer.String()]; ok { 195 return new(big.Int).Neg(bal), nil 196 } else { 197 return big.NewInt(0), nil 198 } 199 } 200 201 // Balances is the mock function wrapper that calls the set implementation 202 func (s *Service) Balances() (map[string]*big.Int, error) { 203 if s.balancesFunc != nil { 204 return s.balancesFunc() 205 } 206 return s.balances, nil 207 } 208 209 // CompensatedBalance is the mock function wrapper that calls the set implementation 210 func (s *Service) CompensatedBalance(peer swarm.Address) (*big.Int, error) { 211 if s.compensatedBalanceFunc != nil { 212 return s.compensatedBalanceFunc(peer) 213 } 214 s.lock.Lock() 215 defer s.lock.Unlock() 216 return s.balances[peer.String()], nil 217 } 218 219 // CompensatedBalances is the mock function wrapper that calls the set implementation 220 func (s *Service) CompensatedBalances() (map[string]*big.Int, error) { 221 if s.compensatedBalancesFunc != nil { 222 return s.compensatedBalancesFunc() 223 } 224 return s.balances, nil 225 } 226 227 func (s *Service) PeerAccounting() (map[string]accounting.PeerInfo, error) { 228 if s.peerAccountingFunc != nil { 229 return s.peerAccountingFunc() 230 } 231 return map[string]accounting.PeerInfo{}, nil 232 } 233 234 func (s *Service) Connect(peer swarm.Address, full bool) { 235 236 } 237 238 func (s *Service) Disconnect(peer swarm.Address) { 239 240 } 241 242 func (s *Service) SurplusBalance(peer swarm.Address) (*big.Int, error) { 243 if s.balanceFunc != nil { 244 return s.balanceSurplusFunc(peer) 245 } 246 s.lock.Lock() 247 defer s.lock.Unlock() 248 return big.NewInt(0), nil 249 } 250 251 // Option is the option passed to the mock accounting service 252 type Option interface { 253 apply(*Service) 254 } 255 256 type optionFunc func(*Service) 257 258 func (f optionFunc) apply(r *Service) { f(r) }