github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/session/pingpong/consumer_balance_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  	"errors"
    22  	"fmt"
    23  	"math/big"
    24  	"sync"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/mysteriumnetwork/node/eventbus"
    30  	"github.com/mysteriumnetwork/node/identity"
    31  	"github.com/mysteriumnetwork/node/identity/registry"
    32  	"github.com/mysteriumnetwork/node/session/pingpong/event"
    33  	"github.com/mysteriumnetwork/payments/client"
    34  	"github.com/stretchr/testify/assert"
    35  )
    36  
    37  var mockMystSCaddress = common.HexToAddress("0x0")
    38  
    39  var initialBalance = big.NewInt(100000000)
    40  
    41  var defaultWaitTime = 3 * time.Second
    42  var defaultWaitInterval = 20 * time.Millisecond
    43  
    44  var defaultCfg = ConsumerBalanceTrackerConfig{
    45  	FastSync: PollConfig{
    46  		Interval: time.Second * 1,
    47  		Timeout:  time.Second * 6,
    48  	},
    49  	LongSync: PollConfig{
    50  		Interval: time.Minute,
    51  	},
    52  }
    53  
    54  func TestConsumerBalanceTracker_Fresh_Registration(t *testing.T) {
    55  	id1 := identity.FromAddress("0x000000001")
    56  	id2 := identity.FromAddress("0x000000002")
    57  	assert.NotEqual(t, id1.Address, id2.Address)
    58  
    59  	bus := eventbus.New()
    60  	mcts := mockConsumerTotalsStorage{
    61  		bus: bus,
    62  		res: big.NewInt(0),
    63  	}
    64  	bc := mockConsumerBalanceChecker{
    65  		channelToReturn: client.ConsumerChannel{
    66  			Balance: initialBalance,
    67  			Settled: big.NewInt(0),
    68  		},
    69  		mystBalanceToReturn: big.NewInt(0),
    70  	}
    71  	calc := mockAddressProvider{}
    72  
    73  	mockBlockchainProvider := mockBlockchainInfoProvider{}
    74  
    75  	cbt := NewConsumerBalanceTracker(bus, &bc, &mcts, &mockconsumerInfoGetter{}, &mockTransactor{}, &mockRegistrationStatusProvider{}, &calc, &mockBlockchainProvider, defaultCfg)
    76  
    77  	err := cbt.Subscribe(bus)
    78  	assert.NoError(t, err)
    79  
    80  	bus.Publish(registry.AppTopicIdentityRegistration, registry.AppEventIdentityRegistration{
    81  		ID:      id1,
    82  		Status:  registry.Registered,
    83  		ChainID: 1,
    84  	})
    85  	bus.Publish(registry.AppTopicIdentityRegistration, registry.AppEventIdentityRegistration{
    86  		ID:      id2,
    87  		Status:  registry.RegistrationError,
    88  		ChainID: 1,
    89  	})
    90  
    91  	assert.Eventually(t, func() bool {
    92  		return cbt.GetBalance(1, id1).Cmp(initialBalance) == 0
    93  	}, defaultWaitTime, defaultWaitInterval)
    94  
    95  	assert.Eventually(t, func() bool {
    96  		return cbt.GetBalance(1, id2).Uint64() == 0
    97  	}, defaultWaitTime, defaultWaitInterval)
    98  
    99  	bus.Publish(identity.AppTopicIdentityUnlock, identity.AppEventIdentityUnlock{
   100  		ChainID: 1,
   101  		ID:      id2,
   102  	})
   103  
   104  	assert.Eventually(t, func() bool {
   105  		return cbt.GetBalance(1, id2).Cmp(initialBalance) == 0
   106  	}, defaultWaitTime, defaultWaitInterval)
   107  
   108  	var promised = big.NewInt(100)
   109  	bus.Publish(event.AppTopicGrandTotalChanged, event.AppEventGrandTotalChanged{
   110  		ChainID:    1,
   111  		ConsumerID: id1,
   112  		Current:    promised,
   113  	})
   114  
   115  	assert.Eventually(t, func() bool {
   116  		return cbt.GetBalance(1, id1).Cmp(new(big.Int).Sub(initialBalance, promised)) == 0
   117  	}, defaultWaitTime, defaultWaitInterval)
   118  }
   119  
   120  func TestConsumerBalanceTracker_Fast_Registration(t *testing.T) {
   121  	id1 := identity.FromAddress("0x000000001")
   122  	t.Run("Takes balance from hermes response", func(t *testing.T) {
   123  		bus := eventbus.New()
   124  		mcts := mockConsumerTotalsStorage{
   125  			bus: bus,
   126  		}
   127  		bc := mockConsumerBalanceChecker{
   128  			channelToReturn: client.ConsumerChannel{
   129  				Balance: initialBalance,
   130  				Settled: big.NewInt(0),
   131  			},
   132  		}
   133  		calc := mockAddressProvider{}
   134  		mockBlockchainProvider := mockBlockchainInfoProvider{}
   135  
   136  		var ba = big.NewInt(10000000)
   137  		cbt := NewConsumerBalanceTracker(bus, &bc, &mcts, &mockconsumerInfoGetter{}, &mockTransactor{
   138  			statusToReturn: registry.TransactorStatusResponse{
   139  				Status:       registry.TransactorRegistrationEntryStatusCreated,
   140  				BountyAmount: ba,
   141  				ChainID:      1,
   142  			},
   143  		}, &mockRegistrationStatusProvider{}, &calc, &mockBlockchainProvider, defaultCfg)
   144  
   145  		err := cbt.Subscribe(bus)
   146  		assert.NoError(t, err)
   147  
   148  		bus.Publish(registry.AppTopicIdentityRegistration, registry.AppEventIdentityRegistration{
   149  			ID:      id1,
   150  			Status:  registry.InProgress,
   151  			ChainID: 1,
   152  		})
   153  
   154  		assert.Eventually(t, func() bool {
   155  			return cbt.GetBalance(1, id1).Cmp(ba) == 0
   156  		}, defaultWaitTime, defaultWaitInterval)
   157  	})
   158  	t.Run("Falls back to blockchain balance if no bounty is specified on transactor", func(t *testing.T) {
   159  		t.Skip()
   160  		bus := eventbus.New()
   161  		mcts := mockConsumerTotalsStorage{
   162  			res: big.NewInt(0),
   163  			bus: bus,
   164  		}
   165  		var ba = big.NewInt(10000000)
   166  		bc := mockConsumerBalanceChecker{
   167  			channelToReturn: client.ConsumerChannel{
   168  				Balance: initialBalance,
   169  				Settled: big.NewInt(0),
   170  			},
   171  			mystBalanceToReturn: ba,
   172  		}
   173  		calc := mockAddressProvider{}
   174  		mockBlockchainProvider := mockBlockchainInfoProvider{}
   175  
   176  		cbt := NewConsumerBalanceTracker(bus, &bc, &mcts, &mockconsumerInfoGetter{}, &mockTransactor{
   177  			statusToReturn: registry.TransactorStatusResponse{
   178  				Status:       registry.TransactorRegistrationEntryStatusCreated,
   179  				BountyAmount: big.NewInt(0),
   180  				ChainID:      1,
   181  			},
   182  		}, &mockRegistrationStatusProvider{}, &calc, &mockBlockchainProvider, defaultCfg)
   183  
   184  		err := cbt.Subscribe(bus)
   185  		assert.NoError(t, err)
   186  
   187  		bus.Publish(registry.AppTopicIdentityRegistration, registry.AppEventIdentityRegistration{
   188  			ID:      id1,
   189  			Status:  registry.InProgress,
   190  			ChainID: 1,
   191  		})
   192  
   193  		assert.Eventually(t, func() bool {
   194  			return cbt.GetBalance(1, id1).Cmp(ba) == 0
   195  		}, defaultWaitTime, defaultWaitInterval)
   196  	})
   197  }
   198  
   199  func TestConsumerBalanceTracker_Handles_GrandTotalChanges(t *testing.T) {
   200  	id1 := identity.FromAddress("0x000000001")
   201  	var grandTotalPromised = big.NewInt(100)
   202  	bus := eventbus.New()
   203  
   204  	mcts := mockConsumerTotalsStorage{
   205  		res: grandTotalPromised,
   206  	}
   207  	bc := mockConsumerBalanceChecker{
   208  		channelToReturn: client.ConsumerChannel{
   209  			Balance: initialBalance,
   210  			Settled: big.NewInt(0),
   211  		},
   212  	}
   213  	calc := mockAddressProvider{}
   214  	mockBlockchainProvider := mockBlockchainInfoProvider{}
   215  
   216  	cbt := NewConsumerBalanceTracker(bus, &bc, &mcts, &mockconsumerInfoGetter{grandTotalPromised, new(big.Int)}, &mockTransactor{}, &mockRegistrationStatusProvider{}, &calc, &mockBlockchainProvider, defaultCfg)
   217  
   218  	err := cbt.Subscribe(bus)
   219  	assert.NoError(t, err)
   220  	bus.Publish(identity.AppTopicIdentityUnlock, identity.AppEventIdentityUnlock{
   221  		ChainID: 1,
   222  		ID:      id1,
   223  	})
   224  	assert.Eventually(t, func() bool {
   225  		return cbt.GetBalance(1, id1).Cmp(new(big.Int).Sub(initialBalance, grandTotalPromised)) == 0
   226  	}, defaultWaitTime, defaultWaitInterval)
   227  
   228  	var diff = big.NewInt(10)
   229  	bus.Publish(event.AppTopicGrandTotalChanged, event.AppEventGrandTotalChanged{
   230  		ChainID:    1,
   231  		ConsumerID: id1,
   232  		Current:    new(big.Int).Add(grandTotalPromised, diff),
   233  	})
   234  
   235  	assert.Eventually(t, func() bool {
   236  		div := new(big.Int).Sub(initialBalance, grandTotalPromised)
   237  		currentBalance := new(big.Int).Sub(div, diff)
   238  		return cbt.GetBalance(1, id1).Cmp(currentBalance) == 0
   239  	}, defaultWaitTime, defaultWaitInterval)
   240  
   241  	var diff2 = big.NewInt(20)
   242  	bus.Publish(event.AppTopicGrandTotalChanged, event.AppEventGrandTotalChanged{
   243  		ChainID:    1,
   244  		ConsumerID: id1,
   245  		Current:    new(big.Int).Add(grandTotalPromised, diff2),
   246  	})
   247  
   248  	assert.Eventually(t, func() bool {
   249  		div := new(big.Int).Sub(initialBalance, grandTotalPromised)
   250  		currentBalance := new(big.Int).Sub(div, diff2)
   251  		return cbt.GetBalance(1, id1).Cmp(currentBalance) == 0
   252  	}, defaultWaitTime, defaultWaitInterval)
   253  }
   254  
   255  func TestConsumerBalanceTracker_FallsBackToTransactorIfInProgress(t *testing.T) {
   256  	id1 := identity.FromAddress("0x000000001")
   257  	var grandTotalPromised = new(big.Int)
   258  	bus := eventbus.New()
   259  	mcts := mockConsumerTotalsStorage{
   260  		res: grandTotalPromised,
   261  		bus: bus,
   262  	}
   263  	bc := mockConsumerBalanceChecker{
   264  		errToReturn:         errors.New("no contract deployed"),
   265  		mystBalanceToReturn: initialBalance,
   266  	}
   267  	cfg := defaultCfg
   268  	cfg.LongSync.Interval = time.Millisecond * 300
   269  	calc := mockAddressProvider{}
   270  	mockBlockchainProvider := mockBlockchainInfoProvider{}
   271  
   272  	cbt := NewConsumerBalanceTracker(bus, &bc, &mcts, &mockconsumerInfoGetter{grandTotalPromised, new(big.Int)}, &mockTransactor{
   273  		statusToReturn: registry.TransactorStatusResponse{
   274  			Status:       registry.TransactorRegistrationEntryStatusCreated,
   275  			ChainID:      1,
   276  			BountyAmount: big.NewInt(100),
   277  		},
   278  	}, &mockRegistrationStatusProvider{
   279  		map[string]mockRegistrationStatus{
   280  			fmt.Sprintf("%d%s", 1, id1.Address): {
   281  				status: registry.InProgress,
   282  			},
   283  		},
   284  	}, &calc, &mockBlockchainProvider, cfg)
   285  
   286  	err := cbt.Subscribe(bus)
   287  	assert.NoError(t, err)
   288  	bus.Publish(identity.AppTopicIdentityUnlock, identity.AppEventIdentityUnlock{
   289  		ChainID: 1,
   290  		ID:      id1,
   291  	})
   292  	assert.Eventually(t, func() bool {
   293  		return cbt.GetBalance(1, id1).Uint64() == 100
   294  	}, defaultWaitTime, defaultWaitInterval)
   295  }
   296  
   297  func TestConsumerBalanceTracker_UnregisteredBalanceReturned(t *testing.T) {
   298  	id1 := identity.FromAddress("0x000000001")
   299  	var grandTotalPromised = new(big.Int)
   300  	bus := eventbus.New()
   301  	mcts := mockConsumerTotalsStorage{
   302  		res: grandTotalPromised,
   303  		bus: bus,
   304  	}
   305  	bc := mockConsumerBalanceChecker{
   306  		mystBalanceToReturn: initialBalance,
   307  		errToReturn:         errors.New("boom"),
   308  	}
   309  	calc := mockAddressProvider{}
   310  	mockBlockchainProvider := mockBlockchainInfoProvider{}
   311  
   312  	cbt := NewConsumerBalanceTracker(bus, &bc, &mcts, &mockconsumerInfoGetter{grandTotalPromised, new(big.Int)}, &mockTransactor{}, &mockRegistrationStatusProvider{
   313  		map[string]mockRegistrationStatus{
   314  			fmt.Sprintf("%d%s", 1, id1.Address): {
   315  				status: registry.Unregistered,
   316  			},
   317  		},
   318  	}, &calc, &mockBlockchainProvider, defaultCfg)
   319  
   320  	b := cbt.ForceBalanceUpdate(1, id1)
   321  	assert.Equal(t, initialBalance, b)
   322  }
   323  
   324  func TestConsumerBalanceTracker_InprogressUnregisteredBalanceReturnedWhenNoBounty(t *testing.T) {
   325  	id1 := identity.FromAddress("0x000000001")
   326  	var grandTotalPromised = new(big.Int)
   327  	bus := eventbus.New()
   328  	mcts := mockConsumerTotalsStorage{
   329  		res: grandTotalPromised,
   330  		bus: bus,
   331  	}
   332  	bc := mockConsumerBalanceChecker{
   333  		errToReturn:         errors.New("no contract deployed"),
   334  		mystBalanceToReturn: initialBalance,
   335  	}
   336  	cfg := defaultCfg
   337  	cfg.LongSync.Interval = time.Millisecond * 300
   338  	calc := mockAddressProvider{}
   339  	mockBlockchainProvider := mockBlockchainInfoProvider{}
   340  
   341  	cbt := NewConsumerBalanceTracker(bus, &bc, &mcts, &mockconsumerInfoGetter{grandTotalPromised, new(big.Int)}, &mockTransactor{
   342  		statusToReturn: registry.TransactorStatusResponse{
   343  			Status:       registry.TransactorRegistrationEntryStatusCreated,
   344  			ChainID:      1,
   345  			BountyAmount: big.NewInt(0),
   346  		},
   347  	}, &mockRegistrationStatusProvider{
   348  		map[string]mockRegistrationStatus{
   349  			fmt.Sprintf("%d%s", 1, id1.Address): {
   350  				status: registry.InProgress,
   351  			},
   352  		},
   353  	}, &calc, &mockBlockchainProvider, cfg)
   354  
   355  	err := cbt.Subscribe(bus)
   356  	assert.NoError(t, err)
   357  	bus.Publish(identity.AppTopicIdentityUnlock, identity.AppEventIdentityUnlock{
   358  		ChainID: 1,
   359  		ID:      id1,
   360  	})
   361  	assert.Eventually(t, func() bool {
   362  		return cbt.GetBalance(1, id1).Cmp(initialBalance) == 0
   363  	}, defaultWaitTime, defaultWaitInterval)
   364  }
   365  
   366  func TestConsumerBalanceTracker_RecoverGrandTotalPromisedSettledIsBiggerThanPromissedNotOffChain(t *testing.T) {
   367  	// make data race more likely to happen
   368  	for i := 0; i < 10; i++ {
   369  		id1 := identity.FromAddress("0x000000001")
   370  		grandTotalPromised := big.NewInt(10)
   371  		settledAmount := big.NewInt(11)
   372  		bus := eventbus.New()
   373  		mcts := NewConsumerTotalsStorage(bus)
   374  		bc := mockConsumerBalanceChecker{}
   375  		cfg := defaultCfg
   376  		cfg.LongSync.Interval = time.Millisecond * 300
   377  		calc := newMockAddressProvider()
   378  		calc.addrToReturn = id1.ToCommonAddress()
   379  		mockBlockchainProvider := &mockBlockchainInfoProvider{}
   380  
   381  		mockBlockchainProvider.AddConsumerChannelsHermes(1, id1.ToCommonAddress(), client.ConsumersHermes{
   382  			Settled: big.NewInt(6),
   383  		})
   384  
   385  		cbt := NewConsumerBalanceTracker(bus, &bc, mcts, &mockconsumerInfoGetter{grandTotalPromised, settledAmount}, &mockTransactor{}, &mockRegistrationStatusProvider{}, calc, mockBlockchainProvider, defaultCfg)
   386  
   387  		err := cbt.Subscribe(bus)
   388  		assert.NoError(t, err)
   389  		bus.Publish(identity.AppTopicIdentityUnlock, identity.AppEventIdentityUnlock{
   390  			ChainID: 1,
   391  			ID:      id1,
   392  		})
   393  		assert.Eventually(t, func() bool {
   394  			savedBalance, _ := mcts.Get(1, id1, common.BigToAddress(big.NewInt(0)))
   395  			return savedBalance.Cmp(big.NewInt(6)) == 0
   396  		}, defaultWaitTime, defaultWaitInterval)
   397  	}
   398  }
   399  
   400  type mockConsumerBalanceChecker struct {
   401  	channelToReturn client.ConsumerChannel
   402  	errToReturn     error
   403  	errLock         sync.Mutex
   404  
   405  	mystBalanceToReturn *big.Int
   406  	mystBalanceError    error
   407  }
   408  
   409  func (mcbc *mockConsumerBalanceChecker) getError() error {
   410  	mcbc.errLock.Lock()
   411  	defer mcbc.errLock.Unlock()
   412  	return mcbc.errToReturn
   413  }
   414  
   415  func (mcbc *mockConsumerBalanceChecker) setError(err error) {
   416  	mcbc.errLock.Lock()
   417  	defer mcbc.errLock.Unlock()
   418  	mcbc.errToReturn = err
   419  }
   420  
   421  func (mcbc *mockConsumerBalanceChecker) GetConsumerChannel(chainID int64, addr common.Address, mystSCAddress common.Address) (client.ConsumerChannel, error) {
   422  	return mcbc.channelToReturn, mcbc.getError()
   423  }
   424  
   425  func (mcbc *mockConsumerBalanceChecker) GetMystBalance(chainID int64, mystAddress, identity common.Address) (*big.Int, error) {
   426  	return mcbc.mystBalanceToReturn, mcbc.mystBalanceError
   427  }
   428  
   429  type mockconsumerInfoGetter struct {
   430  	amount  *big.Int
   431  	settled *big.Int
   432  }
   433  
   434  func (mcig *mockconsumerInfoGetter) GetConsumerData(_ int64, _ string, _ time.Duration) (HermesUserInfo, error) {
   435  	return HermesUserInfo{
   436  		Settled: mcig.settled,
   437  		LatestPromise: LatestPromise{
   438  			Amount: mcig.amount,
   439  		},
   440  	}, nil
   441  }
   442  
   443  func TestConsumerBalanceTracker_DoesNotBlockedOnEmptyBalancesList(t *testing.T) {
   444  	bus := eventbus.New()
   445  	mcts := mockConsumerTotalsStorage{bus: bus, res: big.NewInt(0)}
   446  	bc := mockConsumerBalanceChecker{
   447  		channelToReturn: client.ConsumerChannel{
   448  			Balance: initialBalance,
   449  			Settled: big.NewInt(0),
   450  		},
   451  	}
   452  	calc := mockAddressProvider{}
   453  	mockBlockchainProvider := mockBlockchainInfoProvider{}
   454  
   455  	cbt := NewConsumerBalanceTracker(bus, &bc, &mcts, &mockconsumerInfoGetter{}, &mockTransactor{}, &mockRegistrationStatusProvider{}, &calc, &mockBlockchainProvider, defaultCfg)
   456  
   457  	// Make sure we are not dead locked here. https://github.com/mysteriumnetwork/node/issues/2181
   458  	cbt.updateGrandTotal(1, identity.FromAddress("0x0000"), big.NewInt(1))
   459  }
   460  
   461  func TestConsumerBalance_GetBalance(t *testing.T) {
   462  	type fields struct {
   463  		BCBalance          *big.Int
   464  		BCSettled          *big.Int
   465  		GrandTotalPromised *big.Int
   466  	}
   467  	tests := []struct {
   468  		name   string
   469  		fields fields
   470  		want   *big.Int
   471  	}{
   472  		{
   473  			name: "handles bc balance underflow",
   474  			fields: fields{
   475  				BCBalance:          big.NewInt(0),
   476  				BCSettled:          big.NewInt(0),
   477  				GrandTotalPromised: big.NewInt(1),
   478  			},
   479  			want: big.NewInt(0),
   480  		},
   481  		{
   482  			name: "handles grand total underflow",
   483  			fields: fields{
   484  				BCBalance:          big.NewInt(0),
   485  				BCSettled:          big.NewInt(1),
   486  				GrandTotalPromised: big.NewInt(0),
   487  			},
   488  			want: big.NewInt(0),
   489  		},
   490  		{
   491  			name: "calculates balance correctly",
   492  			fields: fields{
   493  				BCBalance:          big.NewInt(3),
   494  				BCSettled:          big.NewInt(1),
   495  				GrandTotalPromised: big.NewInt(2),
   496  			},
   497  			want: big.NewInt(2),
   498  		},
   499  	}
   500  	for _, tt := range tests {
   501  		t.Run(tt.name, func(t *testing.T) {
   502  			cb := ConsumerBalance{
   503  				BCBalance:          tt.fields.BCBalance,
   504  				BCSettled:          tt.fields.BCSettled,
   505  				GrandTotalPromised: tt.fields.GrandTotalPromised,
   506  			}
   507  			if got := cb.GetBalance(); got.Cmp(tt.want) != 0 {
   508  				t.Errorf("ConsumerBalance.GetBalance() = %v, want %v", got, tt.want)
   509  			}
   510  		})
   511  	}
   512  }
   513  
   514  type mockAddressProvider struct {
   515  	transactor   common.Address
   516  	addrToReturn common.Address
   517  	channels     map[string]common.Address
   518  }
   519  
   520  func newMockAddressProvider() *mockAddressProvider {
   521  	return &mockAddressProvider{channels: make(map[string]common.Address)}
   522  }
   523  
   524  func (ma *mockAddressProvider) GetActiveChannelAddress(chainID int64, id common.Address) (common.Address, error) {
   525  	return ma.addrToReturn, nil
   526  }
   527  func (ma *mockAddressProvider) GetActiveChannelImplementation(chainID int64) (common.Address, error) {
   528  	return common.Address{}, nil
   529  }
   530  func (ma *mockAddressProvider) GetMystAddress(chainID int64) (common.Address, error) {
   531  	return common.Address{}, nil
   532  }
   533  func (ma *mockAddressProvider) GetActiveHermes(chainID int64) (common.Address, error) {
   534  	return common.Address{}, nil
   535  }
   536  func (ma *mockAddressProvider) GetRegistryAddress(chainID int64) (common.Address, error) {
   537  	return common.Address{}, nil
   538  }
   539  func (ma *mockAddressProvider) GetArbitraryChannelAddress(hermes, registry, channel common.Address, id common.Address) (common.Address, error) {
   540  	return ma.addrToReturn, nil
   541  }
   542  func (ma *mockAddressProvider) GetChannelImplementationForHermes(chainID int64, hermes common.Address) (common.Address, error) {
   543  	return common.Address{}, nil
   544  }
   545  func (ma *mockAddressProvider) GetKnownHermeses(chainID int64) ([]common.Address, error) {
   546  	return []common.Address{ma.addrToReturn}, nil
   547  }
   548  func (ma *mockAddressProvider) GetHermesChannelAddress(chainID int64, id, hermesAddr common.Address) (common.Address, error) {
   549  	channelAddr, _ := ma.channels[fmt.Sprintf("%d-%s", chainID, id)]
   550  	return channelAddr, nil
   551  }
   552  
   553  func (ma *mockAddressProvider) setChannelAddress(chainID int64, id, channelAddr common.Address) {
   554  	ma.channels[fmt.Sprintf("%d-%s", chainID, id)] = channelAddr
   555  }
   556  
   557  var _ blockchainInfoProvider = (*mockBlockchainInfoProvider)(nil)
   558  
   559  type mockBlockchainInfoProvider struct {
   560  	consumerChannelsHermesMap map[string]client.ConsumersHermes
   561  }
   562  
   563  func (p *mockBlockchainInfoProvider) GetConsumerChannelsHermes(chainID int64, channelAddress common.Address) (client.ConsumersHermes, error) {
   564  	result, ok := p.consumerChannelsHermesMap[p.mapKey(chainID, channelAddress)]
   565  	if !ok {
   566  		return client.ConsumersHermes{}, fmt.Errorf("mock consumer channels hermes not found")
   567  	}
   568  
   569  	return result, nil
   570  }
   571  
   572  func (p *mockBlockchainInfoProvider) AddConsumerChannelsHermes(chainID int64, channelAddress common.Address, consumerHermes client.ConsumersHermes) {
   573  	if p.consumerChannelsHermesMap == nil {
   574  		p.consumerChannelsHermesMap = map[string]client.ConsumersHermes{}
   575  	}
   576  
   577  	p.consumerChannelsHermesMap[p.mapKey(chainID, channelAddress)] = consumerHermes
   578  }
   579  
   580  func (p *mockBlockchainInfoProvider) mapKey(chainID int64, channelAddress common.Address) string {
   581  	return fmt.Sprintf("%d_%s", chainID, channelAddress.String())
   582  }