From 1944b04d074d8a8e4d9ea3e505ffc91be425fb3b Mon Sep 17 00:00:00 2001 From: ekzyis Date: Tue, 28 Nov 2023 21:39:58 +0100 Subject: [PATCH] Run matchmaking --- db/market.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ lnd/invoice.go | 14 +++++++++++ 2 files changed, 78 insertions(+) diff --git a/db/market.go b/db/market.go index d711394..d35bd32 100644 --- a/db/market.go +++ b/db/market.go @@ -3,6 +3,8 @@ package db import ( "context" "database/sql" + "log" + "time" ) type FetchOrdersWhere struct { @@ -112,6 +114,16 @@ func (db *DB) CreateOrder(tx *sql.Tx, ctx context.Context, order *Order) error { return nil } +func (db *DB) FetchOrder(tx *sql.Tx, ctx context.Context, orderId string, order *Order) error { + query := "" + + "SELECT o.id, o.share_id, o.pubkey, o.side, o.quantity, o.price, o.invoice_id, o.created_at, s.description, s.market_id, i.confirmed_at " + + "FROM orders o " + + "JOIN invoices i ON o.invoice_id = i.id " + + "JOIN shares s ON o.share_id = s.id " + + "WHERE o.id = $1" + return tx.QueryRowContext(ctx, query, orderId).Scan(&order.Id, &order.ShareId, &order.Pubkey, &order.Side, &order.Quantity, &order.Price, &order.InvoiceId, &order.CreatedAt, &order.Share.Description, &order.MarketId, &order.Invoice.ConfirmedAt) +} + func (db *DB) FetchUserOrders(pubkey string, orders *[]Order) error { query := "" + "SELECT o.id, share_id, o.pubkey, o.side, o.quantity, o.price, o.invoice_id, o.created_at, s.description, s.market_id, i.confirmed_at, " + @@ -155,3 +167,55 @@ func (db *DB) FetchMarketOrders(marketId int64, orders *[]Order) error { } return nil } + +func (db *DB) RunMatchmaking(orderId string) { + var ( + ctx context.Context + cancel context.CancelFunc + tx *sql.Tx + o1 Order + o2 Order + err error + ) + ctx, cancel = context.WithTimeout(context.TODO(), 10*time.Second) + defer cancel() + if tx, err = db.BeginTx(ctx, nil); err != nil { + log.Println(err) + return + } + // TODO: assert that order was confirmed + if err = db.FetchOrder(tx, ctx, orderId, &o1); err != nil { + log.Println(err) + tx.Rollback() + return + } + if err = db.FindOrderMatches(tx, ctx, &o1, &o2); err != nil { + log.Println(err) + tx.Rollback() + return + } + if _, err = tx.ExecContext(ctx, "UPDATE orders SET order_id = $1 WHERE id = $2", o1.Id, o2.Id); err != nil { + log.Println(err) + tx.Rollback() + return + } + if _, err = tx.ExecContext(ctx, "UPDATE orders SET order_id = $1 WHERE id = $2", o2.Id, o1.Id); err != nil { + log.Println(err) + tx.Rollback() + return + } + log.Printf("Matched orders: %s <> %s\n", o1.Id, o2.Id) + tx.Commit() +} + +func (db *DB) FindOrderMatches(tx *sql.Tx, ctx context.Context, o1 *Order, o2 *Order) error { + query := "" + + "SELECT o.id FROM orders o " + + "JOIN shares s ON s.id = o.share_id " + + "JOIN invoices i ON i.id = o.invoice_id " + + "WHERE i.confirmed_at IS NOT NULL " + + "AND o.order_id IS NULL AND o.pubkey <> $1 AND o.quantity = $2 AND s.market_id = $3 AND " + + "( (o.share_id <> $4 AND o.side = $5::ORDER_SIDE AND o.price = (100 - $6)) OR (o.share_id = $4 AND o.side <> $5::ORDER_SIDE AND o.price = $6)) " + + "ORDER BY o.created_at ASC LIMIT 1" + return tx.QueryRowContext(ctx, query, o1.Pubkey, o1.Quantity, o1.Share.MarketId, o1.ShareId, o1.Side, o1.Price).Scan(&o2.Id) +} diff --git a/lnd/invoice.go b/lnd/invoice.go index 917214e..59df1ad 100644 --- a/lnd/invoice.go +++ b/lnd/invoice.go @@ -112,7 +112,21 @@ func (lnd *LNDClient) CheckInvoice(d *db.DB, hash lntypes.Hash) { continue } log.Printf("invoice confirmed: hash=%s", hash) + + // Run matchmaking if an order was paid + var orderId string + if err = d.QueryRowContext(ctx, + "SELECT o.id FROM orders o WHERE invoice_id = (SELECT i.id FROM invoices i WHERE hash = $1)", + hash.String(), + ).Scan(&orderId); err != nil && err != sql.ErrNoRows { + handleLoopError(err) + continue + } + if orderId != "" { + go d.RunMatchmaking(orderId) + } tx.Commit() + break } time.Sleep(pollInterval)