code.vegaprotocol.io/vega@v0.79.0/core/settlement/snapshot.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 settlement 17 18 import ( 19 "context" 20 "sort" 21 22 "code.vegaprotocol.io/vega/core/types" 23 "code.vegaprotocol.io/vega/libs/num" 24 "code.vegaprotocol.io/vega/libs/proto" 25 "code.vegaprotocol.io/vega/logging" 26 ) 27 28 type SnapshotEngine struct { 29 *Engine 30 stopped bool 31 } 32 33 func NewSnapshotEngine(log *logging.Logger, conf Config, product Product, market string, timeService TimeService, broker Broker, positionFactor num.Decimal) *SnapshotEngine { 34 return &SnapshotEngine{ 35 Engine: New(log, conf, product, market, timeService, broker, positionFactor), 36 } 37 } 38 39 // StopSnapshots is called when the engines respective market no longer exists. We need to stop 40 // taking snapshots and communicate to the snapshot engine to remove us as a provider. 41 func (e *SnapshotEngine) StopSnapshots() { 42 e.log.Debug("market has been cleared, stopping snapshot production", logging.String("marketid", e.market)) 43 e.stopped = true 44 } 45 46 func (e *SnapshotEngine) Stopped() bool { 47 return e.stopped 48 } 49 50 func (e *SnapshotEngine) Namespace() types.SnapshotNamespace { 51 return types.SettlementSnapshot 52 } 53 54 func (e *SnapshotEngine) Keys() []string { 55 return []string{e.market} 56 } 57 58 func (e *SnapshotEngine) GetState(k string) ([]byte, []types.StateProvider, error) { 59 if k != e.market { 60 return nil, nil, types.ErrSnapshotKeyDoesNotExist 61 } 62 state, err := e.serialise() 63 return state, nil, err 64 } 65 66 func (e *SnapshotEngine) LoadState(_ context.Context, payload *types.Payload) ([]types.StateProvider, error) { 67 if e.Namespace() != payload.Data.Namespace() { 68 return nil, types.ErrInvalidSnapshotNamespace 69 } 70 71 switch pl := payload.Data.(type) { 72 case *types.PayloadSettlement: 73 data := pl.SettlementState 74 75 e.log.Debug("loading settlement snapshot", 76 logging.Int("positions", len(data.PartyLastSettledPosition)), 77 logging.Int("trades", len(data.Trades)), 78 ) 79 80 e.settledPosition = make(map[string]int64, len(data.PartyLastSettledPosition)) 81 for _, psp := range data.PartyLastSettledPosition { 82 e.settledPosition[psp.Party] = psp.SettledPosition 83 } 84 85 e.lastMarkPrice = data.LastMarkPrice 86 87 // restore trades 88 tradeMap := map[string][]*settlementTrade{} 89 for _, trade := range data.Trades { 90 party := trade.Party 91 st := stTypeToInternal(trade) 92 ps, ok := tradeMap[party] 93 if !ok { 94 ps = make([]*settlementTrade, 0, 5) // some buffer 95 } 96 tradeMap[party] = append(ps, st) 97 } 98 e.trades = tradeMap 99 // we restored state just fine 100 return nil, nil 101 default: 102 return nil, types.ErrUnknownSnapshotType 103 } 104 } 105 106 func (e *SnapshotEngine) serialise() ([]byte, error) { 107 // we just use the embedded market positions type for the market ID 108 // positions aren't working correctly for some reason, we get them from positions engine 109 data := types.SettlementState{ 110 MarketID: e.market, 111 LastMarkPrice: e.lastMarkPrice, 112 } 113 114 lastSettledPositions := make([]*types.PartySettledPosition, 0, len(e.settledPosition)) 115 for k, v := range e.settledPosition { 116 lastSettledPositions = append(lastSettledPositions, &types.PartySettledPosition{Party: k, SettledPosition: v}) 117 } 118 sort.Slice(lastSettledPositions, func(i, j int) bool { return lastSettledPositions[i].Party < lastSettledPositions[j].Party }) 119 data.PartyLastSettledPosition = lastSettledPositions 120 121 // first get all parties that traded 122 tradeParties := make([]string, 0, len(e.trades)) 123 tradeTotal := 0 124 // convert to correct type, keep that in a map 125 mapped := make(map[string][]*types.SettlementTrade, len(e.trades)) 126 for k, trades := range e.trades { 127 tradeParties = append(tradeParties, k) // slice of parties 128 mapped[k] = internalSTToType(trades, k) 129 tradeTotal += len(trades) // keep track of the total trades 130 } 131 // get map keys sorted 132 sort.Strings(tradeParties) 133 // now do the trades 134 trades := make([]*types.SettlementTrade, 0, tradeTotal) 135 for _, p := range tradeParties { 136 pp := mapped[p] 137 // append trades for party 138 trades = append(trades, pp...) 139 } 140 data.Trades = trades 141 142 // now the payload type to serialise: 143 payload := types.Payload{ 144 Data: &types.PayloadSettlement{ 145 SettlementState: &data, 146 }, 147 } 148 ser, err := proto.Marshal(payload.IntoProto()) 149 if err != nil { 150 return nil, err 151 } 152 153 return ser, nil 154 } 155 156 func internalSTToType(trades []*settlementTrade, party string) []*types.SettlementTrade { 157 ret := make([]*types.SettlementTrade, 0, len(trades)) 158 for _, t := range trades { 159 ret = append(ret, &types.SettlementTrade{ 160 Price: t.price, 161 MarketPrice: t.marketPrice, 162 Size: t.size, 163 NewSize: t.newSize, 164 Party: party, 165 }) 166 } 167 return ret 168 } 169 170 func stTypeToInternal(st *types.SettlementTrade) *settlementTrade { 171 return &settlementTrade{ 172 size: st.Size, 173 newSize: st.NewSize, 174 price: st.Price, 175 marketPrice: st.MarketPrice, 176 } 177 }