code.vegaprotocol.io/vega@v0.79.0/core/referral/snapshot_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 referral_test
    17  
    18  import (
    19  	"bytes"
    20  	"testing"
    21  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/core/types"
    24  	"code.vegaprotocol.io/vega/libs/num"
    25  	vgtest "code.vegaprotocol.io/vega/libs/test"
    26  	"code.vegaprotocol.io/vega/paths"
    27  
    28  	"github.com/golang/mock/gomock"
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  func TestTakingAndRestoringSnapshotSucceeds(t *testing.T) {
    34  	ctx := vgtest.VegaContext("chainid", 100)
    35  
    36  	vegaPath := paths.New(t.TempDir())
    37  	now := time.Now()
    38  	maxVolumeParams := num.UintFromUint64(100)
    39  
    40  	te1 := newEngine(t)
    41  	snapshotEngine1 := newSnapshotEngine(t, vegaPath, now, te1.engine)
    42  	closeSnapshotEngine1 := vgtest.OnlyOnce(snapshotEngine1.Close)
    43  	defer closeSnapshotEngine1()
    44  
    45  	require.NoError(t, snapshotEngine1.Start(ctx))
    46  
    47  	// Cap the notional volume.
    48  	require.NoError(t, te1.engine.OnReferralProgramMaxPartyNotionalVolumeByQuantumPerEpochUpdate(ctx, maxVolumeParams))
    49  	require.NoError(t, te1.engine.OnReferralProgramMinStakedVegaTokensUpdate(ctx, num.NewUint(100)))
    50  
    51  	referrer1 := newPartyID(t)
    52  	referrer2 := newPartyID(t)
    53  	referrer3 := newPartyID(t)
    54  	referrer4 := newPartyID(t)
    55  	referee1 := newPartyID(t)
    56  	referee2 := newPartyID(t)
    57  	referee3 := newPartyID(t)
    58  	referee4 := newPartyID(t)
    59  	referee5 := newPartyID(t)
    60  	referee6 := newPartyID(t)
    61  	referee7 := newPartyID(t)
    62  	referee8 := newPartyID(t)
    63  	referee9 := newPartyID(t)
    64  
    65  	te1.broker.EXPECT().Send(gomock.Any()).Times(13)
    66  	te1.timeSvc.EXPECT().GetTimeNow().Return(now).Times(13)
    67  	te1.staking.EXPECT().GetAvailableBalance(gomock.Any()).AnyTimes().Return(num.NewUint(100), nil)
    68  
    69  	assert.NoError(t, te1.engine.CreateReferralSet(ctx, referrer1, "id1"))
    70  	assert.NoError(t, te1.engine.CreateReferralSet(ctx, referrer2, "id2"))
    71  	assert.NoError(t, te1.engine.CreateReferralSet(ctx, referrer3, "id3"))
    72  	assert.NoError(t, te1.engine.CreateReferralSet(ctx, referrer4, "id4"))
    73  	assert.NoError(t, te1.engine.ApplyReferralCode(ctx, referee1, "id1"))
    74  	assert.NoError(t, te1.engine.ApplyReferralCode(ctx, referee2, "id4"))
    75  	assert.NoError(t, te1.engine.ApplyReferralCode(ctx, referee3, "id3"))
    76  	assert.NoError(t, te1.engine.ApplyReferralCode(ctx, referee4, "id2"))
    77  	assert.NoError(t, te1.engine.ApplyReferralCode(ctx, referee5, "id2"))
    78  	assert.NoError(t, te1.engine.ApplyReferralCode(ctx, referee6, "id2"))
    79  	assert.NoError(t, te1.engine.ApplyReferralCode(ctx, referee7, "id1"))
    80  	assert.NoError(t, te1.engine.ApplyReferralCode(ctx, referee8, "id4"))
    81  	assert.NoError(t, te1.engine.ApplyReferralCode(ctx, referee9, "id3"))
    82  
    83  	program1 := &types.ReferralProgram{
    84  		EndOfProgramTimestamp: now.Add(24 * time.Hour),
    85  		WindowLength:          10,
    86  		BenefitTiers:          []*types.BenefitTier{},
    87  		StakingTiers:          []*types.StakingTier{},
    88  	}
    89  
    90  	te1.engine.UpdateProgram(program1)
    91  
    92  	// Simulating end of epoch.
    93  	// The program should be applied.
    94  	epochEndVals := map[string]*num.Uint{
    95  		string(referrer1): num.UintFromUint64(10),
    96  		string(referrer2): num.UintFromUint64(20),
    97  		string(referrer3): num.UintFromUint64(30),
    98  		string(referrer4): num.UintFromUint64(40),
    99  		string(referee1):  num.UintFromUint64(50),
   100  		string(referee2):  num.UintFromUint64(60),
   101  		string(referee3):  num.UintFromUint64(70),
   102  		string(referee4):  num.UintFromUint64(80),
   103  		string(referee5):  num.UintFromUint64(90),
   104  		string(referee6):  num.UintFromUint64(100),
   105  		string(referee7):  num.UintFromUint64(110),
   106  		string(referee8):  num.UintFromUint64(120),
   107  		string(referee9):  num.UintFromUint64(130),
   108  	}
   109  	te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(gomock.Any()).DoAndReturn(func(k string) *num.Uint {
   110  		v, ok := epochEndVals[k]
   111  		if !ok {
   112  			return num.UintZero()
   113  		}
   114  		return v
   115  	}).Times(len(epochEndVals) * 3) // once for creation, once for new epoch, once for update
   116  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referrer1)).Return(num.UintFromUint64(10)).Times(1)
   117  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referrer2)).Return(num.UintFromUint64(20)).Times(1)
   118  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referrer3)).Return(num.UintFromUint64(30)).Times(1)
   119  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referrer4)).Return(num.UintFromUint64(40)).Times(1)
   120  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee1)).Return(num.UintFromUint64(50)).Times(1)
   121  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee2)).Return(num.UintFromUint64(60)).Times(1)
   122  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee3)).Return(num.UintFromUint64(70)).Times(1)
   123  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee4)).Return(num.UintFromUint64(80)).Times(1)
   124  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee5)).Return(num.UintFromUint64(90)).Times(1)
   125  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee6)).Return(num.UintFromUint64(100)).Times(1)
   126  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee7)).Return(num.UintFromUint64(110)).Times(1)
   127  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee8)).Return(num.UintFromUint64(120)).Times(1)
   128  	// te1.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee9)).Return(num.UintFromUint64(130)).Times(1)
   129  
   130  	expectReferralProgramStartedEvent(t, te1)
   131  	lastEpochStartTime := program1.EndOfProgramTimestamp.Add(-2 * time.Hour)
   132  	nextEpoch(t, ctx, te1, lastEpochStartTime)
   133  
   134  	program2 := &types.ReferralProgram{
   135  		EndOfProgramTimestamp: lastEpochStartTime.Add(10 * time.Hour),
   136  		WindowLength:          10,
   137  		BenefitTiers:          []*types.BenefitTier{},
   138  	}
   139  
   140  	// Set new program.
   141  	te1.engine.UpdateProgram(program2)
   142  
   143  	// Simulating end of epoch.
   144  	// The program should be updated with the new one.
   145  	postSnapshotActions := func(te *testEngine) {
   146  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referrer1)).Return(num.UintFromUint64(100)).Times(1)
   147  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referrer2)).Return(num.UintFromUint64(100)).Times(1)
   148  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referrer3)).Return(num.UintFromUint64(100)).Times(1)
   149  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referrer4)).Return(num.UintFromUint64(100)).Times(1)
   150  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee1)).Return(num.UintFromUint64(100)).Times(1)
   151  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee2)).Return(num.UintFromUint64(100)).Times(1)
   152  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee3)).Return(num.UintFromUint64(100)).Times(1)
   153  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee4)).Return(num.UintFromUint64(100)).Times(1)
   154  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee5)).Return(num.UintFromUint64(100)).Times(1)
   155  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee6)).Return(num.UintFromUint64(100)).Times(1)
   156  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee7)).Return(num.UintFromUint64(100)).Times(1)
   157  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee8)).Return(num.UintFromUint64(100)).Times(1)
   158  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee9)).Return(num.UintFromUint64(100)).Times(1)
   159  		te.staking.EXPECT().GetAvailableBalance(gomock.Any()).AnyTimes().Return(num.NewUint(100), nil)
   160  
   161  		gomock.InOrder(
   162  			expectReferralSetStatsUpdatedEvent(t, te, 4),
   163  			expectReferralProgramUpdatedEvent(t, te),
   164  		)
   165  		lastEpochStartTime = program2.EndOfProgramTimestamp.Add(-2 * time.Hour)
   166  		nextEpoch(t, ctx, te, lastEpochStartTime)
   167  
   168  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referrer1)).Return(num.UintFromUint64(200)).Times(1)
   169  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referrer2)).Return(num.UintFromUint64(200)).Times(1)
   170  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referrer3)).Return(num.UintFromUint64(200)).Times(1)
   171  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referrer4)).Return(num.UintFromUint64(200)).Times(1)
   172  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee1)).Return(num.UintFromUint64(200)).Times(1)
   173  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee2)).Return(num.UintFromUint64(200)).Times(1)
   174  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee3)).Return(num.UintFromUint64(200)).Times(1)
   175  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee4)).Return(num.UintFromUint64(200)).Times(1)
   176  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee5)).Return(num.UintFromUint64(200)).Times(1)
   177  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee6)).Return(num.UintFromUint64(200)).Times(1)
   178  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee7)).Return(num.UintFromUint64(200)).Times(1)
   179  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee8)).Return(num.UintFromUint64(200)).Times(1)
   180  		te.marketActivityTracker.EXPECT().NotionalTakerVolumeForParty(string(referee9)).Return(num.UintFromUint64(200)).Times(1)
   181  
   182  		expectReferralSetStatsUpdatedEvent(t, te, 4)
   183  		lastEpochStartTime = program2.EndOfProgramTimestamp.Add(-1 * time.Hour)
   184  		nextEpoch(t, ctx, te, lastEpochStartTime)
   185  	}
   186  	postSnapshotActions(te1)
   187  
   188  	// Take a snapshot.
   189  	hash1, err := snapshotEngine1.SnapshotNow(ctx)
   190  	require.NoError(t, err)
   191  
   192  	closeSnapshotEngine1()
   193  
   194  	// Reload the engine using the previous snapshot.
   195  	te2 := newEngine(t)
   196  	snapshotEngine2 := newSnapshotEngine(t, vegaPath, now, te2.engine)
   197  	defer snapshotEngine2.Close()
   198  
   199  	// Simulate restoration of the network parameter at the time of the snapshot
   200  	require.NoError(t, te2.engine.OnReferralProgramMaxPartyNotionalVolumeByQuantumPerEpochUpdate(ctx, maxVolumeParams))
   201  
   202  	// This triggers the state restoration from the local snapshot.
   203  	require.NoError(t, snapshotEngine2.Start(ctx))
   204  
   205  	// Comparing the hash after restoration, to ensure it produces the same result.
   206  	hash2, _, _ := snapshotEngine2.Info()
   207  	require.True(t, bytes.Equal(hash1, hash2))
   208  }