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 }