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 }