code.vegaprotocol.io/vega@v0.79.0/core/products/perpetual_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 products_test 17 18 import ( 19 "context" 20 "testing" 21 "time" 22 23 "code.vegaprotocol.io/vega/core/datasource" 24 dstypes "code.vegaprotocol.io/vega/core/datasource/common" 25 "code.vegaprotocol.io/vega/core/datasource/external/signedoracle" 26 "code.vegaprotocol.io/vega/core/datasource/spec" 27 "code.vegaprotocol.io/vega/core/products" 28 "code.vegaprotocol.io/vega/core/products/mocks" 29 "code.vegaprotocol.io/vega/core/types" 30 tmocks "code.vegaprotocol.io/vega/core/vegatime/mocks" 31 "code.vegaprotocol.io/vega/libs/num" 32 "code.vegaprotocol.io/vega/libs/ptr" 33 "code.vegaprotocol.io/vega/logging" 34 vegapb "code.vegaprotocol.io/vega/protos/vega" 35 datapb "code.vegaprotocol.io/vega/protos/vega/data/v1" 36 snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1" 37 38 "github.com/golang/mock/gomock" 39 "github.com/golang/protobuf/proto" 40 "github.com/stretchr/testify/assert" 41 "github.com/stretchr/testify/require" 42 ) 43 44 func TestPerpetualSnapshot(t *testing.T) { 45 perps := testPerpetual(t) 46 defer perps.ctrl.Finish() 47 expectedTWAP := 1234 48 // set of the data points such that difference in averages is 0 49 points := getTestDataPoints(t) 50 require.GreaterOrEqual(t, 4, len(points)) 51 52 // tell the perpetual that we are ready to accept settlement stuff 53 whenLeaveOpeningAuction(t, perps, points[0].t) 54 55 // submit the first point then enter an auction 56 submitPointWithDifference(t, perps, points[0], expectedTWAP) 57 whenAuctionStateChanges(t, perps, points[0].t+int64(time.Second), true) 58 59 // submit a crazy point difference, then a normal point 60 submitPointWithDifference(t, perps, points[1], -9999999) 61 submitPointWithDifference(t, perps, points[2], expectedTWAP) 62 63 // now we leave auction and the crazy point difference will not affect the TWAP because it was in an auction period 64 whenAuctionStateChanges(t, perps, points[2].t+int64(time.Second), false) 65 66 fundingPayment := getFundingPayment(t, perps, points[3].t) 67 // 2/3 of funding period spent in auction so expecting funding payment of 1/3*1234=~411 68 assert.Equal(t, "411", fundingPayment) 69 fundingPayment = getFundingPayment(t, perps, points[3].t) 70 assert.Equal(t, "411", fundingPayment) 71 72 // now get the serialised state, and try to load it 73 state1 := perps.perpetual.Serialize() 74 serialized1, err := proto.Marshal(state1) 75 assert.NoError(t, err) 76 77 payload := &snapshotpb.Product{} 78 err = proto.Unmarshal(serialized1, payload) 79 assert.NoError(t, err) 80 81 restoreTime := time.Unix(0, points[3].t) // time.Unix(1000000, 100) 82 perps2, scheduleSrc := testPerpetualSnapshot(t, perps.ctrl, payload, restoreTime) 83 84 // now we serialize again, and check the payload are same 85 state2 := perps2.perpetual.Serialize() 86 serialized2, err := proto.Marshal(state2) 87 assert.NoError(t, err) 88 assert.Equal(t, serialized1, serialized2) 89 90 // check funding payment comes out the same 91 fundingPayment = getFundingPayment(t, perps2, points[3].t) 92 assert.Equal(t, "411", fundingPayment) 93 94 // check the the time-trigger has been set properly 95 cfg := scheduleSrc.Data.GetInternalTimeTriggerSpecConfiguration() 96 97 // trigger time in the past should fail, it should be set to restoreTime so should trigger 98 // on a future time only. The trigger times are precision seconds so we pass it in truncated. 99 assert.False(t, cfg.IsTriggered(restoreTime.Truncate(time.Second))) 100 assert.True(t, cfg.IsTriggered(restoreTime.Add(time.Second))) 101 } 102 103 func TestPerpetualSnapshotNotStarted(t *testing.T) { 104 perps := testPerpetual(t) 105 106 // get fresh state before we've started the first period 107 state1 := perps.perpetual.Serialize() 108 109 serialized1, err := proto.Marshal(state1) 110 assert.NoError(t, err) 111 112 state2 := &snapshotpb.Product{} 113 err = proto.Unmarshal(serialized1, state2) 114 assert.NoError(t, err) 115 116 restoreTime := time.Unix(1000000, 100) 117 perps2, _ := testPerpetualSnapshot(t, perps.ctrl, state2, restoreTime) 118 119 // now we serialize again, and check the payload are same 120 121 state3 := perps2.perpetual.Serialize() 122 serialized2, err := proto.Marshal(state3) 123 assert.NoError(t, err) 124 assert.Equal(t, serialized1, serialized2) 125 } 126 127 func testPerpetualSnapshot(t *testing.T, ctrl *gomock.Controller, state *snapshotpb.Product, tm time.Time) (*tstPerp, *datasource.Spec) { 128 t.Helper() 129 130 log := logging.NewTestLogger() 131 oe := mocks.NewMockOracleEngine(ctrl) 132 broker := mocks.NewMockBroker(ctrl) 133 ts := tmocks.NewMockTimeService(ctrl) 134 dp := uint32(1) 135 136 pubKeys := []*dstypes.Signer{ 137 dstypes.CreateSignerFromString("0xDEADBEEF", dstypes.SignerTypePubKey), 138 } 139 factor, _ := num.DecimalFromString("0.5") 140 settlementSrc := &datasource.Spec{ 141 Data: datasource.NewDefinition( 142 datasource.ContentTypeOracle, 143 ).SetOracleConfig( 144 &signedoracle.SpecConfiguration{ 145 Signers: pubKeys, 146 Filters: []*dstypes.SpecFilter{ 147 { 148 Key: &dstypes.SpecPropertyKey{ 149 Name: "foo", 150 Type: datapb.PropertyKey_TYPE_INTEGER, 151 NumberDecimalPlaces: ptr.From(uint64(dp)), 152 }, 153 Conditions: nil, 154 }, 155 }, 156 }, 157 ), 158 } 159 160 definition := datasource.NewDefinition( 161 datasource.ContentTypeInternalTimeTriggerTermination, 162 ).SetTimeTriggerTriggersConfig( 163 dstypes.InternalTimeTriggers{ 164 &dstypes.InternalTimeTrigger{ 165 Initial: &tm, 166 Every: 5, 167 }, 168 }, 169 ).SetTimeTriggerConditionConfig( 170 []*dstypes.SpecCondition{ 171 { 172 Operator: datapb.Condition_OPERATOR_GREATER_THAN, 173 Value: "0", 174 }, 175 }, 176 ) 177 scheduleSrc := datasource.SpecFromProto(vegapb.NewDataSourceSpec(definition.IntoProto())) 178 179 perp := &types.Perps{ 180 MarginFundingFactor: factor, 181 DataSourceSpecForSettlementData: settlementSrc, 182 DataSourceSpecForSettlementSchedule: scheduleSrc, 183 DataSourceSpecBinding: &datasource.SpecBindingForPerps{ 184 SettlementDataProperty: "foo", 185 SettlementScheduleProperty: "vegaprotocol.builtin.timetrigger", 186 }, 187 } 188 oe.EXPECT().Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(spec.SubscriptionID(1), func(_ context.Context, _ spec.SubscriptionID) {}, nil) 189 ts.EXPECT().GetTimeNow().Times(1).Return(tm) 190 perpetual, err := products.NewPerpetualFromSnapshot(context.Background(), log, perp, "", ts, oe, broker, state.GetPerps(), dp) 191 if err != nil { 192 t.Fatalf("couldn't create a perp for testing: %v", err) 193 } 194 195 return &tstPerp{ 196 perpetual: perpetual, 197 oe: oe, 198 broker: broker, 199 ctrl: ctrl, 200 }, scheduleSrc 201 }