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) }