github.com/ethersphere/bee/v2@v2.2.0/pkg/accounting/accounting_test.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_test 6 7 import ( 8 "context" 9 "errors" 10 "math/big" 11 "sync" 12 "testing" 13 "time" 14 15 "github.com/ethersphere/bee/v2/pkg/accounting" 16 "github.com/ethersphere/bee/v2/pkg/log" 17 "github.com/ethersphere/bee/v2/pkg/p2p" 18 p2pmock "github.com/ethersphere/bee/v2/pkg/p2p/mock" 19 "github.com/ethersphere/bee/v2/pkg/statestore/mock" 20 21 "github.com/ethersphere/bee/v2/pkg/swarm" 22 ) 23 24 const ( 25 testPrice = uint64(10) 26 testRefreshRate = int64(1000) 27 ) 28 29 var ( 30 testPaymentTolerance = int64(10) 31 testPaymentEarly = int64(10) 32 testPaymentThreshold = big.NewInt(10000) 33 testLightFactor = int64(10) 34 ) 35 36 type paymentCall struct { 37 peer swarm.Address 38 amount *big.Int 39 } 40 41 // booking represents an accounting action and the expected result afterwards 42 type bookingT struct { 43 peer swarm.Address 44 price int64 // Credit if <0, Debit otherwise 45 expectedBalance int64 46 originatedBalance int64 47 originatedCredit bool 48 notifyPaymentSent bool 49 overpay uint64 50 } 51 52 func TestMutex(t *testing.T) { 53 t.Parallel() 54 55 t.Run("locked mutex can not be locked again", func(t *testing.T) { 56 t.Parallel() 57 58 var ( 59 m = accounting.NewMutex() 60 c = make(chan struct{}, 1) 61 wg sync.WaitGroup 62 ) 63 64 m.Lock() 65 66 wg.Add(1) 67 go func() { 68 wg.Done() 69 m.Lock() 70 c <- struct{}{} 71 }() 72 73 wg.Wait() 74 75 select { 76 case <-c: 77 t.Error("not expected to acquire the lock") 78 case <-time.After(time.Millisecond): 79 } 80 81 m.Unlock() 82 <-c 83 }) 84 85 t.Run("can lock after release", func(t *testing.T) { 86 t.Parallel() 87 88 m := accounting.NewMutex() 89 m.Lock() 90 91 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) 92 defer cancel() 93 94 m.Unlock() 95 if err := m.TryLock(ctx); err != nil { 96 t.Errorf("expected no error, got %v", err) 97 } 98 }) 99 100 t.Run("locked mutex takes context into account", func(t *testing.T) { 101 t.Parallel() 102 103 m := accounting.NewMutex() 104 m.Lock() 105 106 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) 107 defer cancel() 108 if err := m.TryLock(ctx); !errors.Is(err, accounting.ErrFailToLock) { 109 t.Errorf("expected %v, got %v", accounting.ErrFailToLock, err) 110 } 111 }) 112 } 113 114 // TestAccountingAddBalance does several accounting actions and verifies the balance after each steep 115 func TestAccountingAddBalance(t *testing.T) { 116 t.Parallel() 117 118 logger := log.Noop 119 120 store := mock.NewStateStore() 121 defer store.Close() 122 123 pricing := &pricingMock{} 124 125 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 126 if err != nil { 127 t.Fatal(err) 128 } 129 130 peer1Addr, err := swarm.ParseHexAddress("00112233") 131 if err != nil { 132 t.Fatal(err) 133 } 134 135 peer2Addr, err := swarm.ParseHexAddress("00112244") 136 if err != nil { 137 t.Fatal(err) 138 } 139 140 acc.Connect(peer1Addr, true) 141 acc.Connect(peer2Addr, true) 142 143 bookings := []bookingT{ 144 {peer: peer1Addr, price: 100, expectedBalance: 100}, 145 {peer: peer2Addr, price: 200, expectedBalance: 200}, 146 {peer: peer1Addr, price: 300, expectedBalance: 400}, 147 {peer: peer1Addr, price: -100, expectedBalance: 300}, 148 {peer: peer2Addr, price: -1000, expectedBalance: -800}, 149 } 150 151 for i, booking := range bookings { 152 if booking.price < 0 { 153 creditAction, err := acc.PrepareCredit(context.Background(), booking.peer, uint64(-booking.price), true) 154 if err != nil { 155 t.Fatal(err) 156 } 157 err = creditAction.Apply() 158 if err != nil { 159 t.Fatal(err) 160 } 161 creditAction.Cleanup() 162 } else { 163 debitAction, err := acc.PrepareDebit(context.Background(), booking.peer, uint64(booking.price)) 164 if err != nil { 165 t.Fatal(err) 166 } 167 err = debitAction.Apply() 168 if err != nil { 169 t.Fatal(err) 170 } 171 debitAction.Cleanup() 172 } 173 174 balance, err := acc.Balance(booking.peer) 175 if err != nil { 176 t.Fatal(err) 177 } 178 179 if balance.Int64() != booking.expectedBalance { 180 t.Fatalf("balance for peer %v not as expected after booking %d. got %d, wanted %d", booking.peer.String(), i, balance, booking.expectedBalance) 181 } 182 } 183 } 184 185 // TestAccountingAddBalance does several accounting actions and verifies the balance after each steep 186 func TestAccountingAddOriginatedBalance(t *testing.T) { 187 t.Parallel() 188 189 logger := log.Noop 190 191 store := mock.NewStateStore() 192 defer store.Close() 193 194 pricing := &pricingMock{} 195 196 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(0), testLightFactor, p2pmock.New()) 197 if err != nil { 198 t.Fatal(err) 199 } 200 201 f := func(ctx context.Context, peer swarm.Address, amount *big.Int) { 202 acc.NotifyRefreshmentSent(peer, big.NewInt(0), big.NewInt(0), 1000, 0, nil) 203 } 204 205 acc.SetRefreshFunc(f) 206 207 peer1Addr, err := swarm.ParseHexAddress("00112233") 208 if err != nil { 209 t.Fatal(err) 210 } 211 212 acc.Connect(peer1Addr, true) 213 214 bookings := []bookingT{ 215 // originated credit 216 {peer: peer1Addr, price: -2000, expectedBalance: -2000, originatedBalance: -2000, originatedCredit: true}, 217 // forwarder credit 218 {peer: peer1Addr, price: -2000, expectedBalance: -4000, originatedBalance: -2000, originatedCredit: false}, 219 // inconsequential debit not moving balance closer to 0 than originbalance is to 0 220 {peer: peer1Addr, price: 1000, expectedBalance: -3000, originatedBalance: -2000}, 221 // consequential debit moving balance closer to 0 than originbalance, therefore also moving originated balance along 222 {peer: peer1Addr, price: 2000, expectedBalance: -1000, originatedBalance: -1000}, 223 // forwarder credit happening to increase debt 224 {peer: peer1Addr, price: -1000, expectedBalance: -2000, originatedBalance: -1000, originatedCredit: false}, 225 // expect notifypaymentsent triggered by reserve that moves originated balance into positive domain because of earlier debit triggering overpay 226 {peer: peer1Addr, price: -7000, expectedBalance: 1000, originatedBalance: 1000, overpay: 9000, notifyPaymentSent: true}, 227 // inconsequential debit because originated balance is in the positive domain 228 {peer: peer1Addr, price: 1000, expectedBalance: 2000, originatedBalance: 1000}, 229 // originated credit moving the originated balance back into the negative domain, should be limited to the expectedbalance 230 {peer: peer1Addr, price: -3000, expectedBalance: -1000, originatedBalance: -1000, originatedCredit: true}, 231 } 232 233 paychan := make(chan struct{}) 234 235 for i, booking := range bookings { 236 237 makePayFunc := func(currentBooking bookingT) func(ctx context.Context, peer swarm.Address, amount *big.Int) { 238 return func(ctx context.Context, peer swarm.Address, amount *big.Int) { 239 if currentBooking.overpay != 0 { 240 debitAction, err := acc.PrepareDebit(context.Background(), peer, currentBooking.overpay) 241 if err != nil { 242 t.Fatal(err) 243 } 244 _ = debitAction.Apply() 245 } 246 247 acc.NotifyPaymentSent(peer, amount, nil) 248 paychan <- struct{}{} 249 } 250 } 251 252 payFunc := makePayFunc(booking) 253 254 acc.SetPayFunc(payFunc) 255 256 if booking.price < 0 { 257 creditAction, err := acc.PrepareCredit(context.Background(), booking.peer, uint64(-booking.price), booking.originatedCredit) 258 if err != nil { 259 t.Fatal(err) 260 } 261 262 if booking.notifyPaymentSent { 263 select { 264 case <-paychan: 265 case <-time.After(1 * time.Second): 266 t.Fatal("expected payment sent", booking) 267 } 268 } 269 270 err = creditAction.Apply() 271 if err != nil { 272 t.Fatal(err) 273 } 274 275 creditAction.Cleanup() 276 } else { 277 debitAction, err := acc.PrepareDebit(context.Background(), booking.peer, uint64(booking.price)) 278 if err != nil { 279 t.Fatal(err) 280 } 281 err = debitAction.Apply() 282 if err != nil { 283 t.Fatal(err) 284 } 285 debitAction.Cleanup() 286 } 287 288 balance, err := acc.Balance(booking.peer) 289 if err != nil { 290 t.Fatal(err) 291 } 292 293 if balance.Int64() != booking.expectedBalance { 294 t.Fatalf("balance for peer %v not as expected after booking %d. got %d, wanted %d", booking.peer.String(), i, balance, booking.expectedBalance) 295 } 296 297 originatedBalance, err := acc.OriginatedBalance(booking.peer) 298 if err != nil { 299 t.Fatal(err) 300 } 301 302 if originatedBalance.Int64() != booking.originatedBalance { 303 t.Fatalf("originated balance for peer %v not as expected after booking %d. got %d, wanted %d", booking.peer.String(), i, originatedBalance, booking.originatedBalance) 304 } 305 } 306 } 307 308 // TestAccountingAdd_persistentBalances tests that balances are actually persisted 309 // It creates an accounting instance, does some accounting 310 // Then it creates a new accounting instance with the same store and verifies the balances 311 func TestAccountingAdd_persistentBalances(t *testing.T) { 312 t.Parallel() 313 314 logger := log.Noop 315 316 store := mock.NewStateStore() 317 defer store.Close() 318 319 pricing := &pricingMock{} 320 321 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 322 if err != nil { 323 t.Fatal(err) 324 } 325 326 peer1Addr, err := swarm.ParseHexAddress("00112233") 327 if err != nil { 328 t.Fatal(err) 329 } 330 331 peer2Addr, err := swarm.ParseHexAddress("00112244") 332 if err != nil { 333 t.Fatal(err) 334 } 335 336 acc.Connect(peer1Addr, true) 337 acc.Connect(peer2Addr, true) 338 339 peer1DebitAmount := testPrice 340 debitAction, err := acc.PrepareDebit(context.Background(), peer1Addr, peer1DebitAmount) 341 if err != nil { 342 t.Fatal(err) 343 } 344 err = debitAction.Apply() 345 if err != nil { 346 t.Fatal(err) 347 } 348 debitAction.Cleanup() 349 350 peer2CreditAmount := 2 * testPrice 351 creditAction, err := acc.PrepareCredit(context.Background(), peer2Addr, peer2CreditAmount, true) 352 if err != nil { 353 t.Fatal(err) 354 } 355 if err = creditAction.Apply(); err != nil { 356 t.Fatal(err) 357 } 358 creditAction.Cleanup() 359 360 acc, err = accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 361 if err != nil { 362 t.Fatal(err) 363 } 364 365 peer1Balance, err := acc.Balance(peer1Addr) 366 if err != nil { 367 t.Fatal(err) 368 } 369 370 if peer1Balance.Uint64() != peer1DebitAmount { 371 t.Fatalf("peer1Balance not loaded correctly. got %d, wanted %d", peer1Balance, peer1DebitAmount) 372 } 373 374 peer2Balance, err := acc.Balance(peer2Addr) 375 if err != nil { 376 t.Fatal(err) 377 } 378 379 if peer2Balance.Int64() != -int64(peer2CreditAmount) { 380 t.Fatalf("peer2Balance not loaded correctly. got %d, wanted %d", peer2Balance, -int64(peer2CreditAmount)) 381 } 382 } 383 384 // TestAccountingReserve tests that reserve returns an error if the payment threshold would be exceeded 385 func TestAccountingReserve(t *testing.T) { 386 t.Parallel() 387 388 logger := log.Noop 389 390 store := mock.NewStateStore() 391 defer store.Close() 392 393 pricing := &pricingMock{} 394 395 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 396 if err != nil { 397 t.Fatal(err) 398 } 399 400 peer1Addr, err := swarm.ParseHexAddress("00112233") 401 if err != nil { 402 t.Fatal(err) 403 } 404 405 acc.Connect(peer1Addr, true) 406 407 _, err = acc.PrepareCredit(context.Background(), peer1Addr, testPaymentThreshold.Uint64()+2*uint64(testRefreshRate)+1, true) 408 409 if err == nil { 410 t.Fatal("expected error from reserve") 411 } 412 413 if !errors.Is(err, accounting.ErrOverdraft) { 414 t.Fatalf("expected overdraft error from reserve, got %v", err) 415 } 416 } 417 418 // TestAccountingReserve tests that reserve returns an error if the payment threshold would be exceeded, 419 // but extends this limit by 'n' (max 2) seconds worth of refresh rate if last refreshment was 'n' seconds ago. 420 func TestAccountingReserveAheadOfTime(t *testing.T) { 421 t.Parallel() 422 423 logger := log.Noop 424 425 store := mock.NewStateStore() 426 defer store.Close() 427 428 pricing := &pricingMock{} 429 ts := int64(0) 430 431 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 432 if err != nil { 433 t.Fatal(err) 434 } 435 436 refreshchan := make(chan paymentCall, 1) 437 438 f := func(ctx context.Context, peer swarm.Address, amount *big.Int) { 439 acc.NotifyRefreshmentSent(peer, amount, amount, ts*1000, 0, nil) 440 refreshchan <- paymentCall{peer: peer, amount: amount} 441 } 442 443 pay := func(ctx context.Context, peer swarm.Address, amount *big.Int) { 444 } 445 446 acc.SetRefreshFunc(f) 447 acc.SetPayFunc(pay) 448 449 peer1Addr, err := swarm.ParseHexAddress("00112233") 450 if err != nil { 451 t.Fatal(err) 452 } 453 454 acc.Connect(peer1Addr, true) 455 456 acc.SetTime(ts) 457 458 // reserve until limit 459 460 _, err = acc.PrepareCredit(context.Background(), peer1Addr, testPaymentThreshold.Uint64(), false) 461 if err != nil { 462 t.Fatal(err) 463 } 464 465 // attempt further reserve, expect overdraft 466 467 _, err = acc.PrepareCredit(context.Background(), peer1Addr, 1, false) 468 if err == nil { 469 t.Fatal("expected error from reserve") 470 } 471 if !errors.Is(err, accounting.ErrOverdraft) { 472 t.Fatalf("expected overdraft error from reserve, got %v", err) 473 } 474 475 // pass time to increase reserve limit 476 477 ts = int64(1) 478 acc.SetTime(ts) 479 480 // reserve until limit extended by 1 second passed 481 482 credit1, err := acc.PrepareCredit(context.Background(), peer1Addr, uint64(testRefreshRate)-1, false) 483 if err != nil { 484 t.Fatal(err) 485 } 486 487 credit2, err := acc.PrepareCredit(context.Background(), peer1Addr, 1, false) 488 if err != nil { 489 t.Fatal(err) 490 } 491 492 // attempt further reserve, expect overdraft 493 494 _, err = acc.PrepareCredit(context.Background(), peer1Addr, 1, false) 495 if err == nil { 496 t.Fatal("expected error from reserve") 497 } 498 if !errors.Is(err, accounting.ErrOverdraft) { 499 t.Fatalf("expected overdraft error from reserve, got %v", err) 500 } 501 502 // apply less than minimal refreshment amount, expect no refreshment 503 504 err = credit1.Apply() 505 if err != nil { 506 t.Fatal(err) 507 } 508 509 select { 510 case <-refreshchan: 511 t.Fatal("unexpected refreshment below minimum debt") 512 case <-time.After(1 * time.Second): 513 } 514 515 // pass time to see it doesn't mean a further increase to reserve limit beyond 2 seconds 516 517 ts = int64(2) 518 acc.SetTime(ts) 519 520 // attempt further reserve, expect overdraft 521 522 _, err = acc.PrepareCredit(context.Background(), peer1Addr, 1, false) 523 if err == nil { 524 t.Fatal("expected error from reserve") 525 } 526 if !errors.Is(err, accounting.ErrOverdraft) { 527 t.Fatalf("expected overdraft error from reserve, got %v", err) 528 } 529 530 ts = int64(3) 531 acc.SetTime(ts) 532 533 // credit until minimal refreshment sent, expect refreshment made 534 535 err = credit2.Apply() 536 if err != nil { 537 t.Fatal(err) 538 } 539 540 select { 541 case call := <-refreshchan: 542 if call.amount.Cmp(big.NewInt(testRefreshRate)) != 0 { 543 t.Fatalf("paid wrong amount. got %d wanted %d", call.amount, testRefreshRate) 544 } 545 if !call.peer.Equal(peer1Addr) { 546 t.Fatalf("wrong peer address got %v wanted %v", call.peer, peer1Addr) 547 } 548 case <-time.After(1 * time.Second): 549 t.Fatal("timeout waiting for payment") 550 } 551 552 // pass time 553 554 ts = int64(4) 555 acc.SetTime(ts) 556 557 // see that refresh made further reserve possible 558 559 _, err = acc.PrepareCredit(context.Background(), peer1Addr, uint64(testRefreshRate), false) 560 if err != nil { 561 t.Fatal(err) 562 } 563 } 564 565 // TestAccountingDisconnect tests that exceeding the disconnect threshold with Debit returns a p2p.DisconnectError 566 func TestAccountingDisconnect(t *testing.T) { 567 t.Parallel() 568 569 logger := log.Noop 570 571 store := mock.NewStateStore() 572 defer store.Close() 573 574 pricing := &pricingMock{} 575 576 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 577 if err != nil { 578 t.Fatal(err) 579 } 580 581 peer1Addr, err := swarm.ParseHexAddress("00112233") 582 if err != nil { 583 t.Fatal(err) 584 } 585 586 acc.Connect(peer1Addr, true) 587 588 // put the peer 1 unit away from disconnect 589 debitAction, err := acc.PrepareDebit(context.Background(), peer1Addr, uint64(testRefreshRate)+(testPaymentThreshold.Uint64()*(100+uint64(testPaymentTolerance))/100)-1) 590 if err != nil { 591 t.Fatal(err) 592 } 593 err = debitAction.Apply() 594 if err != nil { 595 t.Fatal("expected no error while still within tolerance") 596 } 597 debitAction.Cleanup() 598 599 // put the peer over thee threshold 600 debitAction, err = acc.PrepareDebit(context.Background(), peer1Addr, 1) 601 if err != nil { 602 t.Fatal(err) 603 } 604 err = debitAction.Apply() 605 if err == nil { 606 t.Fatal("expected Add to return error") 607 } 608 debitAction.Cleanup() 609 610 var e *p2p.BlockPeerError 611 if !errors.As(err, &e) { 612 t.Fatalf("expected BlockPeerError, got %v", err) 613 } 614 } 615 616 // TestAccountingCallSettlement tests that settlement is called correctly if the payment threshold is hit 617 func TestAccountingCallSettlement(t *testing.T) { 618 t.Parallel() 619 620 logger := log.Noop 621 622 store := mock.NewStateStore() 623 defer store.Close() 624 625 pricing := &pricingMock{} 626 627 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 628 if err != nil { 629 t.Fatal(err) 630 } 631 632 refreshchan := make(chan paymentCall, 1) 633 634 f := func(ctx context.Context, peer swarm.Address, amount *big.Int) { 635 acc.NotifyRefreshmentSent(peer, amount, amount, 0, 0, nil) 636 refreshchan <- paymentCall{peer: peer, amount: amount} 637 } 638 639 pay := func(ctx context.Context, peer swarm.Address, amount *big.Int) { 640 } 641 642 acc.SetRefreshFunc(f) 643 acc.SetPayFunc(pay) 644 645 peer1Addr, err := swarm.ParseHexAddress("00112233") 646 if err != nil { 647 t.Fatal(err) 648 } 649 650 acc.Connect(peer1Addr, true) 651 652 requestPrice := testPaymentThreshold.Uint64() - 1000 653 654 creditAction, err := acc.PrepareCredit(context.Background(), peer1Addr, requestPrice, true) 655 if err != nil { 656 t.Fatal(err) 657 } 658 659 // Credit until payment threshold 660 err = creditAction.Apply() 661 if err != nil { 662 t.Fatal(err) 663 } 664 665 creditAction.Cleanup() 666 667 select { 668 case call := <-refreshchan: 669 if call.amount.Cmp(big.NewInt(int64(requestPrice))) != 0 { 670 t.Fatalf("paid wrong amount. got %d wanted %d", call.amount, requestPrice) 671 } 672 if !call.peer.Equal(peer1Addr) { 673 t.Fatalf("wrong peer address got %v wanted %v", call.peer, peer1Addr) 674 } 675 case <-time.After(1 * time.Second): 676 t.Fatal("timeout waiting for payment") 677 } 678 679 if acc.IsPaymentOngoing(peer1Addr) { 680 t.Fatal("triggered monetary settlement") 681 } 682 683 balance, err := acc.Balance(peer1Addr) 684 if err != nil { 685 t.Fatal(err) 686 } 687 if balance.Int64() != 0 { 688 t.Fatalf("expected balance to be reset. got %d", balance) 689 } 690 691 // Assume 100 is reserved by some other request 692 creditActionLong, err := acc.PrepareCredit(context.Background(), peer1Addr, 100, true) 693 if err != nil { 694 t.Fatal(err) 695 } 696 697 // Credit until the expected debt exceeds payment threshold 698 expectedAmount := testPaymentThreshold.Uint64() - 101 699 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, expectedAmount, true) 700 if err != nil { 701 t.Fatal(err) 702 } 703 704 err = creditAction.Apply() 705 if err != nil { 706 t.Fatal(err) 707 } 708 709 creditAction.Cleanup() 710 711 // try another request to trigger settlement 712 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, 1, true) 713 if err != nil { 714 t.Fatal(err) 715 } 716 717 creditAction.Cleanup() 718 719 select { 720 case call := <-refreshchan: 721 if call.amount.Cmp(big.NewInt(int64(expectedAmount))) != 0 { 722 t.Fatalf("paid wrong amount. got %d wanted %d", call.amount, expectedAmount) 723 } 724 if !call.peer.Equal(peer1Addr) { 725 t.Fatalf("wrong peer address got %v wanted %v", call.peer, peer1Addr) 726 } 727 case <-time.After(1 * time.Second): 728 t.Fatal("timeout waiting for payment") 729 } 730 731 if acc.IsPaymentOngoing(peer1Addr) { 732 t.Fatal("triggered monetary settlement") 733 } 734 creditActionLong.Cleanup() 735 } 736 737 func TestAccountingCallSettlementMonetary(t *testing.T) { 738 t.Parallel() 739 740 logger := log.Noop 741 742 store := mock.NewStateStore() 743 defer store.Close() 744 745 pricing := &pricingMock{} 746 747 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 748 if err != nil { 749 t.Fatal(err) 750 } 751 752 refreshchan := make(chan paymentCall, 1) 753 paychan := make(chan paymentCall, 1) 754 755 ts := int64(1000) 756 757 acc.SetTime(ts) 758 759 acc.SetRefreshFunc(func(ctx context.Context, peer swarm.Address, amount *big.Int) { 760 acc.NotifyRefreshmentSent(peer, amount, amount, ts*1000, 0, nil) 761 refreshchan <- paymentCall{peer: peer, amount: amount} 762 }) 763 764 acc.SetPayFunc(func(ctx context.Context, peer swarm.Address, amount *big.Int) { 765 acc.NotifyPaymentSent(peer, amount, nil) 766 paychan <- paymentCall{peer: peer, amount: amount} 767 }) 768 769 peer1Addr, err := swarm.ParseHexAddress("00112233") 770 if err != nil { 771 t.Fatal(err) 772 } 773 774 acc.Connect(peer1Addr, true) 775 776 requestPrice := testPaymentThreshold.Uint64() 777 778 creditAction, err := acc.PrepareCredit(context.Background(), peer1Addr, requestPrice, true) 779 if err != nil { 780 t.Fatal(err) 781 } 782 // Credit until payment threshold 783 err = creditAction.Apply() 784 if err != nil { 785 t.Fatal(err) 786 } 787 creditAction.Cleanup() 788 789 select { 790 case call := <-refreshchan: 791 if call.amount.Cmp(big.NewInt(int64(requestPrice))) != 0 { 792 t.Fatalf("paid wrong amount. got %d wanted %d", call.amount, requestPrice) 793 } 794 if !call.peer.Equal(peer1Addr) { 795 t.Fatalf("wrong peer address got %v wanted %v", call.peer, peer1Addr) 796 } 797 case <-time.After(1 * time.Second): 798 t.Fatal("timeout waiting for payment") 799 } 800 801 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, requestPrice, true) 802 if err != nil { 803 t.Fatal(err) 804 } 805 806 // Credit until payment threshold 807 err = creditAction.Apply() 808 if err != nil { 809 t.Fatal(err) 810 } 811 creditAction.Cleanup() 812 813 select { 814 case call := <-paychan: 815 if call.amount.Cmp(testPaymentThreshold) != 0 { 816 t.Fatalf("paid wrong amount. got %d wanted %d", call.amount, requestPrice) 817 } 818 if !call.peer.Equal(peer1Addr) { 819 t.Fatalf("wrong peer address got %v wanted %v", call.peer, peer1Addr) 820 } 821 case <-time.After(1 * time.Second): 822 t.Fatal("timeout waiting for payment") 823 } 824 825 balance, err := acc.Balance(peer1Addr) 826 if err != nil { 827 t.Fatal(err) 828 } 829 if balance.Cmp(big.NewInt(0)) != 0 { 830 t.Fatalf("expected balance to be adjusted. got %d", balance) 831 } 832 833 ts++ 834 acc.SetTime(ts) 835 836 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, requestPrice, true) 837 if err != nil { 838 t.Fatal(err) 839 } 840 err = creditAction.Apply() 841 if err != nil { 842 t.Fatal(err) 843 } 844 creditAction.Cleanup() 845 846 select { 847 case call := <-refreshchan: 848 if call.amount.Cmp(testPaymentThreshold) != 0 { 849 t.Fatalf("paid wrong amount. got %d wanted %d", call.amount, requestPrice) 850 } 851 if !call.peer.Equal(peer1Addr) { 852 t.Fatalf("wrong peer address got %v wanted %v", call.peer, peer1Addr) 853 } 854 case <-time.After(1 * time.Second): 855 t.Fatal("timeout waiting for refreshment") 856 } 857 858 notTimeSettleableAmount := new(big.Int).Sub(testPaymentThreshold, big.NewInt(testRefreshRate)) 859 860 select { 861 case call := <-paychan: 862 if call.amount.Cmp(notTimeSettleableAmount) != 0 { 863 t.Fatalf("paid wrong amount. got %d wanted %d", call.amount, requestPrice) 864 } 865 if !call.peer.Equal(peer1Addr) { 866 t.Fatalf("wrong peer address got %v wanted %v", call.peer, peer1Addr) 867 } 868 case <-time.After(1 * time.Second): 869 t.Fatal("timeout waiting for payment") 870 } 871 872 } 873 874 func TestAccountingCallSettlementTooSoon(t *testing.T) { 875 t.Parallel() 876 877 logger := log.Noop 878 store := mock.NewStateStore() 879 defer store.Close() 880 881 pricing := &pricingMock{} 882 883 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 884 if err != nil { 885 t.Fatal(err) 886 } 887 refreshchan := make(chan paymentCall, 1) 888 paychan := make(chan paymentCall, 1) 889 ts := int64(1000) 890 891 acc.SetRefreshFunc(func(ctx context.Context, peer swarm.Address, amount *big.Int) { 892 acc.NotifyRefreshmentSent(peer, amount, amount, ts*1000, 2, nil) 893 refreshchan <- paymentCall{peer: peer, amount: amount} 894 }) 895 896 acc.SetPayFunc(func(ctx context.Context, peer swarm.Address, amount *big.Int) { 897 paychan <- paymentCall{peer: peer, amount: amount} 898 }) 899 peer1Addr, err := swarm.ParseHexAddress("00112233") 900 if err != nil { 901 t.Fatal(err) 902 } 903 904 acc.Connect(peer1Addr, true) 905 906 requestPrice := testPaymentThreshold.Uint64() - 1000 907 908 creditAction, err := acc.PrepareCredit(context.Background(), peer1Addr, requestPrice, true) 909 if err != nil { 910 t.Fatal(err) 911 } 912 // Credit until payment threshold 913 err = creditAction.Apply() 914 if err != nil { 915 t.Fatal(err) 916 } 917 creditAction.Cleanup() 918 // try another request 919 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, 1, true) 920 if err != nil { 921 t.Fatal(err) 922 } 923 select { 924 case call := <-refreshchan: 925 if call.amount.Cmp(big.NewInt(int64(requestPrice))) != 0 { 926 t.Fatalf("paid wrong amount. got %d wanted %d", call.amount, requestPrice) 927 } 928 if !call.peer.Equal(peer1Addr) { 929 t.Fatalf("wrong peer address got %v wanted %v", call.peer, peer1Addr) 930 } 931 case <-time.After(1 * time.Second): 932 t.Fatal("timeout waiting for payment") 933 } 934 creditAction.Cleanup() 935 balance, err := acc.Balance(peer1Addr) 936 if err != nil { 937 t.Fatal(err) 938 } 939 if balance.Cmp(big.NewInt(0)) != 0 { 940 t.Fatalf("expected balance to be adjusted. got %d", balance) 941 } 942 acc.SetTime(ts) 943 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, requestPrice, true) 944 if err != nil { 945 t.Fatal(err) 946 } 947 // Credit until payment threshold 948 err = creditAction.Apply() 949 if err != nil { 950 t.Fatal(err) 951 } 952 creditAction.Cleanup() 953 // try another request 954 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, 1, true) 955 if err != nil { 956 t.Fatal(err) 957 } 958 select { 959 case <-refreshchan: 960 t.Fatal("sent refreshment") 961 default: 962 } 963 select { 964 case call := <-paychan: 965 if call.amount.Cmp(big.NewInt(int64(requestPrice))) != 0 { 966 t.Fatalf("paid wrong amount. got %d wanted %d", call.amount, requestPrice) 967 } 968 if !call.peer.Equal(peer1Addr) { 969 t.Fatalf("wrong peer address got %v wanted %v", call.peer, peer1Addr) 970 } 971 case <-time.After(1 * time.Second): 972 t.Fatal("payment not sent") 973 } 974 creditAction.Cleanup() 975 acc.NotifyPaymentSent(peer1Addr, big.NewInt(int64(requestPrice)), errors.New("error")) 976 acc.SetTime(ts + 1) 977 // try another request 978 _, err = acc.PrepareCredit(context.Background(), peer1Addr, 1, true) 979 if err != nil { 980 t.Fatal(err) 981 } 982 select { 983 case call := <-refreshchan: 984 if call.amount.Cmp(big.NewInt(int64(requestPrice))) != 0 { 985 t.Fatalf("paid wrong amount. got %d wanted %d", call.amount, requestPrice) 986 } 987 if !call.peer.Equal(peer1Addr) { 988 t.Fatalf("wrong peer address got %v wanted %v", call.peer, peer1Addr) 989 } 990 case <-time.After(1 * time.Second): 991 t.Fatal("no refreshment") 992 } 993 } 994 995 // TestAccountingCallSettlementEarly tests that settlement is called correctly if the payment threshold minus early payment is hit 996 func TestAccountingCallSettlementEarly(t *testing.T) { 997 t.Parallel() 998 999 logger := log.Noop 1000 1001 store := mock.NewStateStore() 1002 defer store.Close() 1003 1004 debt := uint64(500) 1005 earlyPayment := int64(10) 1006 1007 pricing := &pricingMock{} 1008 1009 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, earlyPayment, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 1010 if err != nil { 1011 t.Fatal(err) 1012 } 1013 1014 refreshchan := make(chan paymentCall, 1) 1015 1016 f := func(ctx context.Context, peer swarm.Address, amount *big.Int) { 1017 acc.NotifyRefreshmentSent(peer, amount, amount, 0, 0, nil) 1018 refreshchan <- paymentCall{peer: peer, amount: amount} 1019 } 1020 1021 acc.SetRefreshFunc(f) 1022 1023 peer1Addr, err := swarm.ParseHexAddress("00112233") 1024 if err != nil { 1025 t.Fatal(err) 1026 } 1027 1028 acc.Connect(peer1Addr, true) 1029 1030 creditAction, err := acc.PrepareCredit(context.Background(), peer1Addr, debt, true) 1031 if err != nil { 1032 t.Fatal(err) 1033 } 1034 if err = creditAction.Apply(); err != nil { 1035 t.Fatal(err) 1036 } 1037 creditAction.Cleanup() 1038 1039 debt2 := testPaymentThreshold.Uint64()*(100-uint64(earlyPayment))/100 - debt 1040 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, debt2, true) 1041 if err != nil { 1042 t.Fatal(err) 1043 } 1044 1045 err = creditAction.Apply() 1046 if err != nil { 1047 t.Fatal(err) 1048 } 1049 1050 creditAction.Cleanup() 1051 1052 select { 1053 case call := <-refreshchan: 1054 if call.amount.Cmp(big.NewInt(int64(debt+debt2))) != 0 { 1055 t.Fatalf("paid wrong amount. got %d wanted %d", call.amount, debt+debt2) 1056 } 1057 if !call.peer.Equal(peer1Addr) { 1058 t.Fatalf("wrong peer address got %v wanted %v", call.peer, peer1Addr) 1059 } 1060 case <-time.After(1 * time.Second): 1061 t.Fatal("timeout waiting for payment") 1062 } 1063 1064 balance, err := acc.Balance(peer1Addr) 1065 if err != nil { 1066 t.Fatal(err) 1067 } 1068 if balance.Int64() != 0 { 1069 t.Fatalf("expected balance to be reset. got %d", balance) 1070 } 1071 } 1072 1073 func TestAccountingSurplusBalance(t *testing.T) { 1074 t.Parallel() 1075 1076 logger := log.Noop 1077 1078 store := mock.NewStateStore() 1079 defer store.Close() 1080 1081 pricing := &pricingMock{} 1082 1083 acc, err := accounting.NewAccounting(testPaymentThreshold, 0, 0, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 1084 if err != nil { 1085 t.Fatal(err) 1086 } 1087 peer1Addr, err := swarm.ParseHexAddress("00112233") 1088 if err != nil { 1089 t.Fatal(err) 1090 } 1091 acc.Connect(peer1Addr, true) 1092 1093 // Try Debiting a large amount to peer so balance is large positive 1094 debitAction, err := acc.PrepareDebit(context.Background(), peer1Addr, testPaymentThreshold.Uint64()-1) 1095 if err != nil { 1096 t.Fatal(err) 1097 } 1098 err = debitAction.Apply() 1099 if err != nil { 1100 t.Fatal(err) 1101 } 1102 debitAction.Cleanup() 1103 // Notify of incoming payment from same peer, so balance goes to 0 with surplusbalance 2 1104 err = acc.NotifyPaymentReceived(peer1Addr, new(big.Int).Add(testPaymentThreshold, big.NewInt(1))) 1105 if err != nil { 1106 t.Fatal("Unexpected overflow from doable NotifyPayment") 1107 } 1108 //sanity check surplus balance 1109 val, err := acc.SurplusBalance(peer1Addr) 1110 if err != nil { 1111 t.Fatal("Error checking Surplusbalance") 1112 } 1113 if val.Int64() != 2 { 1114 t.Fatal("Not expected surplus balance") 1115 } 1116 //sanity check balance 1117 val, err = acc.Balance(peer1Addr) 1118 if err != nil { 1119 t.Fatal("Error checking Balance") 1120 } 1121 if val.Int64() != 0 { 1122 t.Fatal("Not expected balance") 1123 } 1124 // Notify of incoming payment from same peer, so balance goes to 0 with surplusbalance 10002 (testpaymentthreshold+2) 1125 err = acc.NotifyPaymentReceived(peer1Addr, testPaymentThreshold) 1126 if err != nil { 1127 t.Fatal("Unexpected error from NotifyPayment") 1128 } 1129 //sanity check surplus balance 1130 val, err = acc.SurplusBalance(peer1Addr) 1131 if err != nil { 1132 t.Fatal("Error checking Surplusbalance") 1133 } 1134 if val.Int64() != testPaymentThreshold.Int64()+2 { 1135 t.Fatal("Unexpected surplus balance") 1136 } 1137 //sanity check balance 1138 val, err = acc.Balance(peer1Addr) 1139 if err != nil { 1140 t.Fatal("Error checking Balance") 1141 } 1142 if val.Int64() != 0 { 1143 t.Fatal("Not expected balance, expected 0") 1144 } 1145 // Debit for same peer, so balance stays 0 with surplusbalance decreasing to 2 1146 debitAction, err = acc.PrepareDebit(context.Background(), peer1Addr, testPaymentThreshold.Uint64()) 1147 if err != nil { 1148 t.Fatal(err) 1149 } 1150 err = debitAction.Apply() 1151 if err != nil { 1152 t.Fatal("Unexpected error from Credit") 1153 } 1154 debitAction.Cleanup() 1155 // sanity check surplus balance 1156 val, err = acc.SurplusBalance(peer1Addr) 1157 if err != nil { 1158 t.Fatal("Error checking Surplusbalance") 1159 } 1160 if val.Int64() != 2 { 1161 t.Fatal("Unexpected surplus balance") 1162 } 1163 //sanity check balance 1164 val, err = acc.Balance(peer1Addr) 1165 if err != nil { 1166 t.Fatal("Error checking Balance") 1167 } 1168 if val.Int64() != 0 { 1169 t.Fatal("Not expected balance, expected 0") 1170 } 1171 // Debit for same peer, so balance goes to 9998 (testpaymentthreshold - 2) with surplusbalance decreasing to 0 1172 debitAction, err = acc.PrepareDebit(context.Background(), peer1Addr, testPaymentThreshold.Uint64()) 1173 if err != nil { 1174 t.Fatal(err) 1175 } 1176 err = debitAction.Apply() 1177 if err != nil { 1178 t.Fatal("Unexpected error from Debit") 1179 } 1180 debitAction.Cleanup() 1181 // sanity check surplus balance 1182 val, err = acc.SurplusBalance(peer1Addr) 1183 if err != nil { 1184 t.Fatal("Error checking Surplusbalance") 1185 } 1186 if val.Int64() != 0 { 1187 t.Fatal("Unexpected surplus balance") 1188 } 1189 //sanity check balance 1190 val, err = acc.Balance(peer1Addr) 1191 if err != nil { 1192 t.Fatal("Error checking Balance") 1193 } 1194 if val.Int64() != testPaymentThreshold.Int64()-2 { 1195 t.Fatal("Not expected balance, expected 0") 1196 } 1197 } 1198 1199 // TestAccountingNotifyPayment tests that payments adjust the balance 1200 func TestAccountingNotifyPaymentReceived(t *testing.T) { 1201 t.Parallel() 1202 1203 logger := log.Noop 1204 1205 store := mock.NewStateStore() 1206 defer store.Close() 1207 1208 pricing := &pricingMock{} 1209 1210 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 1211 if err != nil { 1212 t.Fatal(err) 1213 } 1214 1215 peer1Addr, err := swarm.ParseHexAddress("00112233") 1216 if err != nil { 1217 t.Fatal(err) 1218 } 1219 1220 acc.Connect(peer1Addr, true) 1221 1222 debtAmount := uint64(100) 1223 debitAction, err := acc.PrepareDebit(context.Background(), peer1Addr, debtAmount) 1224 if err != nil { 1225 t.Fatal(err) 1226 } 1227 err = debitAction.Apply() 1228 if err != nil { 1229 t.Fatal(err) 1230 } 1231 debitAction.Cleanup() 1232 1233 err = acc.NotifyPaymentReceived(peer1Addr, new(big.Int).SetUint64(debtAmount)) 1234 if err != nil { 1235 t.Fatal(err) 1236 } 1237 1238 debitAction, err = acc.PrepareDebit(context.Background(), peer1Addr, debtAmount) 1239 if err != nil { 1240 t.Fatal(err) 1241 } 1242 err = debitAction.Apply() 1243 if err != nil { 1244 t.Fatal(err) 1245 } 1246 debitAction.Cleanup() 1247 1248 err = acc.NotifyPaymentReceived(peer1Addr, new(big.Int).SetUint64(debtAmount)) 1249 if err != nil { 1250 t.Fatal(err) 1251 } 1252 } 1253 1254 type pricingMock struct { 1255 called bool 1256 peer swarm.Address 1257 paymentThreshold *big.Int 1258 } 1259 1260 func (p *pricingMock) AnnouncePaymentThreshold(ctx context.Context, peer swarm.Address, paymentThreshold *big.Int) error { 1261 p.called = true 1262 p.peer = peer 1263 p.paymentThreshold = paymentThreshold 1264 return nil 1265 } 1266 1267 func TestAccountingConnected(t *testing.T) { 1268 t.Parallel() 1269 1270 logger := log.Noop 1271 1272 store := mock.NewStateStore() 1273 defer store.Close() 1274 1275 pricing := &pricingMock{} 1276 1277 _, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 1278 if err != nil { 1279 t.Fatal(err) 1280 } 1281 1282 peer1Addr, err := swarm.ParseHexAddress("00112233") 1283 if err != nil { 1284 t.Fatal(err) 1285 } 1286 1287 err = pricing.AnnouncePaymentThreshold(context.Background(), peer1Addr, testPaymentThreshold) 1288 if err != nil { 1289 t.Fatal(err) 1290 } 1291 1292 if !pricing.called { 1293 t.Fatal("expected pricing to be called") 1294 } 1295 1296 if !pricing.peer.Equal(peer1Addr) { 1297 t.Fatalf("paid to wrong peer. got %v wanted %v", pricing.peer, peer1Addr) 1298 } 1299 1300 if pricing.paymentThreshold.Cmp(testPaymentThreshold) != 0 { 1301 t.Fatalf("paid wrong amount. got %d wanted %d", pricing.paymentThreshold, testPaymentThreshold) 1302 } 1303 } 1304 1305 func TestAccountingNotifyPaymentThreshold(t *testing.T) { 1306 t.Parallel() 1307 1308 logger := log.Noop 1309 1310 store := mock.NewStateStore() 1311 defer store.Close() 1312 1313 pricing := &pricingMock{} 1314 1315 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, 0, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 1316 if err != nil { 1317 t.Fatal(err) 1318 } 1319 1320 refreshchan := make(chan paymentCall, 1) 1321 1322 f := func(ctx context.Context, peer swarm.Address, amount *big.Int) { 1323 acc.NotifyRefreshmentSent(peer, amount, amount, 0, 0, nil) 1324 refreshchan <- paymentCall{peer: peer, amount: amount} 1325 } 1326 1327 acc.SetRefreshFunc(f) 1328 1329 peer1Addr, err := swarm.ParseHexAddress("00112233") 1330 if err != nil { 1331 t.Fatal(err) 1332 } 1333 1334 acc.Connect(peer1Addr, true) 1335 1336 debt := 2 * uint64(testRefreshRate) 1337 lowerThreshold := debt 1338 1339 err = acc.NotifyPaymentThreshold(peer1Addr, new(big.Int).SetUint64(lowerThreshold)) 1340 if err != nil { 1341 t.Fatal(err) 1342 } 1343 1344 creditAction, err := acc.PrepareCredit(context.Background(), peer1Addr, debt, true) 1345 if err != nil { 1346 t.Fatal(err) 1347 } 1348 if err = creditAction.Apply(); err != nil { 1349 t.Fatal(err) 1350 } 1351 creditAction.Cleanup() 1352 1353 select { 1354 case call := <-refreshchan: 1355 if call.amount.Cmp(big.NewInt(int64(debt))) != 0 { 1356 t.Fatalf("paid wrong amount. got %d wanted %d", call.amount, debt) 1357 } 1358 if !call.peer.Equal(peer1Addr) { 1359 t.Fatalf("wrong peer address got %v wanted %v", call.peer, peer1Addr) 1360 } 1361 case <-time.After(1 * time.Second): 1362 t.Fatal("timeout waiting for payment") 1363 } 1364 } 1365 1366 func TestAccountingPeerDebt(t *testing.T) { 1367 t.Parallel() 1368 1369 logger := log.Noop 1370 1371 store := mock.NewStateStore() 1372 defer store.Close() 1373 1374 pricing := &pricingMock{} 1375 1376 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, 0, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 1377 if err != nil { 1378 t.Fatal(err) 1379 } 1380 1381 peer1Addr := swarm.MustParseHexAddress("00112233") 1382 acc.Connect(peer1Addr, true) 1383 1384 debt := uint64(1000) 1385 debitAction, err := acc.PrepareDebit(context.Background(), peer1Addr, debt) 1386 if err != nil { 1387 t.Fatal(err) 1388 } 1389 err = debitAction.Apply() 1390 if err != nil { 1391 t.Fatal(err) 1392 } 1393 debitAction.Cleanup() 1394 actualDebt, err := acc.PeerDebt(peer1Addr) 1395 if err != nil { 1396 t.Fatal(err) 1397 } 1398 if actualDebt.Cmp(new(big.Int).SetUint64(debt)) != 0 { 1399 t.Fatalf("wrong actual debt. got %d wanted %d", actualDebt, debt) 1400 } 1401 1402 peer2Addr := swarm.MustParseHexAddress("11112233") 1403 acc.Connect(peer2Addr, true) 1404 creditAction, err := acc.PrepareCredit(context.Background(), peer2Addr, 500, true) 1405 if err != nil { 1406 t.Fatal(err) 1407 } 1408 if err = creditAction.Apply(); err != nil { 1409 t.Fatal(err) 1410 } 1411 creditAction.Cleanup() 1412 actualDebt, err = acc.PeerDebt(peer2Addr) 1413 if err != nil { 1414 t.Fatal(err) 1415 } 1416 if actualDebt.Cmp(big.NewInt(0)) != 0 { 1417 t.Fatalf("wrong actual debt. got %d wanted 0", actualDebt) 1418 } 1419 1420 peer3Addr := swarm.MustParseHexAddress("22112233") 1421 actualDebt, err = acc.PeerDebt(peer3Addr) 1422 if err != nil { 1423 t.Fatal(err) 1424 } 1425 if actualDebt.Cmp(big.NewInt(0)) != 0 { 1426 t.Fatalf("wrong actual debt. got %d wanted 0", actualDebt) 1427 } 1428 } 1429 1430 func TestAccountingCallPaymentErrorRetries(t *testing.T) { 1431 t.Parallel() 1432 1433 logger := log.Noop 1434 1435 store := mock.NewStateStore() 1436 defer store.Close() 1437 1438 pricing := &pricingMock{} 1439 1440 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(1), testLightFactor, p2pmock.New()) 1441 if err != nil { 1442 t.Fatal(err) 1443 } 1444 1445 refreshchan := make(chan paymentCall, 1) 1446 paychan := make(chan paymentCall, 1) 1447 1448 ts := int64(100) 1449 acc.SetTime(ts) 1450 1451 acc.SetRefreshFunc(func(ctx context.Context, peer swarm.Address, amount *big.Int) { 1452 acc.NotifyRefreshmentSent(peer, amount, big.NewInt(2), ts*1000, 0, nil) 1453 refreshchan <- paymentCall{peer: peer, amount: amount} 1454 }) 1455 1456 acc.SetPayFunc(func(ctx context.Context, peer swarm.Address, amount *big.Int) { 1457 paychan <- paymentCall{peer: peer, amount: amount} 1458 }) 1459 1460 peer1Addr, err := swarm.ParseHexAddress("00112233") 1461 if err != nil { 1462 t.Fatal(err) 1463 } 1464 acc.Connect(peer1Addr, true) 1465 1466 requestPrice := testPaymentThreshold.Uint64() - 100 1467 1468 // Credit until near payment threshold 1469 creditAction, err := acc.PrepareCredit(context.Background(), peer1Addr, requestPrice, true) 1470 if err != nil { 1471 t.Fatal(err) 1472 } 1473 if err = creditAction.Apply(); err != nil { 1474 t.Fatal(err) 1475 } 1476 creditAction.Cleanup() 1477 1478 select { 1479 case <-refreshchan: 1480 case <-time.After(1 * time.Second): 1481 t.Fatalf("expected refreshment") 1482 } 1483 1484 // Credit until near payment threshold 1485 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, 80, true) 1486 if err != nil { 1487 t.Fatal(err) 1488 } 1489 if err = creditAction.Apply(); err != nil { 1490 t.Fatal(err) 1491 } 1492 1493 var sentAmount *big.Int 1494 select { 1495 case call := <-paychan: 1496 sentAmount = call.amount 1497 case <-time.After(1 * time.Second): 1498 t.Fatal("payment expected to be sent") 1499 } 1500 1501 creditAction.Cleanup() 1502 1503 acc.NotifyPaymentSent(peer1Addr, sentAmount, errors.New("error")) 1504 1505 // try another n requests 1 per second 1506 for i := 0; i < 10; i++ { 1507 ts++ 1508 acc.SetTime(ts) 1509 1510 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, 2, true) 1511 if err != nil { 1512 t.Fatal(err) 1513 } 1514 1515 err = creditAction.Apply() 1516 if err != nil { 1517 t.Fatal(err) 1518 } 1519 1520 select { 1521 case <-refreshchan: 1522 case <-time.After(1 * time.Second): 1523 t.Fatal("expected refreshment") 1524 } 1525 1526 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, 2, true) 1527 if err != nil { 1528 t.Fatal(err) 1529 } 1530 1531 err = creditAction.Apply() 1532 if err != nil { 1533 t.Fatal(err) 1534 } 1535 1536 if acc.IsPaymentOngoing(peer1Addr) { 1537 t.Fatal("unexpected ongoing payment") 1538 } 1539 1540 creditAction.Cleanup() 1541 } 1542 1543 ts++ 1544 acc.SetTime(ts) 1545 1546 // try another request that uses refreshment 1547 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, 2, true) 1548 if err != nil { 1549 t.Fatal(err) 1550 } 1551 err = creditAction.Apply() 1552 if err != nil { 1553 t.Fatal(err) 1554 } 1555 1556 select { 1557 case <-refreshchan: 1558 case <-time.After(1 * time.Second): 1559 t.Fatalf("expected refreshment") 1560 1561 } 1562 1563 // try another request that uses payment as it happens in the second of last refreshment 1564 creditAction, err = acc.PrepareCredit(context.Background(), peer1Addr, 2, true) 1565 if err != nil { 1566 t.Fatal(err) 1567 } 1568 err = creditAction.Apply() 1569 if err != nil { 1570 t.Fatal(err) 1571 } 1572 1573 select { 1574 case <-paychan: 1575 case <-time.After(500 * time.Millisecond): 1576 t.Fatal("payment expected to be sent") 1577 } 1578 1579 creditAction.Cleanup() 1580 } 1581 1582 var errInvalidReason = errors.New("invalid blocklist reason") 1583 1584 func TestAccountingGhostOverdraft(t *testing.T) { 1585 t.Parallel() 1586 1587 logger := log.Noop 1588 1589 store := mock.NewStateStore() 1590 defer store.Close() 1591 1592 var blocklistTime int64 1593 1594 paymentThresholdInRefreshmentSeconds := new(big.Int).Div(testPaymentThreshold, big.NewInt(testRefreshRate)).Uint64() 1595 1596 f := func(s swarm.Address, t time.Duration, reason string) error { 1597 if reason != "ghost overdraw" { 1598 return errInvalidReason 1599 } 1600 blocklistTime = int64(t.Seconds()) 1601 return nil 1602 } 1603 1604 pricing := &pricingMock{} 1605 1606 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New(p2pmock.WithBlocklistFunc(f))) 1607 if err != nil { 1608 t.Fatal(err) 1609 } 1610 1611 ts := int64(1000) 1612 acc.SetTime(ts) 1613 1614 peer, err := swarm.ParseHexAddress("00112233") 1615 if err != nil { 1616 t.Fatal(err) 1617 } 1618 acc.Connect(peer, true) 1619 1620 requestPrice := testPaymentThreshold.Uint64() 1621 1622 debitActionNormal, err := acc.PrepareDebit(context.Background(), peer, requestPrice) 1623 if err != nil { 1624 t.Fatal(err) 1625 } 1626 err = debitActionNormal.Apply() 1627 if err != nil { 1628 t.Fatal(err) 1629 } 1630 debitActionNormal.Cleanup() 1631 1632 // debit ghost balance 1633 debitActionGhost, err := acc.PrepareDebit(context.Background(), peer, requestPrice) 1634 if err != nil { 1635 t.Fatal(err) 1636 } 1637 debitActionGhost.Cleanup() 1638 1639 // increase shadow reserve 1640 debitActionShadow, err := acc.PrepareDebit(context.Background(), peer, requestPrice) 1641 if err != nil { 1642 t.Fatal(err) 1643 } 1644 _ = debitActionShadow 1645 1646 if blocklistTime != 0 { 1647 t.Fatal("unexpected blocklist") 1648 } 1649 1650 // ghost overdraft triggering blocklist 1651 debitAction4, err := acc.PrepareDebit(context.Background(), peer, requestPrice) 1652 if err != nil { 1653 t.Fatal(err) 1654 } 1655 debitAction4.Cleanup() 1656 1657 if blocklistTime != int64(5*paymentThresholdInRefreshmentSeconds) { 1658 t.Fatalf("unexpected blocklisting time, got %v expected %v", blocklistTime, 5*paymentThresholdInRefreshmentSeconds) 1659 } 1660 } 1661 1662 func TestAccountingReconnectBeforeAllowed(t *testing.T) { 1663 t.Parallel() 1664 1665 logger := log.Noop 1666 1667 store := mock.NewStateStore() 1668 defer store.Close() 1669 1670 var blocklistTime int64 1671 1672 paymentThresholdInRefreshmentSeconds := new(big.Int).Div(testPaymentThreshold, big.NewInt(testRefreshRate)).Uint64() 1673 1674 f := func(s swarm.Address, t time.Duration, reason string) error { 1675 if reason != "accounting disconnect" { 1676 return errInvalidReason 1677 } 1678 blocklistTime = int64(t.Seconds()) 1679 return nil 1680 } 1681 1682 pricing := &pricingMock{} 1683 1684 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New(p2pmock.WithBlocklistFunc(f))) 1685 if err != nil { 1686 t.Fatal(err) 1687 } 1688 1689 ts := int64(1000) 1690 acc.SetTime(ts) 1691 1692 peer, err := swarm.ParseHexAddress("00112233") 1693 if err != nil { 1694 t.Fatal(err) 1695 } 1696 acc.Connect(peer, true) 1697 1698 requestPrice := testPaymentThreshold.Uint64() 1699 1700 debitActionNormal, err := acc.PrepareDebit(context.Background(), peer, requestPrice) 1701 if err != nil { 1702 t.Fatal(err) 1703 } 1704 err = debitActionNormal.Apply() 1705 if err != nil { 1706 t.Fatal(err) 1707 } 1708 debitActionNormal.Cleanup() 1709 1710 // debit ghost balance 1711 debitActionGhost, err := acc.PrepareDebit(context.Background(), peer, requestPrice) 1712 if err != nil { 1713 t.Fatal(err) 1714 } 1715 debitActionGhost.Cleanup() 1716 1717 // increase shadow reserve 1718 debitActionShadow, err := acc.PrepareDebit(context.Background(), peer, requestPrice) 1719 if err != nil { 1720 t.Fatal(err) 1721 } 1722 _ = debitActionShadow 1723 1724 if blocklistTime != 0 { 1725 t.Fatal("unexpected blocklist") 1726 } 1727 1728 acc.Disconnect(peer) 1729 1730 if blocklistTime != int64(4*paymentThresholdInRefreshmentSeconds) { 1731 t.Fatalf("unexpected blocklisting time, got %v expected %v", blocklistTime, 4*paymentThresholdInRefreshmentSeconds) 1732 } 1733 1734 } 1735 1736 func TestAccountingResetBalanceAfterReconnect(t *testing.T) { 1737 t.Parallel() 1738 1739 logger := log.Noop 1740 1741 store := mock.NewStateStore() 1742 defer store.Close() 1743 1744 var blocklistTime int64 1745 1746 paymentThresholdInRefreshmentSeconds := new(big.Int).Div(testPaymentThreshold, big.NewInt(testRefreshRate)).Uint64() 1747 1748 f := func(s swarm.Address, t time.Duration, reason string) error { 1749 if reason != "accounting disconnect" { 1750 return errInvalidReason 1751 } 1752 blocklistTime = int64(t.Seconds()) 1753 return nil 1754 } 1755 1756 pricing := &pricingMock{} 1757 1758 acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New(p2pmock.WithBlocklistFunc(f))) 1759 if err != nil { 1760 t.Fatal(err) 1761 } 1762 1763 ts := int64(1000) 1764 acc.SetTime(ts) 1765 1766 peer, err := swarm.ParseHexAddress("00112233") 1767 if err != nil { 1768 t.Fatal(err) 1769 } 1770 1771 acc.Connect(peer, true) 1772 1773 requestPrice := testPaymentThreshold.Uint64() 1774 1775 debitActionNormal, err := acc.PrepareDebit(context.Background(), peer, requestPrice) 1776 if err != nil { 1777 t.Fatal(err) 1778 } 1779 err = debitActionNormal.Apply() 1780 if err != nil { 1781 t.Fatal(err) 1782 } 1783 debitActionNormal.Cleanup() 1784 1785 // debit ghost balance 1786 debitActionGhost, err := acc.PrepareDebit(context.Background(), peer, requestPrice) 1787 if err != nil { 1788 t.Fatal(err) 1789 } 1790 debitActionGhost.Cleanup() 1791 1792 // increase shadow reserve 1793 debitActionShadow, err := acc.PrepareDebit(context.Background(), peer, requestPrice) 1794 if err != nil { 1795 t.Fatal(err) 1796 } 1797 _ = debitActionShadow 1798 1799 if blocklistTime != 0 { 1800 t.Fatal("unexpected blocklist") 1801 } 1802 1803 acc.Disconnect(peer) 1804 1805 if blocklistTime != int64(4*paymentThresholdInRefreshmentSeconds) { 1806 t.Fatalf("unexpected blocklisting time, got %v expected %v", blocklistTime, 4*paymentThresholdInRefreshmentSeconds) 1807 } 1808 1809 acc.Connect(peer, true) 1810 1811 balance, err := acc.Balance(peer) 1812 if err != nil { 1813 t.Fatal(err) 1814 } 1815 1816 if balance.Int64() != 0 { 1817 t.Fatalf("balance for peer %v not as expected got %d, wanted 0", peer.String(), balance) 1818 } 1819 1820 surplusBalance, err := acc.SurplusBalance(peer) 1821 if err != nil { 1822 t.Fatal(err) 1823 } 1824 1825 if surplusBalance.Int64() != 0 { 1826 t.Fatalf("surplus balance for peer %v not as expected got %d, wanted 0", peer.String(), balance) 1827 } 1828 1829 } 1830 1831 func testAccountingSettlementGrowingThresholds(t *testing.T, settleFunc func(t *testing.T, acc *accounting.Accounting, peer1Addr swarm.Address, debitRefresh int64), fullNode bool, testPayThreshold *big.Int, testGrowth int64) { 1832 t.Helper() 1833 1834 logger := log.Noop 1835 1836 store := mock.NewStateStore() 1837 defer store.Close() 1838 1839 pricing := &pricingMock{} 1840 1841 acc, err := accounting.NewAccounting(testPaymentThreshold, 0, 0, logger, store, pricing, big.NewInt(testRefreshRate), testLightFactor, p2pmock.New()) 1842 if err != nil { 1843 t.Fatal(err) 1844 } 1845 peer1Addr, err := swarm.ParseHexAddress("00112233") 1846 if err != nil { 1847 t.Fatal(err) 1848 } 1849 1850 err = pricing.AnnouncePaymentThreshold(context.Background(), peer1Addr, testPayThreshold) 1851 if err != nil { 1852 t.Fatal(err) 1853 } 1854 1855 acc.Connect(peer1Addr, fullNode) 1856 1857 checkPaymentThreshold := new(big.Int).Set(testPayThreshold) 1858 1859 // Simulate first 18 threshold upgrades 1860 for j := 0; j < 18; j++ { 1861 for i := 0; i < 100; i++ { 1862 1863 // expect no change in threshold while less than 100 seconds worth of refreshment rate was settled 1864 settleFunc(t, acc, peer1Addr, testGrowth-1) 1865 1866 if pricing.paymentThreshold.Cmp(checkPaymentThreshold) != 0 { 1867 t.Fatalf("expected threshold %v got %v", checkPaymentThreshold, pricing.paymentThreshold) 1868 } 1869 1870 } 1871 1872 // Cumulative settled debt is now "checkpoint - 100 + j" ( j < 18 ) 1873 1874 // Expect increase after 100 seconds of refreshment is crossed 1875 1876 checkPaymentThreshold = new(big.Int).Add(checkPaymentThreshold, big.NewInt(testGrowth)) 1877 1878 // Cross 1879 settleFunc(t, acc, peer1Addr, 101) 1880 1881 // Cumulative settled debt is now "checkpoint + j + 1" ( j < 18 ) 1882 1883 // Check increase happened (meaning pricing AnnounceThreshold was called by accounting) 1884 if pricing.paymentThreshold.Cmp(checkPaymentThreshold) != 0 { 1885 t.Fatalf("expected threshold %v got %v", checkPaymentThreshold, pricing.paymentThreshold) 1886 } 1887 1888 } 1889 1890 // Simulate first exponential checkpoint 1891 1892 // Expect no increase for the next 179 seconds of refreshment 1893 1894 for k := 0; k < 1799; k++ { 1895 1896 settleFunc(t, acc, peer1Addr, testGrowth) 1897 1898 // Check threshold have not been updated 1899 if pricing.paymentThreshold.Cmp(checkPaymentThreshold) != 0 { 1900 t.Fatalf("expected threshold %v got %v", checkPaymentThreshold, pricing.paymentThreshold) 1901 } 1902 1903 } 1904 1905 // Expect increase after the 1800th second worth of refreshment settled 1906 1907 checkPaymentThreshold = new(big.Int).Add(checkPaymentThreshold, big.NewInt(testGrowth)) 1908 1909 settleFunc(t, acc, peer1Addr, testGrowth) 1910 1911 // Check increase happened (meaning pricing AnnounceThreshold was called by accounting) 1912 if pricing.paymentThreshold.Cmp(checkPaymentThreshold) != 0 { 1913 t.Fatalf("expected threshold %v got %v", checkPaymentThreshold, pricing.paymentThreshold) 1914 } 1915 1916 // Simulate second exponential checkpoint 1917 1918 // Expect no increase for another 3599 seconds of refreshments 1919 1920 for k := 0; k < 3599; k++ { 1921 1922 settleFunc(t, acc, peer1Addr, testGrowth) 1923 1924 // Check threshold have not been updated 1925 if pricing.paymentThreshold.Cmp(checkPaymentThreshold) != 0 { 1926 t.Fatalf("expected threshold %v got %v", checkPaymentThreshold, pricing.paymentThreshold) 1927 } 1928 1929 } 1930 1931 // Expect increase after the 360th second worth of refreshment settled 1932 1933 checkPaymentThreshold = new(big.Int).Add(checkPaymentThreshold, big.NewInt(testGrowth)) 1934 1935 settleFunc(t, acc, peer1Addr, testGrowth) 1936 1937 // Check increase happened (meaning pricing AnnounceThreshold was called by accounting) 1938 if pricing.paymentThreshold.Cmp(checkPaymentThreshold) != 0 { 1939 t.Fatalf("expected threshold %v got %v", checkPaymentThreshold, pricing.paymentThreshold) 1940 } 1941 1942 } 1943 1944 func TestAccountingRefreshGrowingThresholds(t *testing.T) { 1945 t.Parallel() 1946 1947 testAccountingSettlementGrowingThresholds(t, debitAndRefresh, true, testPaymentThreshold, testRefreshRate) 1948 1949 } 1950 1951 func TestAccountingRefreshGrowingThresholdsLight(t *testing.T) { 1952 t.Parallel() 1953 1954 lightPaymentThresholdDefault := new(big.Int).Div(testPaymentThreshold, big.NewInt(testLightFactor)) 1955 lightRefreshRate := testRefreshRate / testLightFactor 1956 1957 testAccountingSettlementGrowingThresholds(t, debitAndRefresh, false, lightPaymentThresholdDefault, lightRefreshRate) 1958 1959 } 1960 1961 func TestAccountingSwapGrowingThresholds(t *testing.T) { 1962 t.Parallel() 1963 1964 testAccountingSettlementGrowingThresholds(t, debitAndReceivePayment, true, testPaymentThreshold, testRefreshRate) 1965 1966 } 1967 1968 func TestAccountingSwapGrowingThresholdsLight(t *testing.T) { 1969 t.Parallel() 1970 1971 lightPaymentThresholdDefault := new(big.Int).Div(testPaymentThreshold, big.NewInt(testLightFactor)) 1972 lightRefreshRate := testRefreshRate / testLightFactor 1973 1974 testAccountingSettlementGrowingThresholds(t, debitAndReceivePayment, false, lightPaymentThresholdDefault, lightRefreshRate) 1975 1976 } 1977 1978 func debitAndRefresh(t *testing.T, acc *accounting.Accounting, peer1Addr swarm.Address, debitRefresh int64) { 1979 t.Helper() 1980 1981 // Create debt 1982 debitAction, err := acc.PrepareDebit(context.Background(), peer1Addr, uint64(debitRefresh)) 1983 if err != nil { 1984 t.Fatal(err) 1985 } 1986 err = debitAction.Apply() 1987 if err != nil { 1988 t.Fatal(err) 1989 } 1990 debitAction.Cleanup() 1991 1992 // Refresh 1993 err = acc.NotifyRefreshmentReceived(peer1Addr, big.NewInt(debitRefresh), time.Now().Unix()) 1994 if err != nil { 1995 t.Fatalf("unexpected error from NotifyRefreshmentReceived: %v", err) 1996 } 1997 1998 } 1999 2000 func debitAndReceivePayment(t *testing.T, acc *accounting.Accounting, peer1Addr swarm.Address, debitRefresh int64) { 2001 t.Helper() 2002 2003 // Create debt 2004 debitAction, err := acc.PrepareDebit(context.Background(), peer1Addr, uint64(debitRefresh)) 2005 if err != nil { 2006 t.Fatal(err) 2007 } 2008 err = debitAction.Apply() 2009 if err != nil { 2010 t.Fatal(err) 2011 } 2012 debitAction.Cleanup() 2013 2014 // Refresh 2015 err = acc.NotifyPaymentReceived(peer1Addr, big.NewInt(debitRefresh)) 2016 if err != nil { 2017 t.Fatalf("unexpected error from NotifyRefreshmentReceived: %v", err) 2018 } 2019 2020 }