github.com/ethersphere/bee/v2@v2.2.0/pkg/accounting/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 accounting provides functionalities needed
     6  // to do per-peer accounting.
     7  package accounting
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"fmt"
    13  	"math/big"
    14  	"strings"
    15  	"sync"
    16  	"time"
    17  
    18  	"github.com/ethersphere/bee/v2/pkg/log"
    19  	"github.com/ethersphere/bee/v2/pkg/p2p"
    20  	"github.com/ethersphere/bee/v2/pkg/pricing"
    21  	"github.com/ethersphere/bee/v2/pkg/settlement/pseudosettle"
    22  	"github.com/ethersphere/bee/v2/pkg/storage"
    23  	"github.com/ethersphere/bee/v2/pkg/swarm"
    24  )
    25  
    26  // loggerName is the tree path name of the logger for this package.
    27  const loggerName = "accounting"
    28  
    29  const (
    30  	linearCheckpointNumber = 1800
    31  	linearCheckpointStep   = 100
    32  )
    33  
    34  var (
    35  	_                        Interface = (*Accounting)(nil)
    36  	balancesPrefix                     = "accounting_balance_"
    37  	balancesSurplusPrefix              = "accounting_surplusbalance_"
    38  	balancesOriginatedPrefix           = "accounting_originatedbalance_"
    39  	// fraction of the refresh rate that is the minimum for monetary settlement
    40  	// this value is chosen so that tiny payments are prevented while still allowing small payments in environments with lower payment thresholds
    41  	minimumPaymentDivisor    = int64(5)
    42  	failedSettlementInterval = int64(10) // seconds
    43  )
    44  
    45  // Interface is the Accounting interface.
    46  type Interface interface {
    47  	// PrepareCredit action to prevent overspending in case of concurrent requests.
    48  	PrepareCredit(ctx context.Context, peer swarm.Address, price uint64, originated bool) (Action, error)
    49  	// PrepareDebit returns an accounting Action for the later debit to be executed on and to implement shadowing a possibly credited part of reserve on the other side.
    50  	PrepareDebit(ctx context.Context, peer swarm.Address, price uint64) (Action, error)
    51  	// Balance returns the current balance for the given peer.
    52  	Balance(peer swarm.Address) (*big.Int, error)
    53  	// SurplusBalance returns the current surplus balance for the given peer.
    54  	SurplusBalance(peer swarm.Address) (*big.Int, error)
    55  	// Balances returns balances for all known peers.
    56  	Balances() (map[string]*big.Int, error)
    57  	// CompensatedBalance returns the current balance deducted by current surplus balance for the given peer.
    58  	CompensatedBalance(peer swarm.Address) (*big.Int, error)
    59  	// CompensatedBalances returns the compensated balances for all known peers.
    60  	CompensatedBalances() (map[string]*big.Int, error)
    61  	// PeerAccounting returns the associated values for all known peers
    62  	PeerAccounting() (map[string]PeerInfo, error)
    63  }
    64  
    65  // Action represents an accounting action that can be applied
    66  type Action interface {
    67  	// Cleanup cleans up an action. Must be called whether it was applied or not.
    68  	Cleanup()
    69  	// Apply applies an action
    70  	Apply() error
    71  }
    72  
    73  // debitAction represents a future debit
    74  type debitAction struct {
    75  	accounting     *Accounting
    76  	price          *big.Int
    77  	peer           swarm.Address
    78  	accountingPeer *accountingPeer
    79  	applied        bool
    80  }
    81  
    82  // creditAction represents a future debit
    83  type creditAction struct {
    84  	accounting     *Accounting
    85  	price          *big.Int
    86  	peer           swarm.Address
    87  	accountingPeer *accountingPeer
    88  	originated     bool
    89  	applied        bool
    90  }
    91  
    92  // PayFunc is the function used for async monetary settlement
    93  type PayFunc func(context.Context, swarm.Address, *big.Int)
    94  
    95  // RefreshFunc is the function used for sync time-based settlement
    96  type RefreshFunc func(context.Context, swarm.Address, *big.Int)
    97  
    98  // Mutex is a drop in replacement for the sync.Mutex
    99  // it will not lock if the context is expired
   100  type Mutex struct {
   101  	mu chan struct{}
   102  }
   103  
   104  func NewMutex() *Mutex {
   105  	return &Mutex{
   106  		mu: make(chan struct{}, 1), // unlocked by default
   107  	}
   108  }
   109  
   110  var ErrFailToLock = errors.New("failed to lock")
   111  
   112  func (m *Mutex) TryLock(ctx context.Context) error {
   113  	select {
   114  	case m.mu <- struct{}{}:
   115  		return nil // locked
   116  	case <-ctx.Done():
   117  		return fmt.Errorf("%w: %w", ctx.Err(), ErrFailToLock)
   118  	}
   119  }
   120  
   121  func (m *Mutex) Lock() {
   122  	m.mu <- struct{}{}
   123  }
   124  
   125  func (m *Mutex) Unlock() {
   126  	<-m.mu
   127  }
   128  
   129  // accountingPeer holds all in-memory accounting information for one peer.
   130  type accountingPeer struct {
   131  	lock                           *Mutex   // lock to be held during any accounting action for this peer
   132  	reservedBalance                *big.Int // amount currently reserved for active peer interaction
   133  	shadowReservedBalance          *big.Int // amount potentially to be debited for active peer interaction
   134  	refreshReservedBalance         *big.Int // amount debt potentially decreased during an ongoing refreshment
   135  	ghostBalance                   *big.Int // amount potentially could have been debited for but was not
   136  	paymentThreshold               *big.Int // the threshold at which the peer expects us to pay
   137  	earlyPayment                   *big.Int // individual early payment threshold calculated from from payment threshold and early payment percentage
   138  	paymentThresholdForPeer        *big.Int // individual payment threshold at which the peer is expected to pay
   139  	disconnectLimit                *big.Int // individual disconnect threshold calculated from tolerance and payment threshold for peer
   140  	refreshTimestampMilliseconds   int64    // last time we attempted and succeeded time-based settlement
   141  	refreshReceivedTimestamp       int64    // last time we accepted time-based settlement
   142  	paymentOngoing                 bool     // indicate if we are currently settling with the peer
   143  	refreshOngoing                 bool     // indicates if we are currently refreshing with the peer
   144  	lastSettlementFailureTimestamp int64    // time of last unsuccessful attempt to issue a cheque
   145  	connected                      bool     // indicates whether the peer is currently connected
   146  	fullNode                       bool     // the peer connected as full node or light node
   147  	totalDebtRepay                 *big.Int // since being connected, amount of cumulative debt settled by the peer
   148  	thresholdGrowAt                *big.Int // cumulative debt to be settled by the peer in order to give threshold upgrade
   149  }
   150  
   151  // Accounting is the main implementation of the accounting interface.
   152  type Accounting struct {
   153  	// Mutex for accessing the accountingPeers map.
   154  	accountingPeersMu sync.Mutex
   155  	accountingPeers   map[string]*accountingPeer
   156  	logger            log.Logger
   157  	store             storage.StateStorer
   158  	// The payment threshold in BZZ we communicate to our peers.
   159  	paymentThreshold *big.Int
   160  	// The amount in percent we let peers exceed the payment threshold before we
   161  	// disconnect them.
   162  	paymentTolerance int64
   163  	// Start settling when reserve plus debt reaches this close to threshold in percent.
   164  	earlyPayment int64
   165  	// Limit to disconnect peer after going in debt over
   166  	disconnectLimit *big.Int
   167  	// function used for monetary settlement
   168  	payFunction PayFunc
   169  	// function used for time settlement
   170  	refreshFunction RefreshFunc
   171  	// allowance based on time used in pseudo settle
   172  	refreshRate      *big.Int
   173  	lightRefreshRate *big.Int
   174  	// lower bound for the value of issued cheques
   175  	minimumPayment      *big.Int
   176  	pricing             pricing.Interface
   177  	metrics             metrics
   178  	wg                  sync.WaitGroup
   179  	p2p                 p2p.Service
   180  	timeNow             func() time.Time
   181  	thresholdGrowStep   *big.Int
   182  	thresholdGrowChange *big.Int
   183  	// light node counterparts
   184  	lightPaymentThreshold    *big.Int
   185  	lightDisconnectLimit     *big.Int
   186  	lightThresholdGrowStep   *big.Int
   187  	lightThresholdGrowChange *big.Int
   188  }
   189  
   190  var (
   191  	// ErrOverdraft denotes the expected debt in Reserve would exceed the payment thresholds.
   192  	ErrOverdraft = errors.New("attempted overdraft")
   193  	// ErrDisconnectThresholdExceeded denotes a peer has exceeded the disconnect threshold.
   194  	ErrDisconnectThresholdExceeded = errors.New("disconnect threshold exceeded")
   195  	// ErrPeerNoBalance is the error returned if no balance in store exists for a peer
   196  	ErrPeerNoBalance = errors.New("no balance for peer")
   197  	// ErrInvalidValue denotes an invalid value read from store
   198  	ErrInvalidValue = errors.New("invalid value")
   199  	// ErrOverRelease
   200  	ErrOverRelease = errors.New("attempting to release more balance than was reserved for peer")
   201  	// ErrEnforceRefresh
   202  	ErrEnforceRefresh = errors.New("allowance expectation refused")
   203  )
   204  
   205  // NewAccounting creates a new Accounting instance with the provided options.
   206  func NewAccounting(
   207  	PaymentThreshold *big.Int,
   208  	PaymentTolerance,
   209  	EarlyPayment int64,
   210  	logger log.Logger,
   211  	Store storage.StateStorer,
   212  	Pricing pricing.Interface,
   213  	refreshRate *big.Int,
   214  	lightFactor int64,
   215  	p2pService p2p.Service,
   216  ) (*Accounting, error) {
   217  
   218  	lightPaymentThreshold := new(big.Int).Div(PaymentThreshold, big.NewInt(lightFactor))
   219  	lightRefreshRate := new(big.Int).Div(refreshRate, big.NewInt(lightFactor))
   220  	return &Accounting{
   221  		accountingPeers:          make(map[string]*accountingPeer),
   222  		paymentThreshold:         new(big.Int).Set(PaymentThreshold),
   223  		paymentTolerance:         PaymentTolerance,
   224  		earlyPayment:             EarlyPayment,
   225  		disconnectLimit:          percentOf(100+PaymentTolerance, PaymentThreshold),
   226  		logger:                   logger.WithName(loggerName).Register(),
   227  		store:                    Store,
   228  		pricing:                  Pricing,
   229  		metrics:                  newMetrics(),
   230  		refreshRate:              new(big.Int).Set(refreshRate),
   231  		lightRefreshRate:         new(big.Int).Div(refreshRate, big.NewInt(lightFactor)),
   232  		timeNow:                  time.Now,
   233  		minimumPayment:           new(big.Int).Div(refreshRate, big.NewInt(minimumPaymentDivisor)),
   234  		p2p:                      p2pService,
   235  		thresholdGrowChange:      new(big.Int).Mul(refreshRate, big.NewInt(linearCheckpointNumber)),
   236  		thresholdGrowStep:        new(big.Int).Mul(refreshRate, big.NewInt(linearCheckpointStep)),
   237  		lightPaymentThreshold:    new(big.Int).Set(lightPaymentThreshold),
   238  		lightDisconnectLimit:     percentOf(100+PaymentTolerance, lightPaymentThreshold),
   239  		lightThresholdGrowChange: new(big.Int).Mul(lightRefreshRate, big.NewInt(linearCheckpointNumber)),
   240  		lightThresholdGrowStep:   new(big.Int).Mul(lightRefreshRate, big.NewInt(linearCheckpointStep)),
   241  	}, nil
   242  }
   243  
   244  func (a *Accounting) getIncreasedExpectedDebt(peer swarm.Address, accountingPeer *accountingPeer, bigPrice *big.Int) (*big.Int, *big.Int, error) {
   245  	nextReserved := new(big.Int).Add(accountingPeer.reservedBalance, bigPrice)
   246  
   247  	currentBalance, err := a.Balance(peer)
   248  	if err != nil && !errors.Is(err, ErrPeerNoBalance) {
   249  		return nil, nil, fmt.Errorf("failed to load balance: %w", err)
   250  	}
   251  	currentDebt := new(big.Int).Neg(currentBalance)
   252  	if currentDebt.Cmp(big.NewInt(0)) < 0 {
   253  		currentDebt.SetInt64(0)
   254  	}
   255  
   256  	// debt if all reserved operations are successfully credited excluding debt created by surplus balance
   257  	expectedDebt := new(big.Int).Add(currentDebt, nextReserved)
   258  
   259  	// additionalDebt is debt created by incoming payments which we don't consider debt for monetary settlement purposes
   260  	additionalDebt, err := a.SurplusBalance(peer)
   261  	if err != nil {
   262  		return nil, nil, fmt.Errorf("failed to load surplus balance: %w", err)
   263  	}
   264  
   265  	// debt if all reserved operations are successfully credited including debt created by surplus balance
   266  	return new(big.Int).Add(expectedDebt, additionalDebt), currentBalance, nil
   267  }
   268  
   269  func (a *Accounting) PrepareCredit(ctx context.Context, peer swarm.Address, price uint64, originated bool) (Action, error) {
   270  
   271  	accountingPeer := a.getAccountingPeer(peer)
   272  
   273  	if err := accountingPeer.lock.TryLock(ctx); err != nil {
   274  		a.logger.Debug("failed to acquire lock when preparing credit", "error", err)
   275  		return nil, err
   276  	}
   277  	defer accountingPeer.lock.Unlock()
   278  
   279  	if !accountingPeer.connected {
   280  		return nil, errors.New("connection not initialized yet")
   281  	}
   282  
   283  	a.metrics.AccountingReserveCount.Inc()
   284  	bigPrice := new(big.Int).SetUint64(price)
   285  
   286  	threshold := accountingPeer.earlyPayment
   287  
   288  	// debt if all reserved operations are successfully credited including debt created by surplus balance
   289  	increasedExpectedDebt, currentBalance, err := a.getIncreasedExpectedDebt(peer, accountingPeer, bigPrice)
   290  	if err != nil {
   291  		return nil, err
   292  	}
   293  	// debt if all reserved operations are successfully credited and all shadow reserved operations are debited including debt created by surplus balance
   294  	// in other words this the debt the other node sees if everything pending is successful
   295  	increasedExpectedDebtReduced := new(big.Int).Sub(increasedExpectedDebt, accountingPeer.shadowReservedBalance)
   296  
   297  	// If our expected debt reduced by what could have been credited on the other side already is less than earlyPayment away from our payment threshold
   298  	// and we are actually in debt, trigger settlement.
   299  	// we pay early to avoid needlessly blocking request later when concurrent requests occur and we are already close to the payment threshold.
   300  
   301  	if increasedExpectedDebtReduced.Cmp(threshold) >= 0 && currentBalance.Cmp(big.NewInt(0)) < 0 {
   302  		err = a.settle(peer, accountingPeer)
   303  		if err != nil {
   304  			a.metrics.SettleErrorCount.Inc()
   305  			return nil, fmt.Errorf("failed to settle with peer %v: %w", peer, err)
   306  		}
   307  
   308  		increasedExpectedDebt, _, err = a.getIncreasedExpectedDebt(peer, accountingPeer, bigPrice)
   309  		if err != nil {
   310  			return nil, err
   311  		}
   312  	}
   313  
   314  	timeElapsedInSeconds := (a.timeNow().UnixMilli() - accountingPeer.refreshTimestampMilliseconds) / 1000
   315  	if timeElapsedInSeconds > 1 {
   316  		timeElapsedInSeconds = 1
   317  	}
   318  
   319  	refreshDue := new(big.Int).Mul(big.NewInt(timeElapsedInSeconds), a.refreshRate)
   320  	overdraftLimit := new(big.Int).Add(accountingPeer.paymentThreshold, refreshDue)
   321  
   322  	// if expectedDebt would still exceed the paymentThreshold at this point block this request
   323  	// this can happen if there is a large number of concurrent requests to the same peer
   324  	if increasedExpectedDebt.Cmp(overdraftLimit) > 0 {
   325  		a.metrics.AccountingBlocksCount.Inc()
   326  		return nil, ErrOverdraft
   327  	}
   328  
   329  	accountingPeer.reservedBalance = new(big.Int).Add(accountingPeer.reservedBalance, bigPrice)
   330  	return &creditAction{
   331  		accounting:     a,
   332  		price:          bigPrice,
   333  		peer:           peer,
   334  		accountingPeer: accountingPeer,
   335  		originated:     originated,
   336  	}, nil
   337  }
   338  
   339  func (c *creditAction) Apply() error {
   340  	loggerV2 := c.accounting.logger.V(2).Register()
   341  
   342  	c.accountingPeer.lock.Lock()
   343  	defer c.accountingPeer.lock.Unlock()
   344  
   345  	// debt if all reserved operations are successfully credited including debt created by surplus balance
   346  	increasedExpectedDebt, currentBalance, err := c.accounting.getIncreasedExpectedDebt(c.peer, c.accountingPeer, c.price)
   347  	if err != nil {
   348  		if !errors.Is(err, ErrPeerNoBalance) {
   349  			return fmt.Errorf("failed to load balance: %w", err)
   350  		}
   351  	}
   352  
   353  	// Calculate next balance by decreasing current balance with the price we credit
   354  	nextBalance := new(big.Int).Sub(currentBalance, c.price)
   355  
   356  	loggerV2.Debug("credit action apply", "crediting_peer_address", c.peer, "price", c.price, "new_balance", nextBalance)
   357  
   358  	err = c.accounting.store.Put(peerBalanceKey(c.peer), nextBalance)
   359  	if err != nil {
   360  		return fmt.Errorf("failed to persist balance: %w", err)
   361  	}
   362  
   363  	c.accounting.metrics.TotalCreditedAmount.Add(float64(c.price.Int64()))
   364  	c.accounting.metrics.CreditEventsCount.Inc()
   365  
   366  	if c.price.Cmp(c.accountingPeer.reservedBalance) > 0 {
   367  		c.accounting.logger.Error(nil, "attempting to release more balance than was reserved for peer", "peer_address", c.peer)
   368  		c.accountingPeer.reservedBalance.SetUint64(0)
   369  	} else {
   370  		c.accountingPeer.reservedBalance.Sub(c.accountingPeer.reservedBalance, c.price)
   371  	}
   372  
   373  	c.applied = true
   374  
   375  	if !c.originated {
   376  		// debt if all reserved operations are successfully credited and all shadow reserved operations are debited including debt created by surplus balance
   377  		// in other words this the debt the other node sees if everything pending is successful
   378  		increasedExpectedDebtReduced := new(big.Int).Sub(increasedExpectedDebt, c.accountingPeer.shadowReservedBalance)
   379  		if increasedExpectedDebtReduced.Cmp(c.accountingPeer.earlyPayment) > 0 {
   380  			err = c.accounting.settle(c.peer, c.accountingPeer)
   381  			if err != nil {
   382  				c.accounting.logger.Error(err, "failed to settle with credited peer", "peer_address", c.peer)
   383  			}
   384  		}
   385  
   386  		return nil
   387  	}
   388  
   389  	originBalance, err := c.accounting.OriginatedBalance(c.peer)
   390  	if err != nil && !errors.Is(err, ErrPeerNoBalance) {
   391  		return fmt.Errorf("failed to load originated balance: %w", err)
   392  	}
   393  
   394  	// Calculate next balance by decreasing current balance with the price we credit
   395  	nextOriginBalance := new(big.Int).Sub(originBalance, c.price)
   396  
   397  	loggerV2.Debug("credit action apply", "crediting_peer_address", c.peer, "price", c.price, "new_originated_balance", nextOriginBalance)
   398  
   399  	zero := big.NewInt(0)
   400  	// only consider negative balance for limiting originated balance
   401  	if nextBalance.Cmp(zero) > 0 {
   402  		nextBalance.Set(zero)
   403  	}
   404  
   405  	// If originated balance is more into the negative domain, set it to balance
   406  	if nextOriginBalance.Cmp(nextBalance) < 0 {
   407  		nextOriginBalance.Set(nextBalance)
   408  		loggerV2.Debug("credit action apply; decreasing originated balance", "crediting_peer_address", c.peer, "current_balance", nextOriginBalance)
   409  	}
   410  
   411  	err = c.accounting.store.Put(originatedBalanceKey(c.peer), nextOriginBalance)
   412  	if err != nil {
   413  		return fmt.Errorf("failed to persist originated balance: %w", err)
   414  	}
   415  
   416  	c.accounting.metrics.TotalOriginatedCreditedAmount.Add(float64(c.price.Int64()))
   417  	c.accounting.metrics.OriginatedCreditEventsCount.Inc()
   418  
   419  	// debt if all reserved operations are successfully credited and all shadow reserved operations are debited including debt created by surplus balance
   420  	// in other words this the debt the other node sees if everything pending is successful
   421  	increasedExpectedDebtReduced := new(big.Int).Sub(increasedExpectedDebt, c.accountingPeer.shadowReservedBalance)
   422  	if increasedExpectedDebtReduced.Cmp(c.accountingPeer.earlyPayment) > 0 {
   423  		err = c.accounting.settle(c.peer, c.accountingPeer)
   424  		if err != nil {
   425  			c.accounting.logger.Error(err, "failed to settle with credited peer", "peer_address", c.peer)
   426  		}
   427  	}
   428  
   429  	return nil
   430  }
   431  
   432  func (c *creditAction) Cleanup() {
   433  	if c.applied {
   434  		return
   435  	}
   436  
   437  	c.accountingPeer.lock.Lock()
   438  	defer c.accountingPeer.lock.Unlock()
   439  
   440  	if c.price.Cmp(c.accountingPeer.reservedBalance) > 0 {
   441  		c.accounting.logger.Error(nil, "attempting to release more balance than was reserved for peer", "peer_address", c.peer)
   442  		c.accountingPeer.reservedBalance.SetUint64(0)
   443  	} else {
   444  		c.accountingPeer.reservedBalance.Sub(c.accountingPeer.reservedBalance, c.price)
   445  	}
   446  }
   447  
   448  // Settle all debt with a peer. The lock on the accountingPeer must be held when
   449  // called.
   450  func (a *Accounting) settle(peer swarm.Address, balance *accountingPeer) error {
   451  	now := a.timeNow()
   452  	timeElapsedInMilliseconds := now.UnixMilli() - balance.refreshTimestampMilliseconds
   453  
   454  	// get debt towards peer decreased by any amount that is to be debited soon
   455  	paymentAmount, err := a.shadowBalance(peer, balance)
   456  	if err != nil {
   457  		return err
   458  	}
   459  	// Don't do anything if there is not enough actual debt
   460  	// This might be the case if the peer owes us and the total reserve for a peer exceeds the payment threshold.
   461  	// Minimum amount to trigger settlement for is 1 * refresh rate to avoid ineffective use of refreshments
   462  	if paymentAmount.Cmp(a.refreshRate) >= 0 {
   463  		// Only trigger refreshment if last refreshment finished at least 1000 milliseconds ago
   464  		// This is to avoid a peer refusing refreshment because not enough time passed since last refreshment
   465  		if timeElapsedInMilliseconds > 999 {
   466  			if !balance.refreshOngoing {
   467  				balance.refreshOngoing = true
   468  				go a.refreshFunction(context.Background(), peer, paymentAmount)
   469  			}
   470  		}
   471  
   472  		if a.payFunction != nil && !balance.paymentOngoing {
   473  			// if a settlement failed recently, wait until failedSettlementInterval before trying again
   474  			differenceInSeconds := now.Unix() - balance.lastSettlementFailureTimestamp
   475  			if differenceInSeconds > failedSettlementInterval {
   476  				// if there is no monetary settlement happening, check if there is something to settle
   477  				// compute debt excluding debt created by incoming payments
   478  				originatedBalance, err := a.OriginatedBalance(peer)
   479  				if err != nil {
   480  					if !errors.Is(err, ErrPeerNoBalance) {
   481  						return fmt.Errorf("failed to load originated balance to settle: %w", err)
   482  					}
   483  				}
   484  
   485  				paymentAmount := new(big.Int).Neg(originatedBalance)
   486  
   487  				if paymentAmount.Cmp(a.minimumPayment) >= 0 {
   488  					timeElapsedInSeconds := (a.timeNow().UnixMilli() - balance.refreshTimestampMilliseconds) / 1000
   489  					refreshDue := new(big.Int).Mul(big.NewInt(timeElapsedInSeconds), a.refreshRate)
   490  					currentBalance, err := a.Balance(peer)
   491  					if err != nil && !errors.Is(err, ErrPeerNoBalance) {
   492  						return fmt.Errorf("failed to load balance: %w", err)
   493  					}
   494  
   495  					debt := new(big.Int).Neg(currentBalance)
   496  					decreasedDebt := new(big.Int).Sub(debt, refreshDue)
   497  					expectedDecreasedDebt := new(big.Int).Sub(decreasedDebt, balance.shadowReservedBalance)
   498  
   499  					if paymentAmount.Cmp(expectedDecreasedDebt) > 0 {
   500  						paymentAmount.Set(expectedDecreasedDebt)
   501  					}
   502  
   503  					// if the remaining debt is still larger than some minimum amount, trigger monetary settlement
   504  					if paymentAmount.Cmp(a.minimumPayment) >= 0 {
   505  						balance.paymentOngoing = true
   506  						// add settled amount to shadow reserve before sending it
   507  						balance.shadowReservedBalance.Add(balance.shadowReservedBalance, paymentAmount)
   508  						// if a refreshment is ongoing, add this amount sent to cumulative potential debt decrease during refreshment
   509  						if balance.refreshOngoing {
   510  							balance.refreshReservedBalance = new(big.Int).Add(balance.refreshReservedBalance, paymentAmount)
   511  						}
   512  						a.wg.Add(1)
   513  						go a.payFunction(context.Background(), peer, paymentAmount)
   514  					}
   515  				}
   516  			}
   517  		}
   518  	}
   519  	return nil
   520  }
   521  
   522  // Balance returns the current balance for the given peer.
   523  func (a *Accounting) Balance(peer swarm.Address) (balance *big.Int, err error) {
   524  	err = a.store.Get(peerBalanceKey(peer), &balance)
   525  
   526  	if err != nil {
   527  		if errors.Is(err, storage.ErrNotFound) {
   528  			return big.NewInt(0), ErrPeerNoBalance
   529  		}
   530  		return nil, err
   531  	}
   532  
   533  	return balance, nil
   534  }
   535  
   536  // OriginatedBalance returns the current balance for the given peer.
   537  func (a *Accounting) OriginatedBalance(peer swarm.Address) (balance *big.Int, err error) {
   538  	err = a.store.Get(originatedBalanceKey(peer), &balance)
   539  
   540  	if err != nil {
   541  		if errors.Is(err, storage.ErrNotFound) {
   542  			return big.NewInt(0), ErrPeerNoBalance
   543  		}
   544  		return nil, err
   545  	}
   546  
   547  	return balance, nil
   548  }
   549  
   550  // SurplusBalance returns the current balance for the given peer.
   551  func (a *Accounting) SurplusBalance(peer swarm.Address) (balance *big.Int, err error) {
   552  	err = a.store.Get(peerSurplusBalanceKey(peer), &balance)
   553  
   554  	if err != nil {
   555  		if errors.Is(err, storage.ErrNotFound) {
   556  			return big.NewInt(0), nil
   557  		}
   558  		return nil, err
   559  	}
   560  
   561  	if balance.Cmp(big.NewInt(0)) < 0 {
   562  		return nil, ErrInvalidValue
   563  	}
   564  
   565  	return balance, nil
   566  }
   567  
   568  // CompensatedBalance returns balance decreased by surplus balance
   569  func (a *Accounting) CompensatedBalance(peer swarm.Address) (compensated *big.Int, err error) {
   570  	surplus, err := a.SurplusBalance(peer)
   571  	if err != nil {
   572  		return nil, err
   573  	}
   574  
   575  	balance, err := a.Balance(peer)
   576  	if err != nil {
   577  		if !errors.Is(err, ErrPeerNoBalance) {
   578  			return nil, err
   579  		}
   580  	}
   581  
   582  	// if surplus is 0 and peer has no balance, propagate ErrPeerNoBalance
   583  	if surplus.Cmp(big.NewInt(0)) == 0 && errors.Is(err, ErrPeerNoBalance) {
   584  		return nil, err
   585  	}
   586  	// Compensated balance is balance decreased by surplus balance
   587  	compensated = new(big.Int).Sub(balance, surplus)
   588  
   589  	return compensated, nil
   590  }
   591  
   592  // peerBalanceKey returns the balance storage key for the given peer.
   593  func peerBalanceKey(peer swarm.Address) string {
   594  	return fmt.Sprintf("%s%s", balancesPrefix, peer.String())
   595  }
   596  
   597  // peerSurplusBalanceKey returns the surplus balance storage key for the given peer
   598  func peerSurplusBalanceKey(peer swarm.Address) string {
   599  	return fmt.Sprintf("%s%s", balancesSurplusPrefix, peer.String())
   600  }
   601  
   602  func originatedBalanceKey(peer swarm.Address) string {
   603  	return fmt.Sprintf("%s%s", balancesOriginatedPrefix, peer.String())
   604  }
   605  
   606  // getAccountingPeer returns the accountingPeer for a given swarm address.
   607  // If not found in memory it will initialize it.
   608  func (a *Accounting) getAccountingPeer(peer swarm.Address) *accountingPeer {
   609  	a.accountingPeersMu.Lock()
   610  	defer a.accountingPeersMu.Unlock()
   611  
   612  	peerData, ok := a.accountingPeers[peer.String()]
   613  	if !ok {
   614  		peerData = &accountingPeer{
   615  			lock:                    NewMutex(),
   616  			reservedBalance:         big.NewInt(0),
   617  			refreshReservedBalance:  big.NewInt(0),
   618  			shadowReservedBalance:   big.NewInt(0),
   619  			ghostBalance:            big.NewInt(0),
   620  			totalDebtRepay:          big.NewInt(0),
   621  			paymentThreshold:        new(big.Int).Set(a.paymentThreshold),
   622  			paymentThresholdForPeer: new(big.Int).Set(a.paymentThreshold),
   623  			disconnectLimit:         new(big.Int).Set(a.disconnectLimit),
   624  			thresholdGrowAt:         new(big.Int).Set(a.thresholdGrowStep),
   625  			// initially assume the peer has the same threshold as us
   626  			earlyPayment: percentOf(100-a.earlyPayment, a.paymentThreshold),
   627  			connected:    false,
   628  		}
   629  		a.accountingPeers[peer.String()] = peerData
   630  	}
   631  
   632  	return peerData
   633  }
   634  
   635  // notifyPaymentThresholdUpgrade is used when cumulative debt settled by peer reaches current checkpoint,
   636  // to set the next checkpoint and increase the payment threshold given by 1 * refreshment rate
   637  // must be called under accountingPeer lock
   638  func (a *Accounting) notifyPaymentThresholdUpgrade(peer swarm.Address, accountingPeer *accountingPeer) {
   639  
   640  	// get appropriate linear growth limit based on whether the peer is a full node or a light node
   641  	thresholdGrowChange := new(big.Int).Set(a.thresholdGrowChange)
   642  	if !accountingPeer.fullNode {
   643  		thresholdGrowChange.Set(a.lightThresholdGrowChange)
   644  	}
   645  
   646  	// if current checkpoint already passed linear growth limit, set next checkpoint exponentially
   647  	if accountingPeer.thresholdGrowAt.Cmp(thresholdGrowChange) >= 0 {
   648  		accountingPeer.thresholdGrowAt = new(big.Int).Mul(accountingPeer.thresholdGrowAt, big.NewInt(2))
   649  	} else {
   650  		// otherwise set next linear checkpoint
   651  		if accountingPeer.fullNode {
   652  			accountingPeer.thresholdGrowAt = new(big.Int).Add(accountingPeer.thresholdGrowAt, a.thresholdGrowStep)
   653  		} else {
   654  			accountingPeer.thresholdGrowAt = new(big.Int).Add(accountingPeer.thresholdGrowAt, a.lightThresholdGrowStep)
   655  		}
   656  	}
   657  
   658  	// get appropriate refresh rate
   659  	refreshRate := new(big.Int).Set(a.refreshRate)
   660  	if !accountingPeer.fullNode {
   661  		refreshRate = new(big.Int).Set(a.lightRefreshRate)
   662  	}
   663  
   664  	// increase given threshold by refresh rate
   665  	accountingPeer.paymentThresholdForPeer = new(big.Int).Add(accountingPeer.paymentThresholdForPeer, refreshRate)
   666  	// recalculate disconnectLimit for peer
   667  	accountingPeer.disconnectLimit = percentOf(100+a.paymentTolerance, accountingPeer.paymentThresholdForPeer)
   668  
   669  	// announce new payment threshold to peer
   670  	err := a.pricing.AnnouncePaymentThreshold(context.Background(), peer, accountingPeer.paymentThresholdForPeer)
   671  	if err != nil {
   672  		a.logger.Error(err, "announcing increased payment threshold", "value", accountingPeer.paymentThresholdForPeer, "peer_address", peer)
   673  	}
   674  }
   675  
   676  // Balances gets balances for all peers from store.
   677  func (a *Accounting) Balances() (map[string]*big.Int, error) {
   678  	s := make(map[string]*big.Int)
   679  
   680  	err := a.store.Iterate(balancesPrefix, func(key, val []byte) (stop bool, err error) {
   681  		addr, err := balanceKeyPeer(key)
   682  		if err != nil {
   683  			return false, fmt.Errorf("parse address from key: %s: %w", string(key), err)
   684  		}
   685  
   686  		if _, ok := s[addr.String()]; !ok {
   687  			var storevalue *big.Int
   688  			err = a.store.Get(peerBalanceKey(addr), &storevalue)
   689  			if err != nil {
   690  				return false, fmt.Errorf("get peer %s balance: %w", addr.String(), err)
   691  			}
   692  
   693  			s[addr.String()] = storevalue
   694  		}
   695  
   696  		return false, nil
   697  	})
   698  
   699  	if err != nil {
   700  		return nil, err
   701  	}
   702  
   703  	return s, nil
   704  }
   705  
   706  type PeerInfo struct {
   707  	Balance                  *big.Int
   708  	ConsumedBalance          *big.Int
   709  	ThresholdReceived        *big.Int
   710  	ThresholdGiven           *big.Int
   711  	CurrentThresholdReceived *big.Int
   712  	CurrentThresholdGiven    *big.Int
   713  	SurplusBalance           *big.Int
   714  	ReservedBalance          *big.Int
   715  	ShadowReservedBalance    *big.Int
   716  	GhostBalance             *big.Int
   717  }
   718  
   719  func (a *Accounting) PeerAccounting() (map[string]PeerInfo, error) {
   720  	s := make(map[string]PeerInfo)
   721  
   722  	a.accountingPeersMu.Lock()
   723  	accountingPeersList := make(map[string]*accountingPeer)
   724  	for peer, accountingPeer := range a.accountingPeers {
   725  		accountingPeersList[peer] = accountingPeer
   726  	}
   727  	a.accountingPeersMu.Unlock()
   728  
   729  	for peer, accountingPeer := range accountingPeersList {
   730  
   731  		peerAddress := swarm.MustParseHexAddress(peer)
   732  
   733  		balance, err := a.Balance(peerAddress)
   734  		if errors.Is(err, ErrPeerNoBalance) {
   735  			balance = big.NewInt(0)
   736  		} else if err != nil {
   737  			return nil, err
   738  		}
   739  
   740  		surplusBalance, err := a.SurplusBalance(peerAddress)
   741  		if err != nil {
   742  			return nil, err
   743  		}
   744  
   745  		accountingPeer.lock.Lock()
   746  
   747  		t := a.timeNow()
   748  
   749  		timeElapsedInSeconds := t.Unix() - accountingPeer.refreshReceivedTimestamp
   750  		if timeElapsedInSeconds > 1 {
   751  			timeElapsedInSeconds = 1
   752  		}
   753  
   754  		// get appropriate refresh rate
   755  		refreshRate := new(big.Int).Set(a.refreshRate)
   756  		if !accountingPeer.fullNode {
   757  			refreshRate = new(big.Int).Set(a.lightRefreshRate)
   758  		}
   759  
   760  		refreshDue := new(big.Int).Mul(big.NewInt(timeElapsedInSeconds), refreshRate)
   761  		currentThresholdGiven := new(big.Int).Add(accountingPeer.disconnectLimit, refreshDue)
   762  
   763  		timeElapsedInSeconds = (t.UnixMilli() - accountingPeer.refreshTimestampMilliseconds) / 1000
   764  		if timeElapsedInSeconds > 1 {
   765  			timeElapsedInSeconds = 1
   766  		}
   767  
   768  		// get appropriate refresh rate
   769  		refreshDue = new(big.Int).Mul(big.NewInt(timeElapsedInSeconds), a.refreshRate)
   770  		currentThresholdReceived := new(big.Int).Add(accountingPeer.paymentThreshold, refreshDue)
   771  
   772  		s[peer] = PeerInfo{
   773  			Balance:                  new(big.Int).Sub(balance, surplusBalance),
   774  			ConsumedBalance:          new(big.Int).Set(balance),
   775  			ThresholdReceived:        new(big.Int).Set(accountingPeer.paymentThreshold),
   776  			CurrentThresholdReceived: currentThresholdReceived,
   777  			CurrentThresholdGiven:    currentThresholdGiven,
   778  			ThresholdGiven:           new(big.Int).Set(accountingPeer.paymentThresholdForPeer),
   779  			SurplusBalance:           new(big.Int).Set(surplusBalance),
   780  			ReservedBalance:          new(big.Int).Set(accountingPeer.reservedBalance),
   781  			ShadowReservedBalance:    new(big.Int).Set(accountingPeer.shadowReservedBalance),
   782  			GhostBalance:             new(big.Int).Set(accountingPeer.ghostBalance),
   783  		}
   784  		accountingPeer.lock.Unlock()
   785  	}
   786  
   787  	return s, nil
   788  }
   789  
   790  // CompensatedBalances gets balances for all peers from store.
   791  func (a *Accounting) CompensatedBalances() (map[string]*big.Int, error) {
   792  	s := make(map[string]*big.Int)
   793  
   794  	err := a.store.Iterate(balancesPrefix, func(key, val []byte) (stop bool, err error) {
   795  		addr, err := balanceKeyPeer(key)
   796  		if err != nil {
   797  			return false, fmt.Errorf("parse address from key: %s: %w", string(key), err)
   798  		}
   799  		if _, ok := s[addr.String()]; !ok {
   800  			value, err := a.CompensatedBalance(addr)
   801  			if err != nil {
   802  				return false, fmt.Errorf("get peer %s balance: %w", addr.String(), err)
   803  			}
   804  
   805  			s[addr.String()] = value
   806  		}
   807  
   808  		return false, nil
   809  	})
   810  
   811  	if err != nil {
   812  		return nil, err
   813  	}
   814  
   815  	err = a.store.Iterate(balancesSurplusPrefix, func(key, val []byte) (stop bool, err error) {
   816  		addr, err := surplusBalanceKeyPeer(key)
   817  		if err != nil {
   818  			return false, fmt.Errorf("parse address from key: %s: %w", string(key), err)
   819  		}
   820  		if _, ok := s[addr.String()]; !ok {
   821  			value, err := a.CompensatedBalance(addr)
   822  			if err != nil {
   823  				return false, fmt.Errorf("get peer %s balance: %w", addr.String(), err)
   824  			}
   825  
   826  			s[addr.String()] = value
   827  		}
   828  
   829  		return false, nil
   830  	})
   831  
   832  	if err != nil {
   833  		return nil, err
   834  	}
   835  
   836  	return s, nil
   837  }
   838  
   839  // balanceKeyPeer returns the embedded peer from the balance storage key.
   840  func balanceKeyPeer(key []byte) (swarm.Address, error) {
   841  	k := string(key)
   842  
   843  	split := strings.SplitAfter(k, balancesPrefix)
   844  	if len(split) != 2 {
   845  		return swarm.ZeroAddress, errors.New("no peer in key")
   846  	}
   847  
   848  	addr, err := swarm.ParseHexAddress(split[1])
   849  	if err != nil {
   850  		return swarm.ZeroAddress, err
   851  	}
   852  
   853  	return addr, nil
   854  }
   855  
   856  func surplusBalanceKeyPeer(key []byte) (swarm.Address, error) {
   857  	k := string(key)
   858  
   859  	split := strings.SplitAfter(k, balancesSurplusPrefix)
   860  	if len(split) != 2 {
   861  		return swarm.ZeroAddress, errors.New("no peer in key")
   862  	}
   863  
   864  	addr, err := swarm.ParseHexAddress(split[1])
   865  	if err != nil {
   866  		return swarm.ZeroAddress, err
   867  	}
   868  
   869  	return addr, nil
   870  }
   871  
   872  // PeerDebt returns the positive part of the sum of the outstanding balance and the shadow reserve
   873  func (a *Accounting) PeerDebt(peer swarm.Address) (*big.Int, error) {
   874  	accountingPeer := a.getAccountingPeer(peer)
   875  
   876  	accountingPeer.lock.Lock()
   877  	defer accountingPeer.lock.Unlock()
   878  
   879  	balance := new(big.Int)
   880  	zero := big.NewInt(0)
   881  
   882  	err := a.store.Get(peerBalanceKey(peer), &balance)
   883  	if err != nil {
   884  		if !errors.Is(err, storage.ErrNotFound) {
   885  			return nil, err
   886  		}
   887  		balance = big.NewInt(0)
   888  	}
   889  
   890  	peerDebt := new(big.Int).Add(balance, accountingPeer.shadowReservedBalance)
   891  
   892  	if peerDebt.Cmp(zero) < 0 {
   893  		return zero, nil
   894  	}
   895  
   896  	return peerDebt, nil
   897  }
   898  
   899  // peerLatentDebt returns the sum of the positive part of the outstanding balance, shadow reserve and the ghost balance
   900  func (a *Accounting) peerLatentDebt(peer swarm.Address) (*big.Int, error) {
   901  
   902  	accountingPeer := a.getAccountingPeer(peer)
   903  
   904  	balance := new(big.Int)
   905  	zero := big.NewInt(0)
   906  
   907  	err := a.store.Get(peerBalanceKey(peer), &balance)
   908  	if err != nil {
   909  		if !errors.Is(err, storage.ErrNotFound) {
   910  			return nil, err
   911  		}
   912  		balance = big.NewInt(0)
   913  	}
   914  
   915  	if balance.Cmp(zero) < 0 {
   916  		balance.Set(zero)
   917  	}
   918  
   919  	peerDebt := new(big.Int).Add(balance, accountingPeer.shadowReservedBalance)
   920  	peerLatentDebt := new(big.Int).Add(peerDebt, accountingPeer.ghostBalance)
   921  
   922  	if peerLatentDebt.Cmp(zero) < 0 {
   923  		return zero, nil
   924  	}
   925  
   926  	return peerLatentDebt, nil
   927  }
   928  
   929  // shadowBalance returns the current debt reduced by any potentially debitable amount stored in shadowReservedBalance
   930  // this represents how much less our debt could potentially be seen by the other party if it's ahead with processing credits corresponding to our shadow reserve
   931  func (a *Accounting) shadowBalance(peer swarm.Address, accountingPeer *accountingPeer) (shadowBalance *big.Int, err error) {
   932  	balance := new(big.Int)
   933  	zero := big.NewInt(0)
   934  
   935  	err = a.store.Get(peerBalanceKey(peer), &balance)
   936  	if err != nil {
   937  		if errors.Is(err, storage.ErrNotFound) {
   938  			return zero, nil
   939  		}
   940  		return nil, err
   941  	}
   942  
   943  	if balance.Cmp(zero) >= 0 {
   944  		return zero, nil
   945  	}
   946  
   947  	negativeBalance := new(big.Int).Neg(balance)
   948  
   949  	surplusBalance, err := a.SurplusBalance(peer)
   950  	if err != nil {
   951  		return nil, err
   952  	}
   953  
   954  	debt := new(big.Int).Add(negativeBalance, surplusBalance)
   955  
   956  	if debt.Cmp(accountingPeer.shadowReservedBalance) < 0 {
   957  		return zero, nil
   958  	}
   959  
   960  	shadowBalance = new(big.Int).Sub(negativeBalance, accountingPeer.shadowReservedBalance)
   961  
   962  	return shadowBalance, nil
   963  }
   964  
   965  // NotifyPaymentSent is triggered by async monetary settlement to update our balance and remove it's price from the shadow reserve
   966  func (a *Accounting) NotifyPaymentSent(peer swarm.Address, amount *big.Int, receivedError error) {
   967  	loggerV2 := a.logger.V(2).Register()
   968  
   969  	defer a.wg.Done()
   970  	accountingPeer := a.getAccountingPeer(peer)
   971  
   972  	accountingPeer.lock.Lock()
   973  	defer accountingPeer.lock.Unlock()
   974  
   975  	accountingPeer.paymentOngoing = false
   976  	// decrease shadow reserve by payment value
   977  	accountingPeer.shadowReservedBalance.Sub(accountingPeer.shadowReservedBalance, amount)
   978  
   979  	if receivedError != nil {
   980  		accountingPeer.lastSettlementFailureTimestamp = a.timeNow().Unix()
   981  		a.metrics.PaymentErrorCount.Inc()
   982  		a.logger.Warning("payment failure", "error", receivedError)
   983  		return
   984  	}
   985  
   986  	currentBalance, err := a.Balance(peer)
   987  	if err != nil {
   988  		if !errors.Is(err, ErrPeerNoBalance) {
   989  			a.logger.Error(err, "notify payment sent; failed to persist balance")
   990  			return
   991  		}
   992  	}
   993  
   994  	// Get nextBalance by increasing current balance with price
   995  	nextBalance := new(big.Int).Add(currentBalance, amount)
   996  
   997  	loggerV2.Debug("registering payment sent", "peer_address", peer, "amount", amount, "new_balance", nextBalance)
   998  
   999  	err = a.store.Put(peerBalanceKey(peer), nextBalance)
  1000  	if err != nil {
  1001  		a.logger.Error(err, "notify payment sent; failed to persist balance")
  1002  		return
  1003  	}
  1004  
  1005  	err = a.decreaseOriginatedBalanceBy(peer, amount)
  1006  	if err != nil {
  1007  		a.logger.Warning("notify payment sent; failed to decrease originated balance", "error", err)
  1008  	}
  1009  
  1010  }
  1011  
  1012  // NotifyPaymentThreshold should be called to notify accounting of changes in the payment threshold
  1013  func (a *Accounting) NotifyPaymentThreshold(peer swarm.Address, paymentThreshold *big.Int) error {
  1014  	accountingPeer := a.getAccountingPeer(peer)
  1015  
  1016  	accountingPeer.lock.Lock()
  1017  	defer accountingPeer.lock.Unlock()
  1018  
  1019  	accountingPeer.paymentThreshold.Set(paymentThreshold)
  1020  	accountingPeer.earlyPayment.Set(percentOf(100-a.earlyPayment, paymentThreshold))
  1021  	return nil
  1022  }
  1023  
  1024  // NotifyPaymentReceived is called by Settlement when we receive a payment.
  1025  func (a *Accounting) NotifyPaymentReceived(peer swarm.Address, amount *big.Int) error {
  1026  	loggerV2 := a.logger.V(2).Register()
  1027  
  1028  	accountingPeer := a.getAccountingPeer(peer)
  1029  
  1030  	accountingPeer.lock.Lock()
  1031  	defer accountingPeer.lock.Unlock()
  1032  
  1033  	accountingPeer.totalDebtRepay = new(big.Int).Add(accountingPeer.totalDebtRepay, amount)
  1034  
  1035  	if accountingPeer.totalDebtRepay.Cmp(accountingPeer.thresholdGrowAt) > 0 {
  1036  		a.notifyPaymentThresholdUpgrade(peer, accountingPeer)
  1037  	}
  1038  
  1039  	currentBalance, err := a.Balance(peer)
  1040  	if err != nil {
  1041  		if !errors.Is(err, ErrPeerNoBalance) {
  1042  			return err
  1043  		}
  1044  	}
  1045  
  1046  	// if balance is already negative or zero, we credit full amount received to surplus balance and terminate early
  1047  	if currentBalance.Cmp(big.NewInt(0)) <= 0 {
  1048  		surplus, err := a.SurplusBalance(peer)
  1049  		if err != nil {
  1050  			return fmt.Errorf("failed to get surplus balance: %w", err)
  1051  		}
  1052  		increasedSurplus := new(big.Int).Add(surplus, amount)
  1053  
  1054  		loggerV2.Debug("surplus crediting peer", "peer_address", peer, "amount", amount, "new_balance", increasedSurplus)
  1055  
  1056  		err = a.store.Put(peerSurplusBalanceKey(peer), increasedSurplus)
  1057  		if err != nil {
  1058  			return fmt.Errorf("failed to persist surplus balance: %w", err)
  1059  		}
  1060  
  1061  		return nil
  1062  	}
  1063  
  1064  	// if current balance is positive, let's make a partial credit to
  1065  	newBalance := new(big.Int).Sub(currentBalance, amount)
  1066  
  1067  	// Don't allow a payment to put us into debt
  1068  	// This is to prevent another node tricking us into settling by settling
  1069  	// first (e.g. send a bouncing cheque to trigger an honest cheque in swap).
  1070  	nextBalance := newBalance
  1071  	if newBalance.Cmp(big.NewInt(0)) < 0 {
  1072  		nextBalance = big.NewInt(0)
  1073  	}
  1074  
  1075  	loggerV2.Debug("crediting peer", "peer_address", peer, "amount", amount, "new_balance", nextBalance)
  1076  
  1077  	err = a.store.Put(peerBalanceKey(peer), nextBalance)
  1078  	if err != nil {
  1079  		return fmt.Errorf("failed to persist balance: %w", err)
  1080  	}
  1081  
  1082  	// If payment would have put us into debt, rather, let's add to surplusBalance,
  1083  	// so as that an oversettlement attempt creates balance for future forwarding services
  1084  	// charges to be deducted of
  1085  	if newBalance.Cmp(big.NewInt(0)) < 0 {
  1086  		surplusGrowth := new(big.Int).Sub(amount, currentBalance)
  1087  
  1088  		surplus, err := a.SurplusBalance(peer)
  1089  		if err != nil {
  1090  			return fmt.Errorf("failed to get surplus balance: %w", err)
  1091  		}
  1092  		increasedSurplus := new(big.Int).Add(surplus, surplusGrowth)
  1093  
  1094  		loggerV2.Debug("surplus crediting peer due to refreshment", "peer_address", peer, "amount", surplusGrowth, "new_balance", increasedSurplus)
  1095  
  1096  		err = a.store.Put(peerSurplusBalanceKey(peer), increasedSurplus)
  1097  		if err != nil {
  1098  			return fmt.Errorf("failed to persist surplus balance: %w", err)
  1099  		}
  1100  	}
  1101  
  1102  	return nil
  1103  }
  1104  
  1105  // NotifyRefreshmentSent is called by pseudosettle when refreshment is done or failed
  1106  func (a *Accounting) NotifyRefreshmentSent(peer swarm.Address, attemptedAmount, amount *big.Int, timestamp int64, allegedInterval int64, receivedError error) {
  1107  	accountingPeer := a.getAccountingPeer(peer)
  1108  
  1109  	accountingPeer.lock.Lock()
  1110  	defer accountingPeer.lock.Unlock()
  1111  
  1112  	// conclude ongoing refreshment
  1113  	accountingPeer.refreshOngoing = false
  1114  	// save timestamp received in milliseconds of when the refreshment completed locally
  1115  	accountingPeer.refreshTimestampMilliseconds = timestamp
  1116  
  1117  	// if specific error is received increment metrics
  1118  	if receivedError != nil {
  1119  		switch {
  1120  		case errors.Is(receivedError, pseudosettle.ErrRefreshmentAboveExpected):
  1121  			a.metrics.ErrRefreshmentAboveExpected.Inc()
  1122  		case errors.Is(receivedError, pseudosettle.ErrTimeOutOfSyncAlleged):
  1123  			a.metrics.ErrTimeOutOfSyncAlleged.Inc()
  1124  		case errors.Is(receivedError, pseudosettle.ErrTimeOutOfSyncRecent):
  1125  			a.metrics.ErrTimeOutOfSyncRecent.Inc()
  1126  		case errors.Is(receivedError, pseudosettle.ErrTimeOutOfSyncInterval):
  1127  			a.metrics.ErrTimeOutOfSyncInterval.Inc()
  1128  		}
  1129  
  1130  		// if refreshment failed with connected peer, blocklist
  1131  		if !errors.Is(receivedError, p2p.ErrPeerNotFound) {
  1132  			a.metrics.AccountingDisconnectsEnforceRefreshCount.Inc()
  1133  			_ = a.blocklist(peer, 1, "failed to refresh")
  1134  		}
  1135  		a.logger.Error(receivedError, "notifyrefreshmentsent failed to refresh")
  1136  		return
  1137  	}
  1138  
  1139  	// enforce allowance
  1140  	// calculate expectation decreased by any potential debt decreases occurred during the refreshment
  1141  	checkAllowance := new(big.Int).Sub(attemptedAmount, accountingPeer.refreshReservedBalance)
  1142  
  1143  	// reset cumulative potential debt decrease during an ongoing refreshment as refreshment just completed
  1144  	accountingPeer.refreshReservedBalance.Set(big.NewInt(0))
  1145  
  1146  	// dont expect higher amount accepted than attempted (sanity check)
  1147  	if checkAllowance.Cmp(attemptedAmount) > 0 {
  1148  		checkAllowance.Set(attemptedAmount)
  1149  	}
  1150  
  1151  	// calculate time based allowance
  1152  	expectedAllowance := new(big.Int).Mul(big.NewInt(allegedInterval), a.refreshRate)
  1153  	// expect minimum of time based allowance and debt / attempted amount based expectation
  1154  	if expectedAllowance.Cmp(checkAllowance) > 0 {
  1155  		expectedAllowance = new(big.Int).Set(checkAllowance)
  1156  	}
  1157  
  1158  	// compare received refreshment amount to expectation
  1159  	if expectedAllowance.Cmp(amount) > 0 {
  1160  		// if expectation is not met, blocklist peer
  1161  		a.logger.Error(nil, "accepted lower payment than expected", "pseudosettle peer", peer)
  1162  		a.metrics.ErrRefreshmentBelowExpected.Inc()
  1163  		_ = a.blocklist(peer, 1, "failed to meet expectation for allowance")
  1164  		return
  1165  	}
  1166  
  1167  	// update balance
  1168  	currentBalance, err := a.Balance(peer)
  1169  	if err != nil {
  1170  		if !errors.Is(err, ErrPeerNoBalance) {
  1171  			a.logger.Error(err, "notifyrefreshmentsent failed to get balance")
  1172  			return
  1173  		}
  1174  	}
  1175  
  1176  	newBalance := new(big.Int).Add(currentBalance, amount)
  1177  
  1178  	err = a.store.Put(peerBalanceKey(peer), newBalance)
  1179  	if err != nil {
  1180  		a.logger.Error(err, "notifyrefreshmentsent failed to persist balance")
  1181  		return
  1182  	}
  1183  
  1184  	// update originated balance
  1185  	err = a.decreaseOriginatedBalanceTo(peer, newBalance)
  1186  	if err != nil {
  1187  		a.logger.Warning("accounting: notifyrefreshmentsent failed to decrease originated balance", "error", err)
  1188  	}
  1189  
  1190  }
  1191  
  1192  // NotifyRefreshmentReceived is called by pseudosettle when we receive a time based settlement.
  1193  func (a *Accounting) NotifyRefreshmentReceived(peer swarm.Address, amount *big.Int, timestamp int64) error {
  1194  	loggerV2 := a.logger.V(2).Register()
  1195  
  1196  	accountingPeer := a.getAccountingPeer(peer)
  1197  
  1198  	accountingPeer.lock.Lock()
  1199  	defer accountingPeer.lock.Unlock()
  1200  
  1201  	accountingPeer.totalDebtRepay = new(big.Int).Add(accountingPeer.totalDebtRepay, amount)
  1202  
  1203  	if accountingPeer.totalDebtRepay.Cmp(accountingPeer.thresholdGrowAt) > 0 {
  1204  		a.notifyPaymentThresholdUpgrade(peer, accountingPeer)
  1205  	}
  1206  
  1207  	currentBalance, err := a.Balance(peer)
  1208  	if err != nil {
  1209  		if !errors.Is(err, ErrPeerNoBalance) {
  1210  			return err
  1211  		}
  1212  	}
  1213  
  1214  	// Get nextBalance by increasing current balance with amount
  1215  	nextBalance := new(big.Int).Sub(currentBalance, amount)
  1216  
  1217  	// We allow a refreshment to potentially put us into debt as it was previously negotiated and be limited to the peer's outstanding debt plus shadow reserve
  1218  	loggerV2.Debug("crediting peer", "peer_address", peer, "amount", amount, "new_balance", nextBalance)
  1219  	err = a.store.Put(peerBalanceKey(peer), nextBalance)
  1220  	if err != nil {
  1221  		return fmt.Errorf("failed to persist balance: %w", err)
  1222  	}
  1223  
  1224  	accountingPeer.refreshReceivedTimestamp = timestamp
  1225  
  1226  	return nil
  1227  }
  1228  
  1229  // PrepareDebit prepares a debit operation by increasing the shadowReservedBalance
  1230  func (a *Accounting) PrepareDebit(ctx context.Context, peer swarm.Address, price uint64) (Action, error) {
  1231  	loggerV2 := a.logger.V(2).Register()
  1232  
  1233  	accountingPeer := a.getAccountingPeer(peer)
  1234  
  1235  	if err := accountingPeer.lock.TryLock(ctx); err != nil {
  1236  		loggerV2.Debug("prepare debit; failed to acquire lock", "error", err)
  1237  		return nil, err
  1238  	}
  1239  
  1240  	defer accountingPeer.lock.Unlock()
  1241  
  1242  	if !accountingPeer.connected {
  1243  		return nil, errors.New("connection not initialized yet")
  1244  	}
  1245  
  1246  	bigPrice := new(big.Int).SetUint64(price)
  1247  
  1248  	accountingPeer.shadowReservedBalance = new(big.Int).Add(accountingPeer.shadowReservedBalance, bigPrice)
  1249  	// if a refreshment is ongoing, add this amount to the potential debt decrease during an ongoing refreshment
  1250  	if accountingPeer.refreshOngoing {
  1251  		accountingPeer.refreshReservedBalance = new(big.Int).Add(accountingPeer.refreshReservedBalance, bigPrice)
  1252  	}
  1253  
  1254  	return &debitAction{
  1255  		accounting:     a,
  1256  		price:          bigPrice,
  1257  		peer:           peer,
  1258  		accountingPeer: accountingPeer,
  1259  		applied:        false,
  1260  	}, nil
  1261  }
  1262  
  1263  func (a *Accounting) increaseBalance(peer swarm.Address, _ *accountingPeer, price *big.Int) (*big.Int, error) {
  1264  	loggerV2 := a.logger.V(2).Register()
  1265  
  1266  	cost := new(big.Int).Set(price)
  1267  	// see if peer has surplus balance to deduct this transaction of
  1268  
  1269  	surplusBalance, err := a.SurplusBalance(peer)
  1270  	if err != nil {
  1271  		return nil, fmt.Errorf("failed to get surplus balance: %w", err)
  1272  	}
  1273  
  1274  	if surplusBalance.Cmp(big.NewInt(0)) > 0 {
  1275  		// get new surplus balance after deduct
  1276  		newSurplusBalance := new(big.Int).Sub(surplusBalance, cost)
  1277  
  1278  		// if nothing left for debiting, store new surplus balance and return from debit
  1279  		if newSurplusBalance.Cmp(big.NewInt(0)) >= 0 {
  1280  			loggerV2.Debug("surplus debiting peer", "peer_address", peer, "price", price, "new_balance", newSurplusBalance)
  1281  
  1282  			err = a.store.Put(peerSurplusBalanceKey(peer), newSurplusBalance)
  1283  			if err != nil {
  1284  				return nil, fmt.Errorf("failed to persist surplus balance: %w", err)
  1285  			}
  1286  
  1287  			return a.Balance(peer)
  1288  		}
  1289  
  1290  		// if surplus balance didn't cover full transaction, let's continue with leftover part as cost
  1291  		debitIncrease := new(big.Int).Sub(price, surplusBalance)
  1292  
  1293  		// a sanity check
  1294  		if debitIncrease.Cmp(big.NewInt(0)) <= 0 {
  1295  			return nil, errors.New("sanity check failed for partial debit after surplus balance drawn")
  1296  		}
  1297  		cost.Set(debitIncrease)
  1298  
  1299  		// if we still have something to debit, than have run out of surplus balance,
  1300  		// let's store 0 as surplus balance
  1301  		loggerV2.Debug("surplus debiting peer", "peer_address", peer, "amount", debitIncrease, "new_balance", 0)
  1302  
  1303  		err = a.store.Put(peerSurplusBalanceKey(peer), big.NewInt(0))
  1304  		if err != nil {
  1305  			return nil, fmt.Errorf("failed to persist surplus balance: %w", err)
  1306  		}
  1307  	}
  1308  
  1309  	currentBalance, err := a.Balance(peer)
  1310  	if err != nil {
  1311  		if !errors.Is(err, ErrPeerNoBalance) {
  1312  			return nil, fmt.Errorf("failed to load balance: %w", err)
  1313  		}
  1314  	}
  1315  
  1316  	// Get nextBalance by increasing current balance with price
  1317  	nextBalance := new(big.Int).Add(currentBalance, cost)
  1318  
  1319  	loggerV2.Debug("debiting peer", "peer_address", peer, "price", price, "new_balance", nextBalance)
  1320  
  1321  	err = a.store.Put(peerBalanceKey(peer), nextBalance)
  1322  	if err != nil {
  1323  		return nil, fmt.Errorf("failed to persist balance: %w", err)
  1324  	}
  1325  
  1326  	err = a.decreaseOriginatedBalanceTo(peer, nextBalance)
  1327  	if err != nil {
  1328  		a.logger.Warning("increase balance; failed to decrease originated balance", "error", err)
  1329  	}
  1330  
  1331  	return nextBalance, nil
  1332  }
  1333  
  1334  // Apply applies the debit operation and decreases the shadowReservedBalance
  1335  func (d *debitAction) Apply() error {
  1336  	d.accountingPeer.lock.Lock()
  1337  	defer d.accountingPeer.lock.Unlock()
  1338  
  1339  	a := d.accounting
  1340  
  1341  	cost := new(big.Int).Set(d.price)
  1342  
  1343  	nextBalance, err := d.accounting.increaseBalance(d.peer, d.accountingPeer, cost)
  1344  	if err != nil {
  1345  		return err
  1346  	}
  1347  
  1348  	d.applied = true
  1349  	d.accountingPeer.shadowReservedBalance = new(big.Int).Sub(d.accountingPeer.shadowReservedBalance, d.price)
  1350  
  1351  	tot, _ := big.NewFloat(0).SetInt(d.price).Float64()
  1352  
  1353  	a.metrics.TotalDebitedAmount.Add(tot)
  1354  	a.metrics.DebitEventsCount.Inc()
  1355  
  1356  	timeElapsedInSeconds := a.timeNow().Unix() - d.accountingPeer.refreshReceivedTimestamp
  1357  	if timeElapsedInSeconds > 1 {
  1358  		timeElapsedInSeconds = 1
  1359  	}
  1360  
  1361  	// get appropriate refresh rate
  1362  	refreshRate := new(big.Int).Set(a.refreshRate)
  1363  	if !d.accountingPeer.fullNode {
  1364  		refreshRate = new(big.Int).Set(a.lightRefreshRate)
  1365  	}
  1366  
  1367  	refreshDue := new(big.Int).Mul(big.NewInt(timeElapsedInSeconds), refreshRate)
  1368  	disconnectLimit := new(big.Int).Add(d.accountingPeer.disconnectLimit, refreshDue)
  1369  
  1370  	if nextBalance.Cmp(disconnectLimit) >= 0 {
  1371  		// peer too much in debt
  1372  		a.metrics.AccountingDisconnectsOverdrawCount.Inc()
  1373  
  1374  		disconnectFor, err := a.blocklistUntil(d.peer, 1)
  1375  		if err != nil {
  1376  			disconnectFor = 10
  1377  		}
  1378  		return p2p.NewBlockPeerError(time.Duration(disconnectFor)*time.Second, ErrDisconnectThresholdExceeded)
  1379  
  1380  	}
  1381  
  1382  	return nil
  1383  }
  1384  
  1385  // Cleanup reduces shadow reserve if and only if debitaction have not been applied
  1386  func (d *debitAction) Cleanup() {
  1387  	if d.applied {
  1388  		return
  1389  	}
  1390  
  1391  	d.accountingPeer.lock.Lock()
  1392  	defer d.accountingPeer.lock.Unlock()
  1393  
  1394  	a := d.accounting
  1395  	d.accountingPeer.shadowReservedBalance = new(big.Int).Sub(d.accountingPeer.shadowReservedBalance, d.price)
  1396  	d.accountingPeer.ghostBalance = new(big.Int).Add(d.accountingPeer.ghostBalance, d.price)
  1397  	if d.accountingPeer.ghostBalance.Cmp(d.accountingPeer.disconnectLimit) > 0 {
  1398  		a.metrics.AccountingDisconnectsGhostOverdrawCount.Inc()
  1399  		_ = a.blocklist(d.peer, 1, "ghost overdraw")
  1400  	}
  1401  }
  1402  
  1403  func (a *Accounting) blocklistUntil(peer swarm.Address, multiplier int64) (int64, error) {
  1404  
  1405  	debt, err := a.peerLatentDebt(peer)
  1406  	if err != nil {
  1407  		return 0, err
  1408  	}
  1409  
  1410  	if debt.Cmp(a.refreshRate) < 0 {
  1411  		debt.Set(a.refreshRate)
  1412  	}
  1413  
  1414  	additionalDebt := new(big.Int).Add(debt, a.paymentThreshold)
  1415  
  1416  	multiplyDebt := new(big.Int).Mul(additionalDebt, big.NewInt(multiplier))
  1417  
  1418  	k := new(big.Int).Div(multiplyDebt, a.refreshRate)
  1419  
  1420  	kInt := k.Int64()
  1421  
  1422  	return kInt, nil
  1423  }
  1424  
  1425  func (a *Accounting) blocklist(peer swarm.Address, multiplier int64, reason string) error {
  1426  	disconnectFor, err := a.blocklistUntil(peer, multiplier)
  1427  	if err != nil {
  1428  		return a.p2p.Blocklist(peer, 1*time.Minute, reason)
  1429  	}
  1430  
  1431  	return a.p2p.Blocklist(peer, time.Duration(disconnectFor)*time.Second, reason)
  1432  }
  1433  
  1434  func (a *Accounting) Connect(peer swarm.Address, fullNode bool) {
  1435  	accountingPeer := a.getAccountingPeer(peer)
  1436  	zero := big.NewInt(0)
  1437  
  1438  	accountingPeer.lock.Lock()
  1439  	defer accountingPeer.lock.Unlock()
  1440  
  1441  	paymentThreshold := new(big.Int).Set(a.paymentThreshold)
  1442  	thresholdGrowStep := new(big.Int).Set(a.thresholdGrowStep)
  1443  	disconnectLimit := new(big.Int).Set(a.disconnectLimit)
  1444  
  1445  	if !fullNode {
  1446  		paymentThreshold.Set(a.lightPaymentThreshold)
  1447  		thresholdGrowStep.Set(a.lightThresholdGrowStep)
  1448  		disconnectLimit.Set(a.lightDisconnectLimit)
  1449  	}
  1450  
  1451  	accountingPeer.connected = true
  1452  	accountingPeer.fullNode = fullNode
  1453  	accountingPeer.shadowReservedBalance.Set(zero)
  1454  	accountingPeer.ghostBalance.Set(zero)
  1455  	accountingPeer.reservedBalance.Set(zero)
  1456  	accountingPeer.refreshReservedBalance.Set(zero)
  1457  	accountingPeer.paymentThresholdForPeer.Set(paymentThreshold)
  1458  	accountingPeer.thresholdGrowAt.Set(thresholdGrowStep)
  1459  	accountingPeer.disconnectLimit.Set(disconnectLimit)
  1460  
  1461  	err := a.store.Put(peerBalanceKey(peer), zero)
  1462  	if err != nil {
  1463  		a.logger.Error(err, "failed to persist balance")
  1464  	}
  1465  
  1466  	err = a.store.Put(peerSurplusBalanceKey(peer), zero)
  1467  	if err != nil {
  1468  		a.logger.Error(err, "failed to persist surplus balance")
  1469  	}
  1470  }
  1471  
  1472  // decreaseOriginatedBalanceTo decreases the originated balance to provided limit or 0 if limit is positive
  1473  func (a *Accounting) decreaseOriginatedBalanceTo(peer swarm.Address, limit *big.Int) error {
  1474  	loggerV2 := a.logger.V(2).Register()
  1475  
  1476  	zero := big.NewInt(0)
  1477  
  1478  	toSet := new(big.Int).Set(limit)
  1479  
  1480  	originatedBalance, err := a.OriginatedBalance(peer)
  1481  	if err != nil && !errors.Is(err, ErrPeerNoBalance) {
  1482  		return fmt.Errorf("failed to load originated balance: %w", err)
  1483  	}
  1484  
  1485  	if toSet.Cmp(zero) > 0 {
  1486  		toSet.Set(zero)
  1487  	}
  1488  
  1489  	// If originated balance is more into the negative domain, set it to limit
  1490  	if originatedBalance.Cmp(toSet) < 0 {
  1491  		err = a.store.Put(originatedBalanceKey(peer), toSet)
  1492  		if err != nil {
  1493  			return fmt.Errorf("failed to persist originated balance: %w", err)
  1494  		}
  1495  		loggerV2.Debug("decreasing originated balance of peer", "peer_address", peer, "new_balance", toSet)
  1496  	}
  1497  
  1498  	return nil
  1499  }
  1500  
  1501  // decreaseOriginatedBalanceTo decreases the originated balance by provided amount even below 0
  1502  func (a *Accounting) decreaseOriginatedBalanceBy(peer swarm.Address, amount *big.Int) error {
  1503  	loggerV2 := a.logger.V(2).Register()
  1504  
  1505  	originatedBalance, err := a.OriginatedBalance(peer)
  1506  	if err != nil && !errors.Is(err, ErrPeerNoBalance) {
  1507  		return fmt.Errorf("failed to load balance: %w", err)
  1508  	}
  1509  
  1510  	// Move originated balance into the positive domain by amount
  1511  	newOriginatedBalance := new(big.Int).Add(originatedBalance, amount)
  1512  
  1513  	err = a.store.Put(originatedBalanceKey(peer), newOriginatedBalance)
  1514  	if err != nil {
  1515  		return fmt.Errorf("failed to persist originated balance: %w", err)
  1516  	}
  1517  	loggerV2.Debug("decreasing originated balance of peer", "peer_address", peer, "amount", amount, "new_balance", newOriginatedBalance)
  1518  
  1519  	return nil
  1520  }
  1521  
  1522  func (a *Accounting) Disconnect(peer swarm.Address) {
  1523  	accountingPeer := a.getAccountingPeer(peer)
  1524  
  1525  	accountingPeer.lock.Lock()
  1526  	defer accountingPeer.lock.Unlock()
  1527  
  1528  	if accountingPeer.connected {
  1529  		disconnectFor, err := a.blocklistUntil(peer, 1)
  1530  		if err != nil {
  1531  			disconnectFor = int64(10)
  1532  		}
  1533  		accountingPeer.connected = false
  1534  		_ = a.p2p.Blocklist(peer, time.Duration(disconnectFor)*time.Second, "accounting disconnect")
  1535  		a.metrics.AccountingDisconnectsReconnectCount.Inc()
  1536  	}
  1537  }
  1538  
  1539  func (a *Accounting) SetRefreshFunc(f RefreshFunc) {
  1540  	a.refreshFunction = f
  1541  }
  1542  
  1543  func (a *Accounting) SetPayFunc(f PayFunc) {
  1544  	a.payFunction = f
  1545  }
  1546  
  1547  // Close hangs up running websockets on shutdown.
  1548  func (a *Accounting) Close() error {
  1549  	a.wg.Wait()
  1550  	return nil
  1551  }
  1552  
  1553  func percentOf(percent int64, of *big.Int) *big.Int {
  1554  	return new(big.Int).Div(new(big.Int).Mul(of, big.NewInt(percent)), big.NewInt(100))
  1555  }