code.vegaprotocol.io/vega@v0.79.0/core/liquidity/v2/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 liquidity_test 17 18 import ( 19 "context" 20 "testing" 21 "time" 22 23 "code.vegaprotocol.io/vega/core/idgeneration" 24 "code.vegaprotocol.io/vega/core/types" 25 "code.vegaprotocol.io/vega/libs/crypto" 26 "code.vegaprotocol.io/vega/libs/num" 27 "code.vegaprotocol.io/vega/libs/proto" 28 snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1" 29 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 ) 34 35 func TestEngineSnapshotV2(t *testing.T) { 36 originalEngine := newTestEngine(t) 37 38 ctx := context.Background() 39 idgen := idgeneration.New(crypto.RandomHash()) 40 41 party1 := "party-1" 42 commitment1 := 1000000 43 party1Orders := []*types.Order{ 44 {Side: types.SideBuy, Price: num.NewUint(98), Size: 5103}, 45 {Side: types.SideBuy, Price: num.NewUint(93), Size: 5377}, 46 {Side: types.SideSell, Price: num.NewUint(102), Size: 4902}, 47 {Side: types.SideSell, Price: num.NewUint(107), Size: 4673}, 48 } 49 50 party2 := "party-2" 51 commitment2 := 3000000 52 party2Orders := []*types.Order{ 53 {Side: types.SideBuy, Price: num.NewUint(98), Size: 15307}, 54 {Side: types.SideBuy, Price: num.NewUint(93), Size: 16130}, 55 {Side: types.SideSell, Price: num.NewUint(102), Size: 14706}, 56 {Side: types.SideSell, Price: num.NewUint(107), Size: 14019}, 57 } 58 59 party3 := "party-3" 60 commitment3 := 2000000 61 party3Provision := &types.LiquidityProvisionSubmission{ 62 MarketID: originalEngine.marketID, 63 CommitmentAmount: num.NewUint(uint64(commitment3)), 64 Fee: num.DecimalFromFloat(0.5), 65 } 66 67 // change the SLA parameters values that were not set in initialisation 68 slaParams := &types.LiquiditySLAParams{ 69 PriceRange: num.DecimalFromFloat(0.9), // priceRange 70 CommitmentMinTimeFraction: num.DecimalFromFloat(0.9), // commitmentMinTimeFraction 71 SlaCompetitionFactor: num.DecimalFromFloat(0.9), // slaCompetitionFactor, 72 PerformanceHysteresisEpochs: 7, // performanceHysteresisEpochs 73 } 74 originalEngine.engine.UpdateSLAParameters(slaParams) 75 originalEngine.engine.OnNonPerformanceBondPenaltyMaxUpdate(num.DecimalFromFloat(0.9)) 76 originalEngine.engine.OnNonPerformanceBondPenaltySlopeUpdate(num.DecimalFromFloat(0.8)) 77 originalEngine.engine.OnStakeToCcyVolumeUpdate(num.DecimalFromFloat(0.7)) 78 79 // Adding some state. 80 originalEngine.broker.EXPECT().Send(gomock.Any()).AnyTimes() 81 originalEngine.auctionState.EXPECT().IsOpeningAuction().Return(false).AnyTimes() 82 originalEngine.auctionState.EXPECT().InAuction().Return(false).AnyTimes() 83 84 // Adding provisions. 85 // This helper method flush the initially pending provisions as "on-going" 86 // provisions. 87 originalEngine.submitLiquidityProvisionAndCreateOrders(t, ctx, party1, commitment1, idgen, party1Orders) 88 originalEngine.submitLiquidityProvisionAndCreateOrders(t, ctx, party2, commitment2, idgen, party2Orders) 89 // Adding pending provisions. 90 // When not calling `ApplyPendingProvisions()`, the submitted provision is 91 // automatically pending if market is not in auction. 92 provisioned, err := originalEngine.engine.SubmitLiquidityProvision(ctx, party3Provision, party3, idgen) 93 require.NoError(t, err) 94 require.False(t, provisioned, "this will help testing the pending provisions, so it should not directly be added as provision") 95 96 originalEngine.engine.RegisterAllocatedFeesPerParty(map[string]*num.Uint{ 97 "party-1": num.UintFromUint64(1), 98 "party-2": num.UintFromUint64(2), 99 "party-3": num.UintFromUint64(2), 100 "party-4": num.UintFromUint64(3), 101 }) 102 103 preStats := originalEngine.engine.LiquidityProviderSLAStats(time.Now()) 104 require.Len(t, preStats, 2) 105 106 // Verifying we can salvage the state for each key, and they are a valid 107 // Payload. 108 engine1Keys := originalEngine.engine.V2StateProvider().Keys() 109 stateResults1 := map[string]stateResult{} 110 for _, key := range engine1Keys { 111 // Salvage the state. 112 state, additionalProviders, err := originalEngine.engine.V2StateProvider().GetState(key) 113 require.NoError(t, err) 114 assert.Nil(t, additionalProviders, "No additional provider should be generated by this engine") 115 require.NotNil(t, state) 116 require.NotEmpty(t, state) 117 118 // Deserialize the state to Payload. 119 var p snapshotpb.Payload 120 require.NoError(t, proto.Unmarshal(state, &p)) 121 122 stateResults1[key] = stateResult{ 123 state: state, 124 payload: types.PayloadFromProto(&p), 125 } 126 } 127 128 // Another engine to test determinism and consistency. 129 otherEngine := newTestEngine(t) 130 otherEngine.broker.EXPECT().Send(gomock.Any()).AnyTimes() 131 otherEngine.broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 132 otherEngine.auctionState.EXPECT().IsOpeningAuction().Return(false).AnyTimes() 133 otherEngine.auctionState.EXPECT().InAuction().Return(false).AnyTimes() 134 135 // Just to verify the keys are deterministic. 136 require.Equal(t, engine1Keys, otherEngine.engine.V2StateProvider().Keys()) 137 138 // Reloading previous payload in another engine. 139 for _, key := range engine1Keys { 140 additionalProviders, err := otherEngine.engine.V2StateProvider().LoadState(ctx, stateResults1[key].payload) 141 require.NoError(t, err) 142 require.Nil(t, additionalProviders, "No additional provider should be generated by this engine") 143 } 144 145 for _, key1 := range engine1Keys { 146 // Salvage the state from other engine to test determinism. 147 state2, additionalProviders, err := otherEngine.engine.V2StateProvider().GetState(key1) 148 require.NoError(t, err) 149 require.Nil(t, additionalProviders, "No additional provider should be generated by this engine") 150 require.NotNil(t, state2) 151 require.NotEmpty(t, state2) 152 153 var p snapshotpb.Payload 154 require.NoError(t, proto.Unmarshal(state2, &p)) 155 require.Equalf(t, stateResults1[key1].state, state2, "State for key %q between two engines must match", key1) 156 } 157 158 // Check that the restored state is complete, and lead to the same results. 159 now := time.Now() 160 161 // Check for penalties 162 penalties1 := originalEngine.engine.CalculateSLAPenalties(now) 163 penalties2 := otherEngine.engine.CalculateSLAPenalties(now) 164 assert.Equal(t, penalties1.AllPartiesHaveFullFeePenalty, penalties2.AllPartiesHaveFullFeePenalty) 165 assert.Equal(t, len(penalties1.PenaltiesPerParty), len(penalties2.PenaltiesPerParty)) 166 167 for k, p1 := range penalties1.PenaltiesPerParty { 168 p2, ok := penalties2.PenaltiesPerParty[k] 169 assert.True(t, ok) 170 assert.Equal(t, p1.Bond.String(), p2.Bond.String()) 171 assert.Equal(t, p1.Fee.String(), p2.Fee.String()) 172 } 173 174 assert.Equal(t, 175 originalEngine.engine.CalculateSuppliedStake(), 176 otherEngine.engine.CalculateSuppliedStake(), 177 ) 178 179 // Check for fees stats 180 feesStats1 := originalEngine.engine.PaidLiquidityFeesStats() 181 feesStats2 := otherEngine.engine.PaidLiquidityFeesStats() 182 assert.Equal(t, 183 feesStats1.TotalFeesPaid.String(), 184 feesStats2.TotalFeesPaid.String(), 185 ) 186 187 for k, s1 := range feesStats1.FeesPaidPerParty { 188 s2, ok := feesStats2.FeesPaidPerParty[k] 189 assert.True(t, ok) 190 assert.Equal(t, s1.String(), s2.String()) 191 } 192 193 postStats := otherEngine.engine.LiquidityProviderSLAStats(time.Now()) 194 require.Len(t, postStats, 2) 195 for i := range preStats { 196 assert.Equal(t, preStats[i].NotionalVolumeBuys, postStats[i].NotionalVolumeBuys) 197 assert.Equal(t, preStats[i].NotionalVolumeSells, postStats[i].NotionalVolumeSells) 198 assert.Equal(t, preStats[i].RequiredLiquidity, postStats[i].RequiredLiquidity) 199 } 200 } 201 202 func TestStopSnapshotTaking(t *testing.T) { 203 te := newTestEngine(t) 204 keys := te.engine.V2StateProvider().Keys() 205 206 // signal to kill the engine's snapshots 207 te.engine.StopSnapshots() 208 209 s, _, err := te.engine.V2StateProvider().GetState(keys[0]) 210 assert.NoError(t, err) 211 assert.Nil(t, s) 212 assert.True(t, te.engine.V2StateProvider().Stopped()) 213 } 214 215 type stateResult struct { 216 state []byte 217 payload *types.Payload 218 }