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  }