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  }