github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/session/pingpong/invoice_payer_test.go (about) 1 /* 2 * Copyright (C) 2019 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package pingpong 19 20 import ( 21 "math/big" 22 "os" 23 "sync" 24 "testing" 25 "time" 26 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/mysteriumnetwork/node/core/storage/boltdb" 29 "github.com/mysteriumnetwork/node/eventbus" 30 "github.com/mysteriumnetwork/node/identity" 31 "github.com/mysteriumnetwork/node/market" 32 "github.com/mysteriumnetwork/node/mocks" 33 "github.com/mysteriumnetwork/node/session" 34 "github.com/mysteriumnetwork/node/session/mbtime" 35 "github.com/mysteriumnetwork/node/session/pingpong/event" 36 "github.com/mysteriumnetwork/payments/crypto" 37 "github.com/pkg/errors" 38 "github.com/stretchr/testify/assert" 39 ) 40 41 type MockPeerExchangeMessageSender struct { 42 mockError error 43 chanToWriteTo chan crypto.ExchangeMessage 44 } 45 46 func (mpems *MockPeerExchangeMessageSender) Send(em crypto.ExchangeMessage) error { 47 if mpems.chanToWriteTo != nil { 48 mpems.chanToWriteTo <- em 49 } 50 return mpems.mockError 51 } 52 53 func Test_InvoicePayer_Start_Stop(t *testing.T) { 54 ks := identity.NewMockKeystore() 55 acc, err := ks.NewAccount("") 56 assert.Nil(t, err) 57 58 mockSender := &MockPeerExchangeMessageSender{ 59 chanToWriteTo: make(chan crypto.ExchangeMessage, 10), 60 } 61 62 invoiceChan := make(chan crypto.Invoice) 63 tracker := session.NewTracker(mbtime.Now) 64 totalsStorage := NewConsumerTotalsStorage(eventbus.New()) 65 deps := InvoicePayerDeps{ 66 InvoiceChan: invoiceChan, 67 PeerExchangeMessageSender: mockSender, 68 ConsumerTotalsStorage: totalsStorage, 69 TimeTracker: &tracker, 70 Ks: ks, 71 AddressProvider: &mockAddressProvider{}, 72 Identity: identity.FromAddress(acc.Address.Hex()), 73 Peer: identity.FromAddress("0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C"), 74 EventBus: mocks.NewEventBus(), 75 AgreedPrice: *market.NewPrice(600, 0), 76 } 77 InvoicePayer := NewInvoicePayer(deps) 78 79 go func() { 80 time.Sleep(time.Nanosecond * 10) 81 InvoicePayer.Stop() 82 }() 83 84 err = InvoicePayer.Start() 85 assert.Nil(t, err) 86 } 87 88 func Test_InvoicePayer_SendsMessage(t *testing.T) { 89 dir, err := os.MkdirTemp("", "exchange_message_tracker_test") 90 assert.Nil(t, err) 91 defer os.RemoveAll(dir) 92 93 ks := identity.NewMockKeystore() 94 acc, err := ks.NewAccount("") 95 assert.Nil(t, err) 96 97 err = ks.Unlock(acc, "") 98 assert.Nil(t, err) 99 100 mockSender := &MockPeerExchangeMessageSender{ 101 chanToWriteTo: make(chan crypto.ExchangeMessage, 10), 102 } 103 104 invoiceChan := make(chan crypto.Invoice) 105 bolt, err := boltdb.NewStorage(dir) 106 assert.Nil(t, err) 107 defer bolt.Close() 108 109 tracker := session.NewTracker(mbtime.Now) 110 totalsStorage := NewConsumerTotalsStorage(eventbus.New()) 111 totalsStorage.Store(1, identity.FromAddress(acc.Address.Hex()), common.Address{}, big.NewInt(10)) 112 deps := InvoicePayerDeps{ 113 InvoiceChan: invoiceChan, 114 PeerExchangeMessageSender: mockSender, 115 ConsumerTotalsStorage: totalsStorage, 116 TimeTracker: &tracker, 117 EventBus: mocks.NewEventBus(), 118 ChainID: 1, 119 Ks: ks, 120 AddressProvider: &mockAddressProvider{}, 121 Identity: identity.FromAddress(acc.Address.Hex()), 122 Peer: identity.FromAddress("0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C"), 123 AgreedPrice: *market.NewPrice(600, 0), 124 } 125 InvoicePayer := NewInvoicePayer(deps) 126 127 mockInvoice := crypto.Invoice{ 128 AgreementID: big.NewInt(1), 129 AgreementTotal: big.NewInt(0), 130 TransactorFee: big.NewInt(0), 131 Hashlock: "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C", 132 Provider: deps.Peer.Address, 133 } 134 135 testDone := make(chan struct{}) 136 137 defer InvoicePayer.Stop() 138 go func() { 139 err := InvoicePayer.Start() 140 assert.Nil(t, err) 141 testDone <- struct{}{} 142 }() 143 144 invoiceChan <- mockInvoice 145 146 exchangeMessage := <-mockSender.chanToWriteTo 147 InvoicePayer.Stop() 148 149 addr, err := exchangeMessage.RecoverConsumerIdentity() 150 assert.Nil(t, err) 151 assert.Equal(t, acc.Address.Hex(), addr.Hex()) 152 assert.Equal(t, big.NewInt(10), exchangeMessage.Promise.Amount) 153 154 <-testDone 155 } 156 157 func Test_InvoicePayer_SendsMessage_OnFreeService(t *testing.T) { 158 dir, err := os.MkdirTemp("", "exchange_message_tracker_test") 159 assert.Nil(t, err) 160 defer os.RemoveAll(dir) 161 162 ks := identity.NewMockKeystore() 163 acc, err := ks.NewAccount("") 164 assert.Nil(t, err) 165 166 err = ks.Unlock(acc, "") 167 assert.Nil(t, err) 168 169 mockSender := &MockPeerExchangeMessageSender{ 170 chanToWriteTo: make(chan crypto.ExchangeMessage, 10), 171 } 172 173 invoiceChan := make(chan crypto.Invoice) 174 bolt, err := boltdb.NewStorage(dir) 175 assert.Nil(t, err) 176 defer bolt.Close() 177 178 tracker := session.NewTracker(mbtime.Now) 179 totalsStorage := NewConsumerTotalsStorage(eventbus.New()) 180 totalsStorage.Store(1, identity.FromAddress(acc.Address.Hex()), common.Address{}, big.NewInt(0)) 181 deps := InvoicePayerDeps{ 182 InvoiceChan: invoiceChan, 183 PeerExchangeMessageSender: mockSender, 184 ConsumerTotalsStorage: totalsStorage, 185 TimeTracker: &tracker, 186 EventBus: mocks.NewEventBus(), 187 Ks: ks, 188 AddressProvider: &mockAddressProvider{}, 189 Identity: identity.FromAddress(acc.Address.Hex()), 190 Peer: identity.FromAddress("0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C"), 191 AgreedPrice: *market.NewPrice(600, 0), 192 } 193 InvoicePayer := NewInvoicePayer(deps) 194 195 mockInvoice := crypto.Invoice{ 196 AgreementID: big.NewInt(1), 197 AgreementTotal: big.NewInt(0), 198 TransactorFee: big.NewInt(0), 199 Hashlock: "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C", 200 Provider: deps.Peer.Address, 201 } 202 203 testDone := make(chan struct{}) 204 205 defer InvoicePayer.Stop() 206 go func() { 207 err := InvoicePayer.Start() 208 assert.Nil(t, err) 209 testDone <- struct{}{} 210 }() 211 212 invoiceChan <- mockInvoice 213 214 exchangeMessage := <-mockSender.chanToWriteTo 215 InvoicePayer.Stop() 216 addr, err := exchangeMessage.RecoverConsumerIdentity() 217 assert.Nil(t, err) 218 219 assert.Equal(t, acc.Address.Hex(), addr.Hex()) 220 221 <-testDone 222 } 223 224 func Test_InvoicePayer_BubblesErrors(t *testing.T) { 225 dir, err := os.MkdirTemp("", "exchange_message_tracker_test") 226 assert.Nil(t, err) 227 defer os.RemoveAll(dir) 228 229 ks := identity.NewMockKeystore() 230 acc, err := ks.NewAccount("") 231 assert.Nil(t, err) 232 233 mockSender := &MockPeerExchangeMessageSender{ 234 chanToWriteTo: make(chan crypto.ExchangeMessage, 10), 235 } 236 237 invoiceChan := make(chan crypto.Invoice) 238 bolt, err := boltdb.NewStorage(dir) 239 assert.Nil(t, err) 240 defer bolt.Close() 241 242 tracker := session.NewTracker(mbtime.Now) 243 totalsStorage := NewConsumerTotalsStorage(eventbus.New()) 244 deps := InvoicePayerDeps{ 245 InvoiceChan: invoiceChan, 246 EventBus: mocks.NewEventBus(), 247 PeerExchangeMessageSender: mockSender, 248 ConsumerTotalsStorage: totalsStorage, 249 TimeTracker: &tracker, 250 Ks: ks, 251 AddressProvider: &mockAddressProvider{}, 252 Identity: identity.FromAddress(acc.Address.Hex()), 253 Peer: identity.FromAddress("0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C"), 254 AgreedPrice: *market.NewPrice(600, 0), 255 } 256 InvoicePayer := NewInvoicePayer(deps) 257 defer InvoicePayer.Stop() 258 errChan := make(chan error) 259 go func() { errChan <- InvoicePayer.Start() }() 260 261 invoiceChan <- crypto.Invoice{} 262 263 err = <-errChan 264 assert.Error(t, err) 265 } 266 267 func TestInvoicePayer_isInvoiceOK(t *testing.T) { 268 type fields struct { 269 peer identity.Identity 270 timeTracker timeTracker 271 price market.Price 272 } 273 tests := []struct { 274 name string 275 fields fields 276 invoice crypto.Invoice 277 wantErr bool 278 }{ 279 { 280 name: "errors on invalid peer id", 281 fields: fields{ 282 peer: identity.FromAddress("0x01"), 283 }, 284 invoice: crypto.Invoice{ 285 Provider: "0x02", 286 }, 287 wantErr: true, 288 }, 289 { 290 name: "errors on too large invoice", 291 fields: fields{ 292 peer: identity.FromAddress("0x01"), 293 timeTracker: &mockTimeTracker{ 294 timeToReturn: time.Minute, 295 }, 296 price: *market.NewPrice(6000000, 0), 297 }, 298 invoice: crypto.Invoice{ 299 TransactorFee: big.NewInt(0), 300 AgreementID: big.NewInt(1), 301 AgreementTotal: big.NewInt(150100), 302 Provider: "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C", 303 }, 304 wantErr: true, 305 }, 306 { 307 name: "accepts proper invoice", 308 fields: fields{ 309 peer: identity.FromAddress("0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C"), 310 timeTracker: &mockTimeTracker{ 311 timeToReturn: time.Minute, 312 }, 313 price: *market.NewPrice(6000000, 0), 314 }, 315 invoice: crypto.Invoice{ 316 TransactorFee: big.NewInt(0), 317 AgreementID: big.NewInt(1), 318 AgreementTotal: big.NewInt(100000), 319 Provider: "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C", 320 }, 321 wantErr: false, 322 }, 323 } 324 for _, tt := range tests { 325 t.Run(tt.name, func(t *testing.T) { 326 emt := &InvoicePayer{ 327 deps: InvoicePayerDeps{ 328 TimeTracker: tt.fields.timeTracker, 329 AgreedPrice: tt.fields.price, 330 Peer: tt.fields.peer, 331 }, 332 } 333 if err := emt.isInvoiceOK(tt.invoice); (err != nil) != tt.wantErr { 334 t.Errorf("InvoicePayer.isInvoiceOK() error = %v, wantErr %v", err, tt.wantErr) 335 } 336 }) 337 } 338 } 339 340 func TestInvoicePayer_incrementGrandTotalPromised(t *testing.T) { 341 type fields struct { 342 consumerTotalsStorage *mockConsumerTotalsStorage 343 } 344 type args struct { 345 amount *big.Int 346 } 347 tests := []struct { 348 name string 349 fields fields 350 args args 351 wantErr bool 352 want *big.Int 353 }{ 354 { 355 name: "returns the error from storage", 356 fields: fields{ 357 consumerTotalsStorage: &mockConsumerTotalsStorage{ 358 bus: eventbus.New(), 359 err: errors.New("some error"), 360 }, 361 }, 362 args: args{ 363 amount: big.NewInt(0), 364 }, 365 wantErr: true, 366 want: new(big.Int), 367 }, 368 { 369 name: "adds to zero if no previous value", 370 fields: fields{ 371 consumerTotalsStorage: &mockConsumerTotalsStorage{ 372 bus: eventbus.New(), 373 }, 374 }, 375 args: args{ 376 amount: big.NewInt(11), 377 }, 378 wantErr: false, 379 want: big.NewInt(11), 380 }, 381 { 382 name: "adds to value if found", 383 fields: fields{ 384 consumerTotalsStorage: &mockConsumerTotalsStorage{ 385 bus: eventbus.New(), 386 res: big.NewInt(15), 387 }, 388 }, 389 args: args{ 390 amount: big.NewInt(11), 391 }, 392 wantErr: false, 393 want: big.NewInt(26), 394 }, 395 } 396 for _, tt := range tests { 397 t.Run(tt.name, func(t *testing.T) { 398 emt := &InvoicePayer{ 399 deps: InvoicePayerDeps{ 400 ConsumerTotalsStorage: tt.fields.consumerTotalsStorage, 401 }, 402 } 403 if err := emt.incrementGrandTotalPromised(*tt.args.amount); (err != nil) != tt.wantErr { 404 t.Errorf("InvoicePayer.incrementGrandTotalPromised() error = %v, wantErr %v", err, tt.wantErr) 405 } 406 got := tt.fields.consumerTotalsStorage.calledWith 407 if got != nil && got.Cmp(tt.want) != 0 { 408 t.Errorf("InvoicePayer.incrementGrandTotalPromised() = %v, want %v", got, tt.want) 409 } 410 }) 411 } 412 } 413 414 func TestInvoicePayer_calculateAmountToPromise(t *testing.T) { 415 type fields struct { 416 peer identity.Identity 417 lastInvoice crypto.Invoice 418 consumerTotalsStorage *mockConsumerTotalsStorage 419 } 420 tests := []struct { 421 name string 422 fields fields 423 invoice crypto.Invoice 424 wantToPromise *big.Int 425 wantDiff *big.Int 426 wantErr bool 427 }{ 428 { 429 name: "bubbles totals storage errors", 430 fields: fields{ 431 consumerTotalsStorage: &mockConsumerTotalsStorage{ 432 err: errors.New("explosions everywhere"), 433 }, 434 lastInvoice: crypto.Invoice{ 435 AgreementID: new(big.Int), 436 AgreementTotal: new(big.Int), 437 TransactorFee: new(big.Int), 438 }, 439 }, 440 invoice: crypto.Invoice{ 441 AgreementTotal: big.NewInt(0), 442 AgreementID: new(big.Int), 443 TransactorFee: new(big.Int), 444 }, 445 wantErr: true, 446 wantToPromise: big.NewInt(0), 447 wantDiff: big.NewInt(0), 448 }, 449 { 450 name: "assumes zero", 451 fields: fields{ 452 consumerTotalsStorage: &mockConsumerTotalsStorage{ 453 res: new(big.Int), 454 }, 455 lastInvoice: crypto.Invoice{ 456 AgreementID: new(big.Int), 457 AgreementTotal: new(big.Int), 458 TransactorFee: new(big.Int), 459 }, 460 }, 461 invoice: crypto.Invoice{ 462 AgreementTotal: big.NewInt(10), 463 AgreementID: new(big.Int), 464 TransactorFee: new(big.Int), 465 }, 466 wantErr: false, 467 wantDiff: big.NewInt(10), 468 wantToPromise: big.NewInt(10), 469 }, 470 { 471 name: "calculates correctly with different grand total", 472 fields: fields{ 473 consumerTotalsStorage: &mockConsumerTotalsStorage{ 474 res: big.NewInt(100), 475 }, 476 lastInvoice: crypto.Invoice{ 477 AgreementID: new(big.Int), 478 AgreementTotal: new(big.Int), 479 TransactorFee: new(big.Int), 480 }, 481 }, 482 invoice: crypto.Invoice{ 483 AgreementTotal: big.NewInt(10), 484 AgreementID: new(big.Int), 485 TransactorFee: new(big.Int), 486 }, 487 wantErr: false, 488 wantDiff: big.NewInt(10), 489 wantToPromise: big.NewInt(110), 490 }, 491 { 492 name: "calculates correctly with previous invoice", 493 fields: fields{ 494 lastInvoice: crypto.Invoice{ 495 AgreementID: big.NewInt(111), 496 AgreementTotal: big.NewInt(111), 497 TransactorFee: big.NewInt(0), 498 }, 499 consumerTotalsStorage: &mockConsumerTotalsStorage{ 500 res: big.NewInt(100), 501 }, 502 }, 503 invoice: crypto.Invoice{ 504 AgreementID: big.NewInt(111), 505 AgreementTotal: big.NewInt(120), 506 TransactorFee: big.NewInt(0), 507 }, 508 wantErr: false, 509 wantDiff: big.NewInt(9), 510 wantToPromise: big.NewInt(109), 511 }, 512 } 513 for _, tt := range tests { 514 t.Run(tt.name, func(t *testing.T) { 515 emt := &InvoicePayer{ 516 deps: InvoicePayerDeps{ 517 ConsumerTotalsStorage: tt.fields.consumerTotalsStorage, 518 Peer: tt.fields.peer, 519 }, 520 } 521 emt.lastInvoice = tt.fields.lastInvoice 522 gotToPromise, gotDiff, err := emt.calculateAmountToPromise(tt.invoice) 523 if (err != nil) != tt.wantErr { 524 t.Errorf("InvoicePayer.calculateAmountToPromise() error = %v, wantErr %v", err, tt.wantErr) 525 return 526 } 527 if gotToPromise.Cmp(tt.wantToPromise) != 0 { 528 t.Errorf("InvoicePayer.calculateAmountToPromise() gotToPromise = %v, want %v", gotToPromise, tt.wantToPromise) 529 } 530 if gotDiff.Cmp(tt.wantDiff) != 0 { 531 t.Errorf("InvoicePayer.calculateAmountToPromise() gotDiff = %v, want %v", gotDiff, tt.wantDiff) 532 } 533 }) 534 } 535 } 536 537 func TestInvoicePayer_issueExchangeMessage_publishesEvents(t *testing.T) { 538 ks := identity.NewMockKeystore() 539 acc, err := ks.NewAccount("") 540 assert.Nil(t, err) 541 542 err = ks.Unlock(acc, "") 543 assert.Nil(t, err) 544 545 peerID := identity.FromAddress("0x01") 546 547 mp := &mockPublisher{ 548 publicationChan: make(chan testEvent, 10), 549 } 550 emt := &InvoicePayer{ 551 deps: InvoicePayerDeps{ 552 PeerExchangeMessageSender: &MockPeerExchangeMessageSender{ 553 chanToWriteTo: make(chan crypto.ExchangeMessage, 10), 554 }, 555 ConsumerTotalsStorage: &mockConsumerTotalsStorage{ 556 res: big.NewInt(0), 557 bus: mp, 558 }, 559 Ks: ks, 560 EventBus: mp, 561 Identity: identity.FromAddress(acc.Address.Hex()), 562 Peer: peerID, 563 ChainID: 1, 564 SessionID: "someid", 565 }, 566 } 567 emt.lastInvoice = crypto.Invoice{ 568 AgreementID: new(big.Int), 569 AgreementTotal: big.NewInt(10), 570 TransactorFee: new(big.Int), 571 } 572 err = emt.issueExchangeMessage(crypto.Invoice{ 573 AgreementTotal: big.NewInt(15), 574 AgreementID: big.NewInt(0), 575 Hashlock: "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C", 576 TransactorFee: new(big.Int), 577 }) 578 assert.NoError(t, err) 579 580 ev := <-mp.publicationChan 581 assert.Equal(t, event.AppTopicInvoicePaid, ev.name) 582 assert.EqualValues(t, event.AppEventInvoicePaid{ 583 ConsumerID: emt.deps.Identity, 584 Invoice: crypto.Invoice{ 585 AgreementTotal: big.NewInt(15), 586 AgreementID: big.NewInt(0), 587 TransactorFee: new(big.Int), 588 Hashlock: "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C", 589 }, 590 SessionID: emt.deps.SessionID, 591 }, ev.value) 592 593 ev = <-mp.publicationChan 594 assert.Equal(t, event.AppTopicGrandTotalChanged, ev.name) 595 assert.EqualValues(t, event.AppEventGrandTotalChanged{ 596 ChainID: 1, 597 ConsumerID: emt.deps.Identity, 598 Current: big.NewInt(5), 599 }, ev.value) 600 } 601 602 func TestInvoicePayer_issueExchangeMessage(t *testing.T) { 603 ks := identity.NewMockKeystore() 604 acc, err := ks.NewAccount("") 605 assert.Nil(t, err) 606 607 err = ks.Unlock(acc, "") 608 assert.Nil(t, err) 609 610 peerID := identity.FromAddress("0x01") 611 612 type fields struct { 613 peerExchangeMessageSender *MockPeerExchangeMessageSender 614 keystore hashSigner 615 identity identity.Identity 616 peer identity.Identity 617 lastInvoice crypto.Invoice 618 consumerTotalsStorage *mockConsumerTotalsStorage 619 } 620 type args struct { 621 invoice crypto.Invoice 622 } 623 tests := []struct { 624 name string 625 fields fields 626 args args 627 wantErr bool 628 wantMsg *crypto.ExchangeMessage 629 }{ 630 { 631 name: "bubbles exchange message creation errors", 632 fields: fields{ 633 identity: identity.FromAddress(""), 634 peer: peerID, 635 keystore: ks, 636 peerExchangeMessageSender: &MockPeerExchangeMessageSender{ 637 chanToWriteTo: make(chan crypto.ExchangeMessage, 10), 638 }, 639 consumerTotalsStorage: &mockConsumerTotalsStorage{ 640 res: new(big.Int), 641 bus: eventbus.New(), 642 }, 643 lastInvoice: crypto.Invoice{ 644 AgreementTotal: big.NewInt(0), 645 AgreementID: big.NewInt(0), 646 TransactorFee: big.NewInt(0), 647 }, 648 }, 649 wantErr: true, 650 args: args{ 651 invoice: crypto.Invoice{ 652 AgreementTotal: big.NewInt(15), 653 AgreementID: big.NewInt(0), 654 TransactorFee: big.NewInt(0), 655 Hashlock: "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C", 656 }, 657 }, 658 }, 659 { 660 name: "ignores sending errors", 661 fields: fields{ 662 identity: identity.FromAddress(acc.Address.Hex()), 663 peer: peerID, 664 keystore: ks, 665 peerExchangeMessageSender: &MockPeerExchangeMessageSender{ 666 chanToWriteTo: make(chan crypto.ExchangeMessage, 10), 667 mockError: errors.New("explosions everywhere"), 668 }, 669 consumerTotalsStorage: &mockConsumerTotalsStorage{ 670 res: new(big.Int), 671 bus: eventbus.New(), 672 }, 673 lastInvoice: crypto.Invoice{ 674 AgreementTotal: big.NewInt(0), 675 AgreementID: big.NewInt(0), 676 TransactorFee: big.NewInt(0), 677 }, 678 }, 679 wantErr: false, 680 args: args{ 681 invoice: crypto.Invoice{ 682 AgreementTotal: big.NewInt(15), 683 AgreementID: big.NewInt(0), 684 TransactorFee: big.NewInt(0), 685 Hashlock: "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C", 686 }, 687 }, 688 }, 689 { 690 name: "sends exchange message", 691 fields: fields{ 692 identity: identity.FromAddress(acc.Address.Hex()), 693 peer: peerID, 694 keystore: ks, 695 peerExchangeMessageSender: &MockPeerExchangeMessageSender{ 696 chanToWriteTo: make(chan crypto.ExchangeMessage, 10), 697 }, 698 consumerTotalsStorage: &mockConsumerTotalsStorage{ 699 bus: eventbus.New(), 700 res: new(big.Int), 701 }, 702 lastInvoice: crypto.Invoice{ 703 AgreementTotal: big.NewInt(0), 704 AgreementID: big.NewInt(0), 705 TransactorFee: big.NewInt(0), 706 }, 707 }, 708 args: args{ 709 invoice: crypto.Invoice{ 710 AgreementTotal: big.NewInt(15), 711 AgreementID: big.NewInt(0), 712 TransactorFee: big.NewInt(0), 713 Hashlock: "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C", 714 }, 715 }, 716 wantErr: false, 717 }, 718 } 719 for _, tt := range tests { 720 t.Run(tt.name, func(t *testing.T) { 721 emt := &InvoicePayer{ 722 deps: InvoicePayerDeps{ 723 PeerExchangeMessageSender: tt.fields.peerExchangeMessageSender, 724 ConsumerTotalsStorage: tt.fields.consumerTotalsStorage, 725 Peer: tt.fields.peer, 726 Ks: tt.fields.keystore, 727 Identity: tt.fields.identity, 728 EventBus: mocks.NewEventBus(), 729 }, 730 } 731 emt.lastInvoice = tt.fields.lastInvoice 732 if err := emt.issueExchangeMessage(tt.args.invoice); (err != nil) != tt.wantErr { 733 t.Errorf("InvoicePayer.issueExchangeMessage() error = %v, wantErr %v", err, tt.wantErr) 734 } 735 if tt.wantMsg != nil { 736 errMsg := "InvoicePayer.issueExchangeMessage() error" 737 msg := <-tt.fields.peerExchangeMessageSender.chanToWriteTo 738 assert.True(t, len(msg.Signature) > 0, errMsg) 739 assert.True(t, len(msg.Promise.Signature) > 0, errMsg) 740 assert.Equal(t, tt.fields.peer, msg.Provider, errMsg) 741 assert.Equal(t, tt.args.invoice.AgreementTotal, msg.AgreementTotal, errMsg) 742 assert.Equal(t, tt.args.invoice.AgreementTotal, msg.Promise.Amount, errMsg) 743 assert.Equal(t, tt.args.invoice.Hashlock, msg.Promise.Hashlock, errMsg) 744 } 745 }) 746 } 747 } 748 749 type mockConsumerTotalsStorage struct { 750 res *big.Int 751 resLock sync.Mutex 752 bus eventbus.Publisher 753 754 err error 755 calledWith *big.Int 756 } 757 758 func (mcts *mockConsumerTotalsStorage) Store(chainID int64, id identity.Identity, hermesID common.Address, amount *big.Int) error { 759 mcts.calledWith = amount 760 if mcts.bus != nil { 761 go mcts.bus.Publish(event.AppTopicGrandTotalChanged, event.AppEventGrandTotalChanged{ 762 ChainID: chainID, 763 Current: amount, 764 HermesID: hermesID, 765 ConsumerID: id, 766 }) 767 } 768 return nil 769 } 770 771 func (mcts *mockConsumerTotalsStorage) Add(chainID int64, id identity.Identity, hermesID common.Address, amount *big.Int) error { 772 prevAmount := big.NewInt(0) 773 if mcts.res != nil { 774 prevAmount = mcts.res 775 } 776 mcts.calledWith = new(big.Int).Add(prevAmount, amount) 777 if mcts.bus != nil { 778 go mcts.bus.Publish(event.AppTopicGrandTotalChanged, event.AppEventGrandTotalChanged{ 779 ChainID: chainID, 780 Current: amount, 781 HermesID: hermesID, 782 ConsumerID: id, 783 }) 784 } 785 return mcts.err 786 } 787 788 func (mcts *mockConsumerTotalsStorage) Get(chainID int64, id identity.Identity, hermesID common.Address) (*big.Int, error) { 789 mcts.resLock.Lock() 790 defer mcts.resLock.Unlock() 791 return mcts.res, mcts.err 792 } 793 794 type mockTimeTracker struct { 795 timeToReturn time.Duration 796 } 797 798 func (mtt *mockTimeTracker) StartTracking() { 799 800 } 801 func (mtt *mockTimeTracker) Elapsed() time.Duration { 802 return mtt.timeToReturn 803 } 804 805 func Test_estimateInvoiceTolerance(t *testing.T) { 806 type args struct { 807 elapsed time.Duration 808 transferred DataTransferred 809 } 810 tests := []struct { 811 name string 812 args args 813 want float64 814 }{ 815 {"Zero time, zero data", 816 args{ 817 0 * time.Second, 818 DataTransferred{0, 0}}, 819 3}, 820 821 {"1 sec, 0 bytes", 822 args{ 823 1 * time.Second, 824 DataTransferred{0, 0}}, 825 1.6109756097560976}, 826 827 {"1 sec, 2 000 bytes", 828 args{ 829 1 * time.Second, 830 DataTransferred{1000, 1000}}, 831 1.6100149009391526}, 832 833 {"1 sec, 2 000 000 bytes", 834 args{ 835 1 * time.Second, 836 DataTransferred{1000000, 1000000}}, 837 1.6246823767314633}, 838 839 {"1 sec, 20 000 000 bytes", 840 args{ 841 1 * time.Second, 842 DataTransferred{10000000, 10000000}}, 843 1.7396867763477881}, 844 845 {"1 sec, 200 000 000 bytes", 846 args{ 847 1 * time.Second, 848 DataTransferred{100000000, 100000000}}, 849 2.2084123020547852}, 850 851 {"2 min, 0 bytes", 852 args{ 853 2 * time.Minute, 854 DataTransferred{0, 0}}, 855 1.4443089430894309}, 856 857 {"2 min, 2 000 bytes", 858 args{ 859 2 * time.Minute, 860 DataTransferred{1000, 1000}}, 861 1.4433334575096612}, 862 863 {"2 min, 2 000 000 bytes", 864 args{ 865 2 * time.Minute, 866 DataTransferred{1000000, 1000000}}, 867 1.4434574942587659}, 868 869 {"2 min, 20 000 000 bytes", 870 args{ 871 2 * time.Minute, 872 DataTransferred{10000000, 10000000}}, 873 1.4445735567021262}, 874 875 {"2 min, 200 000 000 bytes", 876 args{ 877 2 * time.Minute, 878 DataTransferred{100000000, 100000000}}, 879 1.455598661303886}, 880 881 {"20 min, 0 bytes", 882 args{ 883 20 * time.Minute, 884 DataTransferred{0, 0}}, 885 1.1585946573751453}, 886 887 {"20 min, 2 000 bytes", 888 args{ 889 20 * time.Minute, 890 DataTransferred{1000, 1000}}, 891 1.1576190600366817}, 892 893 {"20 min, 2 000 000 bytes", 894 args{ 895 20 * time.Minute, 896 DataTransferred{1000000, 1000000}}, 897 1.1576314650991801}, 898 899 {"20 min, 20 000 000 bytes", 900 args{ 901 20 * time.Minute, 902 DataTransferred{10000000, 10000000}}, 903 1.15774320854448}, 904 905 {"20 min, 200 000 000 bytes", 906 args{ 907 20 * time.Minute, 908 DataTransferred{100000000, 100000000}}, 909 1.1588592709878404}, 910 911 {"200 min, 200 000 000 bytes", 912 args{ 913 200 * time.Minute, 914 DataTransferred{100000000, 100000000}}, 915 1.115099285303542}, 916 917 {"1 min, 200 000 000 bytes", 918 args{ 919 1 * time.Minute, 920 DataTransferred{50000000, 50000000}}, 921 1.6222653279705525}, 922 923 {"1 min, 2 000 000 000 bytes", 924 args{ 925 1 * time.Minute, 926 DataTransferred{100000000, 100000000}}, 927 1.6342334250351986}, 928 929 {"1 min, 20 000 000 000 bytes", 930 args{ 931 1 * time.Minute, 932 DataTransferred{1000000000, 1000000000}}, 933 1.8089443281831476}, 934 935 {"10 min, 20 000 000 000 bytes", 936 args{ 937 10 * time.Minute, 938 DataTransferred{1000000000, 1000000000}}, 939 1.2251425159442896}, 940 941 {"6 hours, 20 000 000 000 bytes", 942 args{ 943 6 * time.Hour, 944 DataTransferred{1000000000, 1000000000}}, 945 1.1134594760857283}, 946 } 947 for _, tt := range tests { 948 t.Run(tt.name, func(t *testing.T) { 949 if got := estimateInvoiceTolerance(tt.args.elapsed, tt.args.transferred); got != tt.want { 950 t.Errorf("estimateInvoiceTolerance() = %v, want %v", got, tt.want) 951 } 952 }) 953 } 954 }