code.vegaprotocol.io/vega@v0.79.0/core/rewards/engine_test.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package rewards
    17  
    18  import (
    19  	"context"
    20  	"testing"
    21  	"time"
    22  
    23  	bmocks "code.vegaprotocol.io/vega/core/broker/mocks"
    24  	"code.vegaprotocol.io/vega/core/collateral"
    25  	"code.vegaprotocol.io/vega/core/rewards/mocks"
    26  	"code.vegaprotocol.io/vega/core/types"
    27  	"code.vegaprotocol.io/vega/libs/num"
    28  	"code.vegaprotocol.io/vega/logging"
    29  
    30  	"github.com/golang/mock/gomock"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  func Test(t *testing.T) {
    35  	t.Run("Update max payout per participant for staking and delegation reward scheme succeeds", testUpdateMaxPayoutPerParticipantForStakingRewardScheme)
    36  	t.Run("Calculation of reward payout succeeds", testCalculateRewards)
    37  	t.Run("Calculation of reward payout succeeds with map per participant", testCalculateRewardsWithMaxPerParticipant)
    38  	t.Run("Payout distribution succeeds", testDistributePayout)
    39  	t.Run("Process epoch end to calculate payout with no delay - rewards are distributed successfully", testOnEpochEventNoPayoutDelay)
    40  }
    41  
    42  func TestRewardFactors(t *testing.T) {
    43  	testEngine := getEngine(t)
    44  	engine := testEngine.engine
    45  
    46  	p, e := engine.calculateRewardFactors(num.DecimalFromInt64(10), num.DecimalFromInt64(10))
    47  	require.Equal(t, "0.5", p.String())
    48  	require.Equal(t, "0.5", e.String())
    49  
    50  	p, e = engine.calculateRewardFactors(num.DecimalFromInt64(100), num.DecimalFromInt64(0))
    51  	require.Equal(t, "1", p.String())
    52  	require.Equal(t, "0", e.String())
    53  
    54  	p, e = engine.calculateRewardFactors(num.DecimalFromInt64(0), num.DecimalFromInt64(1))
    55  	require.Equal(t, "0", p.String())
    56  	require.Equal(t, "1", e.String())
    57  
    58  	p, e = engine.calculateRewardFactors(num.DecimalFromInt64(99999999), num.DecimalFromInt64(1))
    59  	require.Equal(t, "0.99999999", p.String())
    60  	require.Equal(t, "0.00000001", e.String())
    61  
    62  	p, e = engine.calculateRewardFactors(num.DecimalFromInt64(1), num.DecimalFromInt64(99999999))
    63  	require.Equal(t, "0.00000001", p.String())
    64  	require.Equal(t, "0.99999999", e.String())
    65  }
    66  
    67  // test updating of max payout per participant for staking and delegation reward scheme.
    68  func testUpdateMaxPayoutPerParticipantForStakingRewardScheme(t *testing.T) {
    69  	testEngine := getEngine(t)
    70  	engine := testEngine.engine
    71  	require.NoError(t, engine.UpdateMaxPayoutPerParticipantForStakingRewardScheme(context.Background(), num.NewDecimalFromFloat(10000)))
    72  	require.Equal(t, num.NewUint(10000), engine.global.maxPayoutPerParticipant)
    73  }
    74  
    75  // test calculation of reward payout.
    76  func testCalculateRewards(t *testing.T) {
    77  	testEngine := getEngine(t)
    78  	now := time.Now()
    79  	testEngine.timeService.EXPECT().GetTimeNow().DoAndReturn(func() time.Time {
    80  		return now
    81  	}).AnyTimes()
    82  
    83  	engine := testEngine.engine
    84  	require.NoError(t, engine.UpdateAssetForStakingAndDelegation(context.Background(), "VEGA"))
    85  	require.NoError(t, engine.UpdateDelegatorShareForStakingRewardScheme(context.Background(), num.DecimalFromFloat(0.3)))
    86  	require.NoError(t, engine.UpdateMinimumValidatorStakeForStakingRewardScheme(context.Background(), num.NewDecimalFromFloat(0)))
    87  	require.NoError(t, engine.UpdateCompetitionLevelForStakingRewardScheme(context.Background(), num.DecimalFromFloat(1.1)))
    88  	require.NoError(t, engine.UpdateMinValidatorsStakingRewardScheme(context.Background(), 5))
    89  	require.NoError(t, engine.UpdateOptimalStakeMultiplierStakingRewardScheme(context.Background(), num.DecimalFromFloat(5)))
    90  	require.NoError(t, engine.UpdateMaxPayoutPerParticipantForStakingRewardScheme(context.Background(), num.DecimalZero()))
    91  	require.NoError(t, engine.UpdateErsatzRewardFactor(context.Background(), num.DecimalFromFloat(0.5)))
    92  
    93  	epoch := types.Epoch{EndTime: now}
    94  	rewardAccount, err := testEngine.collateral.GetGlobalRewardAccount("VEGA")
    95  	require.NoError(t, err)
    96  
    97  	testEngine.delegation.EXPECT().ProcessEpochDelegations(gomock.Any(), gomock.Any()).Return(testEngine.validatorData)
    98  	testEngine.delegation.EXPECT().GetValidatorData().AnyTimes()
    99  	testEngine.topology.EXPECT().RecalcValidatorSet(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   100  	testEngine.topology.EXPECT().GetRewardsScores(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, epochSeq string, delegationState []*types.ValidatorData, stakeScoreParams types.StakeScoreParams) (*types.ScoreData, *types.ScoreData) {
   101  		return &types.ScoreData{
   102  				NodeIDSlice: []string{"node1", "node2"},
   103  				NormalisedScores: map[string]num.Decimal{
   104  					"node1": num.DecimalFromFloat(0.2),
   105  					"node2": num.DecimalFromFloat(0.8),
   106  				},
   107  			}, &types.ScoreData{
   108  				NodeIDSlice: []string{"node3", "node4"},
   109  				NormalisedScores: map[string]num.Decimal{
   110  					"node3": num.DecimalFromFloat(0.6),
   111  					"node4": num.DecimalFromFloat(0.4),
   112  				},
   113  			}
   114  	})
   115  
   116  	require.NoError(t, testEngine.collateral.IncrementBalance(context.Background(), rewardAccount.ID, num.NewUint(1000000)))
   117  
   118  	payouts := engine.calculateRewardPayouts(context.Background(), epoch)
   119  	primary := payouts[0]
   120  	ersatz := payouts[1]
   121  
   122  	// calculation
   123  	// node1 has total delegation of 15000
   124  	// node2 has total delegation of 60000
   125  	// node3 has total delegation of 4000
   126  	// node4 has total delegation of 6000
   127  	// primary validators have stake of 75000
   128  	// ersatz validators have a stake of 10000
   129  	// therefore primary get 0.9375 of the reward, ersatz 0.0625 of the reward
   130  	// primary validators
   131  	// node1, node2
   132  	// node1 has normalised score of 0.2 => node1 and its delegators get 0.2 * 0.9375 * 1e6 = 1875000
   133  	// out of 187500, delegators get 0.3 (delegatorShare) * 2/3 (the ratio of delegation by delegator in node1)= 37500
   134  	// that leaves 187500-37500 = 150000 to node1
   135  	// out of the 37500 party1 gets 0.6x (22500) and party2 gets 0.4x (15000) given their ratio of delegation in the node
   136  	// node2 has normalised score of 0.8 => node 2 and its delegators get 0.8 * 0.9375 *1e6 = 750000
   137  	// out of the 750000, delegators get 0.3 (delegatorShare) * 2/3 (the ratio of delegation by delegator in node2)= 150000
   138  	// the 150000 goes exclusively to party1 and added to the 22500 they get from node1 to a total of 172500
   139  	// node2 gets the rest of the 750000 => 600000
   140  	// ersatz validators
   141  	// node3 has normalised score of 0.6 => 0.6 * 62500 = 37500
   142  	// node4 has normalised score of 0.4 => 0.4 * 62500 = 25000
   143  
   144  	require.Equal(t, 4, len(primary.partyToAmount))
   145  
   146  	require.Equal(t, num.NewUint(172500), primary.partyToAmount["party1"])
   147  	require.Equal(t, num.NewUint(15000), primary.partyToAmount["party2"])
   148  	require.Equal(t, num.NewUint(150000), primary.partyToAmount["node1"])
   149  	require.Equal(t, num.NewUint(600000), primary.partyToAmount["node2"])
   150  	require.Equal(t, num.NewUint(37500), ersatz.partyToAmount["node3"])
   151  	require.Equal(t, num.NewUint(25000), ersatz.partyToAmount["node4"])
   152  	require.Equal(t, epoch.EndTime.UnixNano(), primary.timestamp)
   153  	require.Equal(t, epoch.EndTime.UnixNano(), ersatz.timestamp)
   154  	require.Equal(t, num.NewUint(937500), primary.totalReward)
   155  	require.Equal(t, num.NewUint(62500), ersatz.totalReward)
   156  }
   157  
   158  func testCalculateRewardsWithMaxPerParticipant(t *testing.T) {
   159  	testEngine := getEngine(t)
   160  	now := time.Now()
   161  	testEngine.timeService.EXPECT().GetTimeNow().DoAndReturn(
   162  		func() time.Time {
   163  			return now
   164  		}).AnyTimes()
   165  
   166  	engine := testEngine.engine
   167  	require.NoError(t, engine.UpdateAssetForStakingAndDelegation(context.Background(), "VEGA"))
   168  	require.NoError(t, engine.UpdateDelegatorShareForStakingRewardScheme(context.Background(), num.DecimalFromFloat(0.3)))
   169  	require.NoError(t, engine.UpdateMinimumValidatorStakeForStakingRewardScheme(context.Background(), num.NewDecimalFromFloat(0)))
   170  	require.NoError(t, engine.UpdateCompetitionLevelForStakingRewardScheme(context.Background(), num.DecimalFromFloat(1.1)))
   171  	require.NoError(t, engine.UpdateMinValidatorsStakingRewardScheme(context.Background(), 5))
   172  	require.NoError(t, engine.UpdateOptimalStakeMultiplierStakingRewardScheme(context.Background(), num.DecimalFromFloat(5)))
   173  	require.NoError(t, engine.UpdateMaxPayoutPerParticipantForStakingRewardScheme(context.Background(), num.DecimalFromFloat(100000)))
   174  	require.NoError(t, engine.UpdateErsatzRewardFactor(context.Background(), num.DecimalFromFloat(0.5)))
   175  
   176  	epoch := types.Epoch{EndTime: now}
   177  	rewardAccount, err := testEngine.collateral.GetGlobalRewardAccount("VEGA")
   178  	require.NoError(t, err)
   179  	testEngine.delegation.EXPECT().ProcessEpochDelegations(gomock.Any(), gomock.Any()).Return(testEngine.validatorData)
   180  	testEngine.delegation.EXPECT().GetValidatorData().AnyTimes()
   181  	testEngine.topology.EXPECT().RecalcValidatorSet(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   182  	testEngine.topology.EXPECT().GetRewardsScores(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, epochSeq string, delegationState []*types.ValidatorData, stakeScoreParams types.StakeScoreParams) (*types.ScoreData, *types.ScoreData) {
   183  		return &types.ScoreData{
   184  				NodeIDSlice: []string{"node1", "node2"},
   185  				NormalisedScores: map[string]num.Decimal{
   186  					"node1": num.DecimalFromFloat(0.2),
   187  					"node2": num.DecimalFromFloat(0.8),
   188  				},
   189  			}, &types.ScoreData{
   190  				NodeIDSlice: []string{"node3", "node4"},
   191  				NormalisedScores: map[string]num.Decimal{
   192  					"node3": num.DecimalFromFloat(0.6),
   193  					"node4": num.DecimalFromFloat(0.4),
   194  				},
   195  			}
   196  	})
   197  
   198  	err = testEngine.collateral.IncrementBalance(context.Background(), rewardAccount.ID, num.NewUint(1000000))
   199  	require.Nil(t, err)
   200  
   201  	payouts := engine.calculateRewardPayouts(context.Background(), epoch)
   202  	primary := payouts[0]
   203  	ersatz := payouts[1]
   204  
   205  	// calculation
   206  	// party1 should get 172500 => 100000
   207  	// party2 should get 15000 => 15000
   208  	// node1 should get 150000 => 100000
   209  	// node2 should get 600000 => 100000
   210  	// node3 should get 37500
   211  	// node4 should get 25000
   212  
   213  	require.Equal(t, 4, len(primary.partyToAmount))
   214  
   215  	require.Equal(t, num.NewUint(100000), primary.partyToAmount["party1"])
   216  	require.Equal(t, num.NewUint(15000), primary.partyToAmount["party2"])
   217  	require.Equal(t, num.NewUint(100000), primary.partyToAmount["node1"])
   218  	require.Equal(t, num.NewUint(100000), primary.partyToAmount["node2"])
   219  	require.Equal(t, num.NewUint(37500), ersatz.partyToAmount["node3"])
   220  	require.Equal(t, num.NewUint(25000), ersatz.partyToAmount["node4"])
   221  	require.Equal(t, epoch.EndTime.UnixNano(), primary.timestamp)
   222  	require.Equal(t, epoch.EndTime.UnixNano(), ersatz.timestamp)
   223  	require.Equal(t, num.NewUint(315000), primary.totalReward)
   224  	require.Equal(t, num.NewUint(62500), ersatz.totalReward)
   225  }
   226  
   227  // test payout distribution.
   228  func testDistributePayout(t *testing.T) {
   229  	testEngine := getEngine(t)
   230  	now := time.Now()
   231  	testEngine.timeService.EXPECT().GetTimeNow().DoAndReturn(
   232  		func() time.Time {
   233  			return now
   234  		}).AnyTimes()
   235  
   236  	engine := testEngine.engine
   237  	require.NoError(t, engine.UpdateAssetForStakingAndDelegation(context.Background(), "VEGA"))
   238  	require.NoError(t, engine.UpdateMinimumValidatorStakeForStakingRewardScheme(context.Background(), num.NewDecimalFromFloat(0)))
   239  	require.NoError(t, engine.UpdateMinValidatorsStakingRewardScheme(context.Background(), 5))
   240  	require.NoError(t, engine.UpdateOptimalStakeMultiplierStakingRewardScheme(context.Background(), num.DecimalFromFloat(5)))
   241  	require.NoError(t, engine.UpdateErsatzRewardFactor(context.Background(), num.DecimalFromFloat(0.5)))
   242  
   243  	// setup balance of reward account
   244  	rewardAccount, err := testEngine.collateral.GetGlobalRewardAccount("VEGA")
   245  	require.NoError(t, err)
   246  	err = testEngine.collateral.IncrementBalance(context.Background(), rewardAccount.ID, num.NewUint(1000000))
   247  	require.Nil(t, err)
   248  	partyToAmount := map[string]*num.Uint{}
   249  	partyToAmount["party1"] = num.NewUint(5000)
   250  
   251  	payout := &payout{
   252  		fromAccount:   rewardAccount.ID,
   253  		totalReward:   num.NewUint(5000),
   254  		partyToAmount: partyToAmount,
   255  		asset:         "VEGA",
   256  	}
   257  
   258  	// testEngine.broker.EXPECT().SendBatch(gomock.Any()).Times(1)
   259  	engine.distributePayout(context.Background(), payout)
   260  
   261  	rewardAccount, _ = engine.collateral.GetAccountByID(rewardAccount.ID)
   262  	partyAccount := testEngine.collateral.GetOrCreatePartyVestingRewardAccount(context.Background(), "party1", "VEGA")
   263  	require.Nil(t, err)
   264  
   265  	require.Equal(t, num.NewUint(5000), partyAccount.Balance)
   266  	require.Equal(t, num.NewUint(995000), rewardAccount.Balance)
   267  }
   268  
   269  // test payout distribution on epoch end with no delay.
   270  func testOnEpochEventNoPayoutDelay(t *testing.T) {
   271  	testEngine := getEngine(t)
   272  	now := time.Now()
   273  	testEngine.timeService.EXPECT().GetTimeNow().DoAndReturn(
   274  		func() time.Time {
   275  			return now
   276  		}).AnyTimes()
   277  
   278  	engine := testEngine.engine
   279  	require.NoError(t, engine.UpdateAssetForStakingAndDelegation(context.Background(), "VEGA"))
   280  	require.NoError(t, engine.UpdateDelegatorShareForStakingRewardScheme(context.Background(), num.DecimalFromFloat(0.3)))
   281  	require.NoError(t, engine.UpdateMinimumValidatorStakeForStakingRewardScheme(context.Background(), num.NewDecimalFromFloat(0)))
   282  	require.NoError(t, engine.UpdateCompetitionLevelForStakingRewardScheme(context.Background(), num.DecimalFromFloat(1.1)))
   283  	require.NoError(t, engine.UpdateMinValidatorsStakingRewardScheme(context.Background(), 5))
   284  	require.NoError(t, engine.UpdateOptimalStakeMultiplierStakingRewardScheme(context.Background(), num.DecimalFromFloat(5)))
   285  	require.NoError(t, engine.UpdateMaxPayoutPerParticipantForStakingRewardScheme(context.Background(), num.DecimalZero()))
   286  	require.NoError(t, engine.UpdateErsatzRewardFactor(context.Background(), num.DecimalFromFloat(0.5)))
   287  
   288  	testEngine.delegation.EXPECT().GetValidatorData().AnyTimes()
   289  	testEngine.topology.EXPECT().RecalcValidatorSet(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   290  	testEngine.topology.EXPECT().GetRewardsScores(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, epochSeq string, delegationState []*types.ValidatorData, stakeScoreParams types.StakeScoreParams) (*types.ScoreData, *types.ScoreData) {
   291  		return &types.ScoreData{
   292  				NodeIDSlice: []string{"node1", "node2"},
   293  				NormalisedScores: map[string]num.Decimal{
   294  					"node1": num.DecimalFromFloat(0.2),
   295  					"node2": num.DecimalFromFloat(0.8),
   296  				},
   297  			}, &types.ScoreData{
   298  				NodeIDSlice: []string{"node3", "node4"},
   299  				NormalisedScores: map[string]num.Decimal{
   300  					"node3": num.DecimalFromFloat(0.6),
   301  					"node4": num.DecimalFromFloat(0.4),
   302  				},
   303  			}
   304  	}).AnyTimes()
   305  
   306  	// setup reward account balance
   307  	rewardAccount, err := testEngine.collateral.GetGlobalRewardAccount("VEGA")
   308  	require.NoError(t, err)
   309  	err = testEngine.collateral.IncrementBalance(context.Background(), rewardAccount.ID, num.NewUint(1000000))
   310  	require.Nil(t, err)
   311  
   312  	// there is remaining 1000000 to distribute as payout
   313  	epoch := types.Epoch{StartTime: now, EndTime: now}
   314  
   315  	testEngine.delegation.EXPECT().ProcessEpochDelegations(gomock.Any(), gomock.Any()).Return(testEngine.validatorData)
   316  	engine.OnEpochEvent(context.Background(), epoch)
   317  
   318  	// get party account balances
   319  	ctx := context.Background()
   320  	party1Acc := testEngine.collateral.GetOrCreatePartyVestingRewardAccount(ctx, "party1", "VEGA")
   321  	party2Acc := testEngine.collateral.GetOrCreatePartyVestingRewardAccount(ctx, "party2", "VEGA")
   322  	node1Acc := testEngine.collateral.GetOrCreatePartyVestingRewardAccount(ctx, "node1", "VEGA")
   323  	node2Acc := testEngine.collateral.GetOrCreatePartyVestingRewardAccount(ctx, "node2", "VEGA")
   324  	node3Acc := testEngine.collateral.GetOrCreatePartyVestingRewardAccount(ctx, "node3", "VEGA")
   325  	node4Acc := testEngine.collateral.GetOrCreatePartyVestingRewardAccount(ctx, "node4", "VEGA")
   326  
   327  	require.Equal(t, num.NewUint(172500), party1Acc.Balance)
   328  	require.Equal(t, num.NewUint(15000), party2Acc.Balance)
   329  	require.Equal(t, num.NewUint(150000), node1Acc.Balance)
   330  	require.Equal(t, num.NewUint(600000), node2Acc.Balance)
   331  	require.Equal(t, num.NewUint(37500), node3Acc.Balance)
   332  	require.Equal(t, num.NewUint(25000), node4Acc.Balance)
   333  }
   334  
   335  func TestErsatzTendermintRewardSplit(t *testing.T) {
   336  	testEngine := getEngine(t)
   337  	now := time.Now()
   338  	testEngine.timeService.EXPECT().GetTimeNow().DoAndReturn(
   339  		func() time.Time {
   340  			return now
   341  		}).AnyTimes()
   342  
   343  	engine := testEngine.engine
   344  	require.NoError(t, engine.UpdateAssetForStakingAndDelegation(context.Background(), "VEGA"))
   345  	require.NoError(t, engine.UpdateDelegatorShareForStakingRewardScheme(context.Background(), num.DecimalFromFloat(0.3)))
   346  	require.NoError(t, engine.UpdateMinimumValidatorStakeForStakingRewardScheme(context.Background(), num.NewDecimalFromFloat(0)))
   347  	require.NoError(t, engine.UpdateCompetitionLevelForStakingRewardScheme(context.Background(), num.DecimalFromFloat(1.1)))
   348  	require.NoError(t, engine.UpdateMinValidatorsStakingRewardScheme(context.Background(), 5))
   349  	require.NoError(t, engine.UpdateOptimalStakeMultiplierStakingRewardScheme(context.Background(), num.DecimalFromFloat(5)))
   350  	require.NoError(t, engine.UpdateMaxPayoutPerParticipantForStakingRewardScheme(context.Background(), num.DecimalZero()))
   351  	require.NoError(t, engine.UpdateErsatzRewardFactor(context.Background(), num.DecimalFromFloat(0.5)))
   352  
   353  	testEngine.delegation.EXPECT().GetValidatorData().AnyTimes()
   354  	testEngine.topology.EXPECT().RecalcValidatorSet(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   355  	testEngine.topology.EXPECT().GetRewardsScores(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, epochSeq string, delegationState []*types.ValidatorData, stakeScoreParams types.StakeScoreParams) (*types.ScoreData, *types.ScoreData) {
   356  		return &types.ScoreData{
   357  				NodeIDSlice: []string{"node1", "node2"},
   358  				NormalisedScores: map[string]num.Decimal{
   359  					"node1": num.DecimalFromFloat(0.2),
   360  					"node2": num.DecimalFromFloat(0.8),
   361  				},
   362  			}, &types.ScoreData{
   363  				NodeIDSlice: []string{"node3", "node4"},
   364  				NormalisedScores: map[string]num.Decimal{
   365  					"node3": num.DecimalFromFloat(0.6),
   366  					"node4": num.DecimalFromFloat(0.4),
   367  				},
   368  			}
   369  	}).AnyTimes()
   370  
   371  	// setup reward account balance
   372  	rewardAccount := testEngine.collateral.GetInfraFeeAccountIDs()[0]
   373  	err := testEngine.collateral.IncrementBalance(context.Background(), rewardAccount, num.NewUint(1000000))
   374  	require.Nil(t, err)
   375  
   376  	// there is remaining 1000000 to distribute as payout
   377  	epoch := types.Epoch{StartTime: now, EndTime: now}
   378  
   379  	testEngine.delegation.EXPECT().ProcessEpochDelegations(gomock.Any(), gomock.Any()).Return(testEngine.validatorData)
   380  	engine.OnEpochEvent(context.Background(), epoch)
   381  
   382  	// given the delegation breakdown we expect
   383  	// tendermint validators to get 0.9375 x 1000000 => 937500
   384  	// ersatzh validators to get => 0.0625 x 1000000 => 62500
   385  	// in the tendermint validators node1 gets 0.2 x 937500 => 187500 and node2 gets 0.8 x 937500 => 750000
   386  	// in the tendermint validators node3 gets 0.6 x 62500 => 37500 and node4 gets 0.4 x 62500 => 25000
   387  	// from tendermint validators reward balance:
   388  	// party1 gets 172500
   389  	// party2 gets 15000
   390  	// node1 gets 150000
   391  	// node2 gets 600000
   392  	// from ersatz validators reward balance:
   393  	// node3 gets 37500
   394  	// node 4 gets 25000
   395  
   396  	// get party account balances
   397  	party1Acc, _ := testEngine.collateral.GetPartyGeneralAccount("party1", "VEGA")
   398  	party2Acc, _ := testEngine.collateral.GetPartyGeneralAccount("party2", "VEGA")
   399  	node1Acc, _ := testEngine.collateral.GetPartyGeneralAccount("node1", "VEGA")
   400  	node2Acc, _ := testEngine.collateral.GetPartyGeneralAccount("node2", "VEGA")
   401  	node3Acc, _ := testEngine.collateral.GetPartyGeneralAccount("node3", "VEGA")
   402  	node4Acc, _ := testEngine.collateral.GetPartyGeneralAccount("node4", "VEGA")
   403  
   404  	require.Equal(t, num.NewUint(172500), party1Acc.Balance)
   405  	require.Equal(t, num.NewUint(15000), party2Acc.Balance)
   406  	require.Equal(t, num.NewUint(150000), node1Acc.Balance)
   407  	require.Equal(t, num.NewUint(600000), node2Acc.Balance)
   408  	require.Equal(t, num.NewUint(37500), node3Acc.Balance)
   409  	require.Equal(t, num.NewUint(25000), node4Acc.Balance)
   410  }
   411  
   412  type testEngine struct {
   413  	engine        *Engine
   414  	ctrl          *gomock.Controller
   415  	timeService   *mocks.MockTimeService
   416  	broker        *bmocks.MockBroker
   417  	epochEngine   *TestEpochEngine
   418  	delegation    *mocks.MockDelegation
   419  	collateral    *collateral.Engine
   420  	validatorData []*types.ValidatorData
   421  	topology      *mocks.MockTopology
   422  }
   423  
   424  func getEngine(t *testing.T) *testEngine {
   425  	t.Helper()
   426  	conf := NewDefaultConfig()
   427  	ctrl := gomock.NewController(t)
   428  	broker := bmocks.NewMockBroker(ctrl)
   429  	logger := logging.NewTestLogger()
   430  	delegation := mocks.NewMockDelegation(ctrl)
   431  	epochEngine := &TestEpochEngine{
   432  		callbacks: []func(context.Context, types.Epoch){},
   433  		restore:   []func(context.Context, types.Epoch){},
   434  	}
   435  	ts := mocks.NewMockTimeService(ctrl)
   436  
   437  	broker.EXPECT().Send(gomock.Any()).AnyTimes()
   438  	broker.EXPECT().SendBatch(gomock.Any()).AnyTimes()
   439  
   440  	collateralEng := collateral.New(logger, collateral.NewDefaultConfig(), ts, broker)
   441  	asset := types.Asset{
   442  		ID: "VEGA",
   443  		Details: &types.AssetDetails{
   444  			Symbol:  "VEGA",
   445  			Quantum: num.DecimalFromFloat(1),
   446  		},
   447  	}
   448  
   449  	require.NoError(t, collateralEng.EnableAsset(context.Background(), asset))
   450  	topology := mocks.NewMockTopology(ctrl)
   451  	marketActivityTracker := mocks.NewMockMarketActivityTracker(ctrl)
   452  	vesting := mocks.NewMockVesting(ctrl)
   453  	vesting.EXPECT().AddReward(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   454  	transfers := mocks.NewMockTransfers(ctrl)
   455  	activityStreak := mocks.NewMockActivityStreak(ctrl)
   456  	engine := New(logger, conf, broker, delegation, epochEngine, collateralEng, ts, marketActivityTracker, topology, vesting, transfers, activityStreak)
   457  
   458  	broker.EXPECT().Send(gomock.Any()).AnyTimes()
   459  
   460  	delegatorForVal1 := map[string]*num.Uint{}
   461  	delegatorForVal1["party1"] = num.NewUint(6000)
   462  	delegatorForVal1["party2"] = num.NewUint(4000)
   463  	validator1 := &types.ValidatorData{
   464  		NodeID:            "node1",
   465  		PubKey:            "node1",
   466  		SelfStake:         num.NewUint(5000),
   467  		StakeByDelegators: num.NewUint(10000),
   468  		Delegators:        delegatorForVal1,
   469  	}
   470  	delegatorForVal2 := map[string]*num.Uint{}
   471  	delegatorForVal2["party1"] = num.NewUint(40000)
   472  	validator2 := &types.ValidatorData{
   473  		NodeID:            "node2",
   474  		PubKey:            "node2",
   475  		SelfStake:         num.NewUint(20000),
   476  		StakeByDelegators: num.NewUint(40000),
   477  		Delegators:        delegatorForVal2,
   478  	}
   479  
   480  	validator3 := &types.ValidatorData{
   481  		NodeID:            "node3",
   482  		PubKey:            "node3",
   483  		SelfStake:         num.NewUint(4000),
   484  		StakeByDelegators: num.UintZero(),
   485  		Delegators:        map[string]*num.Uint{},
   486  	}
   487  
   488  	validator4 := &types.ValidatorData{
   489  		NodeID:            "node4",
   490  		PubKey:            "node4",
   491  		SelfStake:         num.NewUint(6000),
   492  		StakeByDelegators: num.UintZero(),
   493  		Delegators:        map[string]*num.Uint{},
   494  	}
   495  
   496  	validatorData := []*types.ValidatorData{validator1, validator2, validator3, validator4}
   497  
   498  	return &testEngine{
   499  		engine:        engine,
   500  		ctrl:          ctrl,
   501  		timeService:   ts,
   502  		broker:        broker,
   503  		epochEngine:   epochEngine,
   504  		delegation:    delegation,
   505  		collateral:    collateralEng,
   506  		validatorData: validatorData,
   507  		topology:      topology,
   508  	}
   509  }
   510  
   511  type TestEpochEngine struct {
   512  	callbacks []func(context.Context, types.Epoch)
   513  	restore   []func(context.Context, types.Epoch)
   514  }
   515  
   516  func (e *TestEpochEngine) NotifyOnEpoch(f func(context.Context, types.Epoch), r func(context.Context, types.Epoch)) {
   517  	e.callbacks = append(e.callbacks, f)
   518  	e.restore = append(e.callbacks, r)
   519  }
   520  
   521  func (e *TestEpochEngine) GetTimeNow() time.Time {
   522  	return time.Now()
   523  }