github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/session/pingpong/invoice_tracker_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 "encoding/hex" 22 "math/big" 23 "os" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/ethereum/go-ethereum/common" 29 "github.com/mysteriumnetwork/node/core/storage/boltdb" 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/payments/crypto" 36 "github.com/mysteriumnetwork/payments/observer" 37 "github.com/pkg/errors" 38 "github.com/stretchr/testify/assert" 39 ) 40 41 const ( 42 mockRegistryAddress = "0xE6b3a5c92e7c1f9543A0aEE9A93fE2F6B584c1f7" 43 mockHermesAddress = "0xf28DB7aDf64A2811202B149aa4733A1FB9100e5c" 44 mockChannelImplementation = "0xa26b684d8dBa935DD34544FBd3Ab4d7FDe1C4D07" 45 ) 46 47 type MockPeerInvoiceSender struct { 48 mockError error 49 chanToWriteTo chan crypto.Invoice 50 } 51 52 func (mpis *MockPeerInvoiceSender) Send(invoice crypto.Invoice) error { 53 if mpis.chanToWriteTo != nil { 54 mpis.chanToWriteTo <- invoice 55 } 56 return mpis.mockError 57 } 58 59 type mockHermesCaller struct { 60 errToReturn error 61 } 62 63 func (mac *mockHermesCaller) RequestPromise(rp RequestPromise) (crypto.Promise, error) { 64 return crypto.Promise{}, mac.errToReturn 65 } 66 67 func (mac *mockHermesCaller) PayAndSettle(rp RequestPromise) (crypto.Promise, error) { 68 return crypto.Promise{}, mac.errToReturn 69 } 70 71 func (mac *mockHermesCaller) RevealR(r string, provider string, agreementID *big.Int) error { 72 return mac.errToReturn 73 } 74 75 func (mac *mockHermesCaller) UpdatePromiseFee(promise crypto.Promise, newFee *big.Int) (crypto.Promise, error) { 76 return promise, nil 77 } 78 79 func (mac *mockHermesCaller) GetConsumerData(chainID int64, id string, cacheTime time.Duration) (HermesUserInfo, error) { 80 return HermesUserInfo{}, nil 81 } 82 83 func (mac *mockHermesCaller) GetProviderData(chainID int64, id string) (HermesUserInfo, error) { 84 return HermesUserInfo{}, nil 85 } 86 87 func (mac *mockHermesCaller) SyncProviderPromise(promise crypto.Promise, signer identity.Signer) error { 88 return nil 89 } 90 91 func (mac *mockHermesCaller) RefreshLatestProviderPromise(chainID int64, id string, hashlock, recoveryData []byte, signer identity.Signer) (crypto.Promise, error) { 92 return crypto.Promise{}, nil 93 } 94 95 func Test_InvoiceTracker_Start_Stop(t *testing.T) { 96 dir, err := os.MkdirTemp("", "invoice_tracker_test") 97 assert.Nil(t, err) 98 defer os.RemoveAll(dir) 99 100 ks := identity.NewMockKeystore() 101 acc, err := ks.NewAccount("") 102 assert.Nil(t, err) 103 104 mockSender := &MockPeerInvoiceSender{ 105 chanToWriteTo: make(chan crypto.Invoice, 10), 106 } 107 108 exchangeMessageChan := make(chan crypto.ExchangeMessage) 109 bolt, err := boltdb.NewStorage(dir) 110 assert.Nil(t, err) 111 defer bolt.Close() 112 113 tracker := session.NewTracker(mbtime.Now) 114 invoiceStorage := NewProviderInvoiceStorage(NewInvoiceStorage(bolt)) 115 deps := InvoiceTrackerDeps{ 116 AgreedPrice: *market.NewPrice(600, 0), 117 Peer: identity.FromAddress("some peer"), 118 PeerInvoiceSender: mockSender, 119 EventBus: mocks.NewEventBus(), 120 InvoiceStorage: invoiceStorage, 121 TimeTracker: &tracker, 122 ChargePeriod: time.Nanosecond, 123 ChargePeriodLeeway: 15 * time.Minute, 124 LimitChargePeriod: time.Nanosecond, 125 LimitNotPaidInvoice: big.NewInt(0), 126 ExchangeMessageChan: exchangeMessageChan, 127 ExchangeMessageWaitTimeout: time.Second, 128 ProviderID: identity.FromAddress(acc.Address.Hex()), 129 ConsumersHermesID: acc.Address, 130 AddressProvider: &mockAddressProvider{}, 131 HermesStatusChecker: &mockHermesStatusChecker{statusToReturn: HermesStatus{IsActive: true}}, 132 } 133 invoiceTracker := NewInvoiceTracker(deps) 134 135 go func() { 136 time.Sleep(time.Nanosecond * 10) 137 invoiceTracker.Stop() 138 }() 139 140 err = invoiceTracker.Start() 141 assert.Nil(t, err) 142 } 143 144 func Test_InvoiceTracker_Start_RefusesLargeFee(t *testing.T) { 145 dir, err := os.MkdirTemp("", "invoice_tracker_test") 146 assert.Nil(t, err) 147 defer os.RemoveAll(dir) 148 149 ks := identity.NewMockKeystore() 150 acc, err := ks.NewAccount("") 151 assert.Nil(t, err) 152 153 mockSender := &MockPeerInvoiceSender{ 154 chanToWriteTo: make(chan crypto.Invoice, 10), 155 } 156 157 exchangeMessageChan := make(chan crypto.ExchangeMessage) 158 bolt, err := boltdb.NewStorage(dir) 159 assert.Nil(t, err) 160 defer bolt.Close() 161 162 tracker := session.NewTracker(mbtime.Now) 163 invoiceStorage := NewProviderInvoiceStorage(NewInvoiceStorage(bolt)) 164 deps := InvoiceTrackerDeps{ 165 AgreedPrice: *market.NewPrice(600, 0), 166 Peer: identity.FromAddress("some peer"), 167 PeerInvoiceSender: mockSender, 168 InvoiceStorage: invoiceStorage, 169 TimeTracker: &tracker, 170 ChargePeriod: time.Nanosecond, 171 LimitChargePeriod: time.Nanosecond, 172 LimitNotPaidInvoice: big.NewInt(0), 173 ExchangeMessageChan: exchangeMessageChan, 174 ExchangeMessageWaitTimeout: time.Second, 175 ProviderID: identity.FromAddress(acc.Address.Hex()), 176 ConsumersHermesID: acc.Address, 177 AddressProvider: &mockAddressProvider{}, 178 EventBus: mocks.NewEventBus(), 179 MaxAllowedHermesFee: 1500, 180 HermesStatusChecker: &mockHermesStatusChecker{statusToReturn: HermesStatus{IsActive: true, Fee: 1501}}, 181 } 182 invoiceTracker := NewInvoiceTracker(deps) 183 184 go func() { 185 time.Sleep(time.Nanosecond * 10) 186 invoiceTracker.Stop() 187 }() 188 189 err = invoiceTracker.Start() 190 assert.Equal(t, ErrHermesFeeTooLarge, err) 191 } 192 193 func Test_InvoiceTracker_Start_BubblesHermesCheckError(t *testing.T) { 194 dir, err := os.MkdirTemp("", "invoice_tracker_test") 195 assert.Nil(t, err) 196 defer os.RemoveAll(dir) 197 198 ks := identity.NewMockKeystore() 199 acc, err := ks.NewAccount("") 200 assert.Nil(t, err) 201 202 mockSender := &MockPeerInvoiceSender{ 203 chanToWriteTo: make(chan crypto.Invoice, 10), 204 } 205 206 exchangeMessageChan := make(chan crypto.ExchangeMessage) 207 bolt, err := boltdb.NewStorage(dir) 208 assert.Nil(t, err) 209 defer bolt.Close() 210 211 mockErr := errors.New("explosions everywhere") 212 tracker := session.NewTracker(mbtime.Now) 213 invoiceStorage := NewProviderInvoiceStorage(NewInvoiceStorage(bolt)) 214 NewHermesPromiseStorage(bolt) 215 deps := InvoiceTrackerDeps{ 216 AgreedPrice: *market.NewPrice(600, 0), 217 Peer: identity.FromAddress("some peer"), 218 PeerInvoiceSender: mockSender, 219 InvoiceStorage: invoiceStorage, 220 TimeTracker: &tracker, 221 ChargePeriod: time.Nanosecond, 222 ChargePeriodLeeway: 15 * time.Minute, 223 LimitChargePeriod: time.Nanosecond, 224 LimitNotPaidInvoice: big.NewInt(0), 225 ExchangeMessageChan: exchangeMessageChan, 226 ExchangeMessageWaitTimeout: time.Second, 227 ProviderID: identity.FromAddress(acc.Address.Hex()), 228 ConsumersHermesID: acc.Address, 229 AddressProvider: &mockAddressProvider{}, 230 EventBus: mocks.NewEventBus(), 231 HermesStatusChecker: &mockHermesStatusChecker{errToReturn: mockErr}, 232 } 233 invoiceTracker := NewInvoiceTracker(deps) 234 235 go func() { 236 time.Sleep(time.Nanosecond * 10) 237 invoiceTracker.Stop() 238 }() 239 240 err = invoiceTracker.Start() 241 assert.Equal(t, errors.Wrap(mockErr, "could not check hermes status").Error(), err.Error()) 242 } 243 244 func Test_InvoiceTracker_BubblesErrors(t *testing.T) { 245 dir, err := os.MkdirTemp("", "invoice_tracker_test") 246 assert.Nil(t, err) 247 defer os.RemoveAll(dir) 248 249 ks := identity.NewMockKeystore() 250 acc, err := ks.NewAccount("") 251 assert.Nil(t, err) 252 253 mockSender := &MockPeerInvoiceSender{ 254 chanToWriteTo: make(chan crypto.Invoice, 10), 255 } 256 257 exchangeMessageChan := make(chan crypto.ExchangeMessage) 258 bolt, err := boltdb.NewStorage(dir) 259 assert.Nil(t, err) 260 defer bolt.Close() 261 262 tracker := session.NewTracker(mbtime.Now) 263 invoiceStorage := NewProviderInvoiceStorage(NewInvoiceStorage(bolt)) 264 deps := InvoiceTrackerDeps{ 265 AgreedPrice: *market.NewPrice(600, 0), 266 Peer: identity.FromAddress("some peer"), 267 PeerInvoiceSender: mockSender, 268 InvoiceStorage: invoiceStorage, 269 TimeTracker: &tracker, 270 LimitChargePeriod: time.Nanosecond, 271 LimitNotPaidInvoice: big.NewInt(0), 272 ChargePeriod: time.Millisecond, 273 ChargePeriodLeeway: 15 * time.Minute, 274 ExchangeMessageChan: exchangeMessageChan, 275 ExchangeMessageWaitTimeout: time.Second, 276 ProviderID: identity.FromAddress(acc.Address.Hex()), 277 ConsumersHermesID: acc.Address, 278 AddressProvider: &mockAddressProvider{}, 279 EventBus: mocks.NewEventBus(), 280 HermesStatusChecker: &mockHermesStatusChecker{statusToReturn: HermesStatus{IsActive: true}}, 281 } 282 invoiceTracker := NewInvoiceTracker(deps) 283 defer invoiceTracker.Stop() 284 285 errChan := make(chan error) 286 go func() { errChan <- invoiceTracker.Start() }() 287 288 invoice := <-mockSender.chanToWriteTo 289 b, err := hex.DecodeString(invoice.Hashlock) 290 assert.NoError(t, err) 291 exchangeMessageChan <- crypto.ExchangeMessage{ 292 Promise: crypto.Promise{ 293 Hashlock: b, 294 }, 295 } 296 297 err = <-errChan 298 assert.Error(t, err) 299 } 300 301 func Test_InvoiceTracker_SendsInvoice(t *testing.T) { 302 dir, err := os.MkdirTemp("", "invoice_tracker_test") 303 assert.Nil(t, err) 304 defer os.RemoveAll(dir) 305 306 ks := identity.NewMockKeystore() 307 acc, err := ks.NewAccount("") 308 assert.Nil(t, err) 309 mockSender := &MockPeerInvoiceSender{ 310 chanToWriteTo: make(chan crypto.Invoice, 10), 311 } 312 313 exchangeMessageChan := make(chan crypto.ExchangeMessage) 314 bolt, err := boltdb.NewStorage(dir) 315 assert.Nil(t, err) 316 defer bolt.Close() 317 318 tracker := session.NewTracker(mbtime.Now) 319 invoiceStorage := NewProviderInvoiceStorage(NewInvoiceStorage(bolt)) 320 deps := InvoiceTrackerDeps{ 321 AgreedPrice: *market.NewPrice(60000000000000, 0), 322 Peer: identity.FromAddress("some peer"), 323 PeerInvoiceSender: mockSender, 324 InvoiceStorage: invoiceStorage, 325 TimeTracker: &tracker, 326 LimitChargePeriod: time.Nanosecond, 327 ChargePeriod: time.Nanosecond, 328 LimitNotPaidInvoice: big.NewInt(0), 329 MaxNotPaidInvoice: big.NewInt(0), 330 ChargePeriodLeeway: 15 * time.Minute, 331 ExchangeMessageChan: exchangeMessageChan, 332 ExchangeMessageWaitTimeout: time.Second, 333 ProviderID: identity.FromAddress(acc.Address.Hex()), 334 ConsumersHermesID: acc.Address, 335 AddressProvider: &mockAddressProvider{}, 336 HermesStatusChecker: &mockHermesStatusChecker{statusToReturn: HermesStatus{IsActive: true}}, 337 EventBus: mocks.NewEventBus(), 338 } 339 invoiceTracker := NewInvoiceTracker(deps) 340 defer invoiceTracker.Stop() 341 342 errChan := make(chan error) 343 go func() { errChan <- invoiceTracker.Start() }() 344 345 invoice := <-mockSender.chanToWriteTo 346 assert.True(t, invoice.AgreementTotal.Cmp(new(big.Int)) > 0) 347 assert.Len(t, invoice.Hashlock, 64) 348 assert.Equal(t, strings.ToLower(acc.Address.Hex()), strings.ToLower(invoice.Provider)) 349 350 invoiceTracker.Stop() 351 assert.NoError(t, <-errChan) 352 } 353 354 func Test_InvoiceTracker_FirstInvoice_Has_Static_Value(t *testing.T) { 355 dir, err := os.MkdirTemp("", "invoice_tracker_test") 356 assert.Nil(t, err) 357 defer os.RemoveAll(dir) 358 359 ks := identity.NewMockKeystore() 360 acc, err := ks.NewAccount("") 361 assert.Nil(t, err) 362 mockSender := &MockPeerInvoiceSender{ 363 chanToWriteTo: make(chan crypto.Invoice, 10), 364 } 365 366 exchangeMessageChan := make(chan crypto.ExchangeMessage) 367 bolt, err := boltdb.NewStorage(dir) 368 assert.Nil(t, err) 369 defer bolt.Close() 370 371 tracker := session.NewTracker(mbtime.Now) 372 invoiceStorage := NewProviderInvoiceStorage(NewInvoiceStorage(bolt)) 373 deps := InvoiceTrackerDeps{ 374 AgreedPrice: *market.NewPrice(60000000000000, 0), 375 Peer: identity.FromAddress("some peer"), 376 PeerInvoiceSender: mockSender, 377 InvoiceStorage: invoiceStorage, 378 TimeTracker: &tracker, 379 LimitChargePeriod: time.Nanosecond, 380 LimitNotPaidInvoice: big.NewInt(0), 381 ChargePeriod: time.Nanosecond, 382 ChargePeriodLeeway: 15 * time.Minute, 383 ExchangeMessageChan: exchangeMessageChan, 384 ExchangeMessageWaitTimeout: time.Second, 385 ProviderID: identity.FromAddress(acc.Address.Hex()), 386 ConsumersHermesID: acc.Address, 387 AddressProvider: &mockAddressProvider{}, 388 HermesStatusChecker: &mockHermesStatusChecker{statusToReturn: HermesStatus{IsActive: true}}, 389 EventBus: mocks.NewEventBus(), 390 } 391 invoiceTracker := NewInvoiceTracker(deps) 392 defer invoiceTracker.Stop() 393 394 errChan := make(chan error) 395 go func() { errChan <- invoiceTracker.Start() }() 396 397 invoice := <-mockSender.chanToWriteTo 398 assert.Equal(t, providerFirstInvoiceValue, invoice.AgreementTotal) 399 assert.Len(t, invoice.Hashlock, 64) 400 assert.Equal(t, strings.ToLower(acc.Address.Hex()), strings.ToLower(invoice.Provider)) 401 402 invoiceTracker.Stop() 403 assert.NoError(t, <-errChan) 404 } 405 406 func Test_InvoiceTracker_FreeServiceSendsInvoices(t *testing.T) { 407 dir, err := os.MkdirTemp("", "invoice_tracker_test") 408 assert.Nil(t, err) 409 defer os.RemoveAll(dir) 410 411 ks := identity.NewMockKeystore() 412 acc, err := ks.NewAccount("") 413 assert.Nil(t, err) 414 mockSender := &MockPeerInvoiceSender{ 415 chanToWriteTo: make(chan crypto.Invoice, 10), 416 } 417 418 exchangeMessageChan := make(chan crypto.ExchangeMessage) 419 bolt, err := boltdb.NewStorage(dir) 420 assert.Nil(t, err) 421 defer bolt.Close() 422 423 tracker := session.NewTracker(mbtime.Now) 424 invoiceStorage := NewProviderInvoiceStorage(NewInvoiceStorage(bolt)) 425 deps := InvoiceTrackerDeps{ 426 AgreedPrice: *market.NewPrice(600, 0), 427 Peer: identity.FromAddress("some peer"), 428 PeerInvoiceSender: mockSender, 429 InvoiceStorage: invoiceStorage, 430 TimeTracker: &tracker, 431 LimitChargePeriod: time.Nanosecond, 432 LimitNotPaidInvoice: big.NewInt(0), 433 ChargePeriod: time.Nanosecond, 434 ChargePeriodLeeway: 15 * time.Second, 435 ExchangeMessageChan: exchangeMessageChan, 436 ExchangeMessageWaitTimeout: time.Second, 437 ProviderID: identity.FromAddress(acc.Address.Hex()), 438 ConsumersHermesID: acc.Address, 439 AddressProvider: &mockAddressProvider{}, 440 HermesStatusChecker: &mockHermesStatusChecker{statusToReturn: HermesStatus{IsActive: true}}, 441 EventBus: mocks.NewEventBus(), 442 } 443 invoiceTracker := NewInvoiceTracker(deps) 444 defer invoiceTracker.Stop() 445 446 errChan := make(chan error) 447 go func() { errChan <- invoiceTracker.Start() }() 448 449 invoice := <-mockSender.chanToWriteTo 450 assert.Equal(t, big.NewInt(0), invoice.AgreementTotal) 451 assert.Len(t, invoice.Hashlock, 64) 452 assert.Equal(t, strings.ToLower(acc.Address.Hex()), strings.ToLower(invoice.Provider)) 453 454 invoiceTracker.Stop() 455 assert.NoError(t, <-errChan) 456 } 457 458 func Test_sendsInvoiceIfThresholdReached(t *testing.T) { 459 tracker := session.NewTracker(mbtime.Now) 460 tracker.StartTracking() 461 deps := InvoiceTrackerDeps{ 462 TimeTracker: &tracker, 463 EventBus: mocks.NewEventBus(), 464 AgreedPrice: *market.NewPrice(600, 10995116277760), 465 MaxNotPaidInvoice: big.NewInt(100), 466 } 467 invoiceTracker := NewInvoiceTracker(deps) 468 invoiceTracker.dataTransferred = DataTransferred{ 469 Up: 100, 470 Down: 100, 471 } 472 invoiceTracker.invoiceDebounceRate = time.Nanosecond 473 defer invoiceTracker.Stop() 474 475 go invoiceTracker.sendInvoicesWhenNeeded(time.Millisecond * 5) 476 477 res := <-invoiceTracker.invoiceChannel 478 assert.True(t, res) 479 } 480 481 func Test_sendsInvoiceIfTimePassed(t *testing.T) { 482 tracker := session.NewTracker(mbtime.Now) 483 tracker.StartTracking() 484 deps := InvoiceTrackerDeps{ 485 TimeTracker: &tracker, 486 EventBus: mocks.NewEventBus(), 487 AgreedPrice: *market.NewPrice(600, 0), 488 MaxNotPaidInvoice: big.NewInt(100), 489 ChargePeriod: time.Millisecond * 2, 490 LimitChargePeriod: time.Millisecond * 3, 491 LimitNotPaidInvoice: big.NewInt(0), 492 } 493 invoiceTracker := NewInvoiceTracker(deps) 494 invoiceTracker.dataTransferred = DataTransferred{ 495 Up: 1, 496 Down: 1, 497 } 498 invoiceTracker.invoiceDebounceRate = time.Nanosecond 499 500 wait := make(chan struct{}, 0) 501 go func() { 502 defer close(wait) 503 invoiceTracker.sendInvoicesWhenNeeded(time.Millisecond * 5) 504 }() 505 506 res := <-invoiceTracker.invoiceChannel 507 assert.False(t, res) 508 res = <-invoiceTracker.invoiceChannel 509 assert.False(t, res) 510 invoiceTracker.Stop() 511 512 <-wait 513 // Test that MaxUnpaid and ChargePeriod Increased 514 assert.Equal(t, time.Millisecond*3, invoiceTracker.deps.ChargePeriod, "charge period should increase up to limit") 515 516 } 517 518 func Test_sendsInvoiceIfDataUsed(t *testing.T) { 519 tracker := session.NewTracker(mbtime.Now) 520 tracker.StartTracking() 521 deps := InvoiceTrackerDeps{ 522 TimeTracker: &tracker, 523 EventBus: mocks.NewEventBus(), 524 AgreedPrice: *market.NewPrice(600, 100), 525 MaxNotPaidInvoice: big.NewInt(100), 526 ChargePeriod: time.Millisecond * 2, 527 LimitChargePeriod: time.Millisecond * 3, 528 LimitNotPaidInvoice: big.NewInt(140), 529 } 530 invoiceTracker := NewInvoiceTracker(deps) 531 invoiceTracker.invoiceDebounceRate = time.Nanosecond 532 invoiceTracker.dataTransferred = DataTransferred{ 533 Up: 1000000000, 534 Down: 1000000000, 535 } 536 invoiceTracker.invoiceDebounceRate = time.Millisecond * 1 537 538 wait := make(chan struct{}, 0) 539 go func() { 540 defer close(wait) 541 invoiceTracker.sendInvoicesWhenNeeded(time.Millisecond * 5) 542 }() 543 544 res := <-invoiceTracker.invoiceChannel 545 assert.True(t, res) 546 res = <-invoiceTracker.invoiceChannel 547 assert.True(t, res) 548 invoiceTracker.Stop() 549 550 <-wait 551 // Test that MaxUnpaid and ChargePeriod Increased 552 assert.Equal(t, big.NewInt(140), invoiceTracker.deps.MaxNotPaidInvoice, "max unpaid invoice should increase up to limit") 553 554 } 555 556 func Test_calculateMaxNotReceivedExchangeMessageCount(t *testing.T) { 557 res := calculateMaxNotReceivedExchangeMessageCount(time.Minute*5, time.Second*240) 558 assert.Equal(t, uint64(1), res) 559 res = calculateMaxNotReceivedExchangeMessageCount(time.Minute*5, time.Second*20) 560 assert.Equal(t, uint64(15), res) 561 res = calculateMaxNotReceivedExchangeMessageCount(time.Hour*2, time.Second*20) 562 assert.Equal(t, uint64(360), res) 563 } 564 565 func generateExchangeMessage(t *testing.T, amount *big.Int, invoice crypto.Invoice, channel string) (crypto.ExchangeMessage, string) { 566 dir, err := os.MkdirTemp("", "invoice_tracker_test") 567 assert.Nil(t, err) 568 defer os.RemoveAll(dir) 569 570 ks := identity.NewMockKeystore() 571 acc, err := ks.NewAccount("") 572 assert.Nil(t, err) 573 574 err = ks.Unlock(acc, "") 575 assert.Nil(t, err) 576 577 if channel == "" { 578 addr, err := crypto.GenerateChannelAddress(acc.Address.Hex(), mockHermesAddress, mockRegistryAddress, mockChannelImplementation) 579 assert.Nil(t, err) 580 channel = addr 581 } 582 583 em, err := crypto.CreateExchangeMessage(1, invoice, amount, channel, "", ks, acc.Address) 584 assert.Nil(t, err) 585 if em != nil { 586 return *em, acc.Address.Hex() 587 } 588 return crypto.ExchangeMessage{}, acc.Address.Hex() 589 } 590 591 func TestInvoiceTracker_receiveExchangeMessageOrTimeout(t *testing.T) { 592 dir, err := os.MkdirTemp("", "invoice_tracker_test") 593 assert.Nil(t, err) 594 defer os.RemoveAll(dir) 595 596 bolt, err := boltdb.NewStorage(dir) 597 assert.Nil(t, err) 598 defer bolt.Close() 599 600 msg1, addr1 := generateExchangeMessage(t, big.NewInt(10), crypto.Invoice{AgreementTotal: big.NewInt(10), AgreementID: new(big.Int), TransactorFee: new(big.Int)}, "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C") 601 msg2, addr2 := generateExchangeMessage(t, big.NewInt(10), crypto.Invoice{AgreementTotal: big.NewInt(10), AgreementID: new(big.Int), TransactorFee: new(big.Int), Hashlock: "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C"}, "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C") 602 msg3, addr3 := generateExchangeMessage(t, big.NewInt(10), crypto.Invoice{AgreementTotal: big.NewInt(10), AgreementID: new(big.Int), TransactorFee: new(big.Int), Hashlock: "0x441Da57A51e42DAB7Daf55909Af93A9b00eEF23C"}, "") 603 type fields struct { 604 peer identity.Identity 605 exchangeMessageChan chan crypto.ExchangeMessage 606 exchangeMessageWaitTimeout time.Duration 607 hermesFailureCount uint64 608 hermesPromiseStorage hermesPromiseStorage 609 hermesID common.Address 610 AgreementID *big.Int 611 lastExchangeMessage crypto.ExchangeMessage 612 invoicesSent map[string]sentInvoice 613 addressProvider addressProvider 614 } 615 tests := []struct { 616 name string 617 fields fields 618 wantErr bool 619 em *crypto.ExchangeMessage 620 }{ 621 { 622 name: "errors on invalid signature", 623 wantErr: true, 624 fields: fields{ 625 exchangeMessageWaitTimeout: time.Minute, 626 exchangeMessageChan: make(chan crypto.ExchangeMessage), 627 peer: identity.FromAddress(addr1), 628 lastExchangeMessage: crypto.ExchangeMessage{ 629 Promise: crypto.Promise{ 630 Amount: new(big.Int), 631 Fee: new(big.Int), 632 }, 633 AgreementID: new(big.Int), 634 AgreementTotal: new(big.Int), 635 }, 636 }, 637 em: &crypto.ExchangeMessage{}, 638 }, 639 { 640 name: "errors on missmatching hashlocks", 641 wantErr: true, 642 fields: fields{ 643 exchangeMessageWaitTimeout: time.Minute, 644 exchangeMessageChan: make(chan crypto.ExchangeMessage), 645 peer: identity.FromAddress(addr1), 646 lastExchangeMessage: crypto.ExchangeMessage{ 647 Promise: crypto.Promise{ 648 Amount: new(big.Int), 649 Fee: new(big.Int), 650 }, 651 AgreementID: new(big.Int), 652 AgreementTotal: new(big.Int), 653 }, 654 }, 655 em: &msg1, 656 }, 657 { 658 name: "errors on bad channel ID", 659 wantErr: true, 660 fields: fields{ 661 exchangeMessageWaitTimeout: time.Minute, 662 exchangeMessageChan: make(chan crypto.ExchangeMessage), 663 peer: identity.FromAddress(addr2), 664 lastExchangeMessage: crypto.ExchangeMessage{ 665 Promise: crypto.Promise{ 666 Amount: new(big.Int), 667 Fee: new(big.Int), 668 }, 669 AgreementID: new(big.Int), 670 AgreementTotal: new(big.Int), 671 }, 672 }, 673 em: &msg2, 674 }, 675 { 676 name: "completes green path", 677 wantErr: false, 678 fields: fields{ 679 addressProvider: &mockAddressProvider{addrToReturn: common.BytesToAddress(msg3.Promise.ChannelID)}, 680 exchangeMessageWaitTimeout: time.Minute, 681 exchangeMessageChan: make(chan crypto.ExchangeMessage), 682 hermesPromiseStorage: &mockHermesPromiseStorage{}, 683 peer: identity.FromAddress(addr3), 684 hermesID: common.HexToAddress(mockHermesAddress), 685 lastExchangeMessage: crypto.ExchangeMessage{ 686 Promise: crypto.Promise{ 687 Amount: new(big.Int), 688 Fee: new(big.Int), 689 }, 690 AgreementID: new(big.Int), 691 AgreementTotal: new(big.Int), 692 }, 693 invoicesSent: map[string]sentInvoice{ 694 hex.EncodeToString(msg3.Promise.Hashlock): { 695 invoice: crypto.Invoice{ 696 Hashlock: hex.EncodeToString(msg3.Promise.Hashlock), 697 }, 698 }, 699 }, 700 }, 701 em: &msg3, 702 }, 703 } 704 for _, tt := range tests { 705 t.Run(tt.name, func(t *testing.T) { 706 deps := InvoiceTrackerDeps{ 707 Peer: tt.fields.peer, 708 ExchangeMessageChan: tt.fields.exchangeMessageChan, 709 ExchangeMessageWaitTimeout: tt.fields.exchangeMessageWaitTimeout, 710 ConsumersHermesID: tt.fields.hermesID, 711 EventBus: mocks.NewEventBus(), 712 InvoiceStorage: NewProviderInvoiceStorage(NewInvoiceStorage(bolt)), 713 AddressProvider: tt.fields.addressProvider, 714 AgreedPrice: *market.NewPrice(0, 0), 715 LimitChargePeriod: time.Nanosecond, 716 LimitNotPaidInvoice: big.NewInt(0), 717 } 718 it := &InvoiceTracker{ 719 hermesFailureCount: tt.fields.hermesFailureCount, 720 lastExchangeMessage: tt.fields.lastExchangeMessage, 721 agreementID: tt.fields.AgreementID, 722 deps: deps, 723 invoicesSent: tt.fields.invoicesSent, 724 } 725 if err := it.handleExchangeMessage(*tt.em); (err != nil) != tt.wantErr { 726 t.Errorf("InvoiceTracker.receiveExchangeMessageOrTimeout() error = %v, wantErr %v", err, tt.wantErr) 727 } 728 }) 729 } 730 } 731 732 func TestInvoiceTracker_handleHermesError(t *testing.T) { 733 tests := []struct { 734 name string 735 maxHermesFailureCount uint64 736 err error 737 wantErr error 738 }{ 739 { 740 name: "ignores nil errors", 741 wantErr: nil, 742 err: nil, 743 }, 744 { 745 name: "handles wrapped errors", 746 wantErr: ErrHermesInternal, 747 err: errors.Wrap(ErrHermesInternal, "pita bread"), 748 }, 749 { 750 name: "bubbles internal on failure exceeded", 751 wantErr: ErrHermesInternal, 752 err: ErrHermesInternal, 753 }, 754 { 755 name: "returns nil on internal not exceeding limit", 756 wantErr: nil, 757 maxHermesFailureCount: 1, 758 err: ErrHermesInternal, 759 }, 760 { 761 name: "bubbles hashlock missmatch on failure exceeded", 762 wantErr: ErrHermesHashlockMissmatch, 763 err: ErrHermesHashlockMissmatch, 764 }, 765 { 766 name: "returns nil on hashlock missmatch not exceeding limit", 767 wantErr: nil, 768 maxHermesFailureCount: 1, 769 err: ErrHermesHashlockMissmatch, 770 }, 771 { 772 name: "returns unknown error if failures exceeded", 773 wantErr: errors.New("unknown error"), 774 err: errors.New("unknown error"), 775 }, 776 { 777 name: "returns nil on unknown error if failures not exceeded", 778 wantErr: nil, 779 maxHermesFailureCount: 100, 780 err: errors.New("unknown error"), 781 }, 782 { 783 name: "returns overspend", 784 maxHermesFailureCount: 100, 785 wantErr: ErrHermesOverspend, 786 err: ErrHermesOverspend, 787 }, 788 } 789 for _, tt := range tests { 790 t.Run(tt.name, func(t *testing.T) { 791 it := &InvoiceTracker{ 792 deps: InvoiceTrackerDeps{ 793 MaxHermesFailureCount: tt.maxHermesFailureCount, 794 LimitChargePeriod: time.Nanosecond, 795 LimitNotPaidInvoice: big.NewInt(0), 796 }, 797 } 798 err := it.handleHermesError(tt.err) 799 if tt.wantErr == nil { 800 assert.NoError(t, err, tt.name) 801 } else { 802 assert.EqualError(t, errors.Cause(err), tt.wantErr.Error(), tt.name) 803 } 804 }) 805 } 806 } 807 808 type mockEncryptor struct { 809 errToReturn error 810 } 811 812 func (me *mockEncryptor) Decrypt(addr common.Address, encrypted []byte) ([]byte, error) { 813 return encrypted, me.errToReturn 814 } 815 816 func (me *mockEncryptor) Encrypt(addr common.Address, plaintext []byte) ([]byte, error) { 817 return plaintext, me.errToReturn 818 } 819 820 type mockHermesPromiseStorage struct { 821 toReturn HermesPromise 822 errToReturn error 823 } 824 825 func (maps *mockHermesPromiseStorage) Store(_ HermesPromise) error { 826 return maps.errToReturn 827 } 828 829 func (maps *mockHermesPromiseStorage) Delete(_ HermesPromise) error { 830 return maps.errToReturn 831 } 832 833 func (maps *mockHermesPromiseStorage) Get(chainID int64, _ string) (HermesPromise, error) { 834 return maps.toReturn, maps.errToReturn 835 } 836 837 func (maps *mockHermesPromiseStorage) List(_ HermesPromiseFilter) ([]HermesPromise, error) { 838 return []HermesPromise{maps.toReturn}, maps.errToReturn 839 } 840 841 type testEvent struct { 842 name string 843 value interface{} 844 } 845 846 type mockPublisher struct { 847 publicationChan chan testEvent 848 } 849 850 func (mp *mockPublisher) Publish(topic string, payload interface{}) { 851 if mp.publicationChan != nil { 852 mp.publicationChan <- testEvent{ 853 name: topic, 854 value: payload, 855 } 856 } 857 } 858 859 func (mp *mockPublisher) Subscribe(topic string, fn interface{}) error { 860 return nil 861 } 862 863 func (mp *mockPublisher) SubscribeWithUID(topic, uid string, fn interface{}) error { 864 return nil 865 } 866 867 func (mp *mockPublisher) SubscribeAsync(topic string, fn interface{}) error { 868 return nil 869 } 870 871 func (mp *mockPublisher) Unsubscribe(topic string, fn interface{}) error { 872 return nil 873 } 874 875 func (mp *mockPublisher) UnsubscribeWithUID(topic, uid string, fn interface{}) error { 876 return nil 877 } 878 879 type mockObserver struct { 880 } 881 882 func (mo *mockObserver) GetHermeses(f *observer.HermesFilter) (observer.HermesesResponse, error) { 883 return observer.HermesesResponse{}, nil 884 } 885 886 func (mo *mockObserver) GetHermesData(chainId int64, hermesAddress common.Address) (*observer.HermesResponse, error) { 887 return nil, nil 888 } 889 890 func TestInvoiceTracker_validateExchangeMessage(t *testing.T) { 891 type fields struct { 892 deps InvoiceTrackerDeps 893 } 894 type args struct { 895 em crypto.ExchangeMessage 896 } 897 tests := []struct { 898 name string 899 fields fields 900 args args 901 wantErr bool 902 }{ 903 { 904 args: args{ 905 em: crypto.ExchangeMessage{ 906 HermesID: "0x1", 907 }, 908 }, 909 name: "rejects exchange message with unsupported hermes", 910 wantErr: true, 911 fields: fields{ 912 deps: InvoiceTrackerDeps{}, 913 }, 914 }, 915 } 916 for _, tt := range tests { 917 t.Run(tt.name, func(t *testing.T) { 918 it := &InvoiceTracker{ 919 deps: tt.fields.deps, 920 } 921 if err := it.validateExchangeMessage(tt.args.em); (err != nil) != tt.wantErr { 922 t.Errorf("InvoiceTracker.validateExchangeMessage() error = %v, wantErr %v", err, tt.wantErr) 923 } 924 }) 925 } 926 } 927 928 type mockHermesStatusChecker struct { 929 statusToReturn HermesStatus 930 errToReturn error 931 } 932 933 func (mhsc *mockHermesStatusChecker) GetHermesStatus(chainID int64, registryAddress common.Address, hermesID common.Address) (HermesStatus, error) { 934 return mhsc.statusToReturn, mhsc.errToReturn 935 }