code.vegaprotocol.io/vega@v0.79.0/datanode/sqlsubscribers/order.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 sqlsubscribers 17 18 import ( 19 "context" 20 "time" 21 22 "code.vegaprotocol.io/vega/core/events" 23 "code.vegaprotocol.io/vega/core/types" 24 "code.vegaprotocol.io/vega/datanode/entities" 25 "code.vegaprotocol.io/vega/protos/vega" 26 27 "github.com/pkg/errors" 28 ) 29 30 type MarketDepthService interface { 31 OnAMMUpdate(pool entities.AMMPool, vt time.Time, seqNum uint64) 32 AddOrder(order *types.Order, vegaTime time.Time, sequenceNumber uint64) 33 PublishAtEndOfBlock() 34 } 35 36 type OrderEvent interface { 37 events.Event 38 Order() *vega.Order 39 } 40 41 type ExpiredOrdersEvent interface { 42 events.Event 43 MarketID() string 44 OrderIDs() []string 45 } 46 47 type CancelledOrdersEvent interface { 48 ExpiredOrdersEvent 49 PartyID() string 50 } 51 52 type OrderStore interface { 53 Add(entities.Order) error 54 Flush(ctx context.Context) error 55 GetByMarketAndID(ctx context.Context, marketIDstr string, orderIDs []string) ([]entities.Order, error) 56 } 57 58 type Order struct { 59 subscriber 60 store OrderStore 61 depthService MarketDepthService 62 // the store uses the batcher type which could be used as a cache, provided we know the 63 // version and vegatime type for the orders we need to persist. This isn't the case 64 // but orders are ingested here, so we can cache them here, of course. 65 cache map[entities.OrderID]entities.Order 66 } 67 68 func NewOrder(store OrderStore, depthService MarketDepthService) *Order { 69 return &Order{ 70 store: store, 71 depthService: depthService, 72 cache: map[entities.OrderID]entities.Order{}, 73 } 74 } 75 76 func (os *Order) Types() []events.Type { 77 return []events.Type{ 78 events.OrderEvent, 79 events.ExpiredOrdersEvent, 80 events.EndBlockEvent, 81 events.CancelledOrdersEvent, 82 } 83 } 84 85 func (os *Order) Push(ctx context.Context, evt events.Event) error { 86 switch evt.Type() { 87 case events.OrderEvent: 88 return os.consume(evt.(OrderEvent), evt.Sequence()) 89 case events.ExpiredOrdersEvent: 90 return os.expired(ctx, evt.(ExpiredOrdersEvent), evt.Sequence()) 91 case events.EndBlockEvent: 92 os.consumeEndBlock() 93 case events.CancelledOrdersEvent: 94 return os.cancelled(ctx, evt.(CancelledOrdersEvent), evt.Sequence()) 95 } 96 return nil 97 } 98 99 func (os *Order) Flush(ctx context.Context) error { 100 // clear cache 101 os.cache = map[entities.OrderID]entities.Order{} 102 return os.store.Flush(ctx) 103 } 104 105 func (os *Order) expired(ctx context.Context, eo ExpiredOrdersEvent, seqNum uint64) error { 106 orders, err := os.store.GetByMarketAndID(ctx, eo.MarketID(), eo.OrderIDs()) 107 if err != nil { 108 return err 109 } 110 txHash := entities.TxHash(eo.TxHash()) 111 for _, o := range orders { 112 o.Status = entities.OrderStatusExpired 113 o.SeqNum = seqNum 114 o.UpdatedAt = os.vegaTime 115 o.VegaTime = os.vegaTime 116 o.TxHash = txHash 117 118 // to the depth service 119 torder, err := types.OrderFromProto(o.ToProto()) 120 if err != nil { 121 panic(err) 122 } 123 os.depthService.AddOrder(torder, os.vegaTime, seqNum) 124 125 if err := os.persist(o); err != nil { 126 return errors.Wrap(os.store.Add(o), "adding order to database") 127 } 128 // the next order will be insterted as though it was the next event on the bus, with a new sequence number: 129 seqNum++ 130 } 131 return nil 132 } 133 134 func (os *Order) cancelled(ctx context.Context, co CancelledOrdersEvent, seqNum uint64) error { 135 allIds := co.OrderIDs() 136 ids := make([]string, 0, len(allIds)) 137 orders := make([]entities.Order, 0, len(allIds)) 138 for _, id := range allIds { 139 k := entities.OrderID(id) 140 if o, ok := os.cache[k]; ok { 141 orders = append(orders, o) 142 } else { 143 ids = append(ids, id) 144 } 145 } 146 147 if len(ids) > 0 { 148 ncOrders, err := os.store.GetByMarketAndID(ctx, co.MarketID(), ids) 149 if err != nil { 150 return err 151 } 152 orders = append(orders, ncOrders...) 153 } 154 155 txHash := entities.TxHash(co.TxHash()) 156 for _, o := range orders { 157 o.Status = entities.OrderStatusCancelled 158 o.SeqNum = seqNum 159 o.UpdatedAt = os.vegaTime 160 o.VegaTime = os.vegaTime 161 o.TxHash = txHash 162 163 torder, err := types.OrderFromProto(o.ToProto()) 164 if err != nil { 165 panic(err) 166 } 167 os.depthService.AddOrder(torder, os.vegaTime, seqNum) 168 169 if err := os.persist(o); err != nil { 170 return errors.Wrap(err, "adding order to database") 171 } 172 seqNum++ 173 } 174 return nil 175 } 176 177 func (os *Order) consume(oe OrderEvent, seqNum uint64) error { 178 protoOrder := oe.Order() 179 180 order, err := entities.OrderFromProto(protoOrder, seqNum, entities.TxHash(oe.TxHash())) 181 if err != nil { 182 return errors.Wrap(err, "deserializing order") 183 } 184 order.VegaTime = os.vegaTime 185 186 // then publish to the market depthService 187 torder, err := types.OrderFromProto(oe.Order()) 188 if err != nil { 189 panic(err) 190 } 191 os.depthService.AddOrder(torder, os.vegaTime, seqNum) 192 193 return errors.Wrap(os.persist(order), "adding order to database") 194 } 195 196 func (os *Order) consumeEndBlock() { 197 os.depthService.PublishAtEndOfBlock() 198 } 199 200 func (os *Order) persist(o entities.Order) error { 201 os.cache[o.ID] = o 202 return os.store.Add(o) 203 } 204 205 func (os *Order) Name() string { 206 return "Order" 207 }