diff --git a/tatanka/db/db.go b/tatanka/db/db.go index cfbf950dcd..b5a5874387 100644 --- a/tatanka/db/db.go +++ b/tatanka/db/db.go @@ -7,6 +7,7 @@ import ( "context" "encoding" "encoding/binary" + "errors" "fmt" "os" "path/filepath" @@ -20,16 +21,12 @@ import ( type DB struct { *lexi.DB - log dex.Logger - scores *lexi.Table - scoredIdx *lexi.Index - bonds *lexi.Table - bonderIdx *lexi.Index - bondStampIdx *lexi.Index - orderbook *lexi.Table - orderbookOrderIDIdx *lexi.Index - orderbookStampIdx *lexi.Index - orderbookPeerIDIdx *lexi.Index + log dex.Logger + scores *lexi.Table + scoredIdx *lexi.Index + bonds *lexi.Table + bonderIdx *lexi.Index + bondStampIdx *lexi.Index } func New(dir string, log dex.Logger) (*DB, error) { @@ -92,11 +89,30 @@ func New(dir string, log dex.Logger) (*DB, error) { if err != nil { return nil, fmt.Errorf("error constructing bond stamp index: %w", err) } - orderbookTable, err := db.Table("orderbook") + return &DB{ + DB: db, + scores: scoreTable, + scoredIdx: scoredIdx, + log: log, + bonds: bondsTable, + bonderIdx: bonderIdx, + bondStampIdx: bondStampIdx, + }, nil +} + +func (db *DB) NewOrderBook(baseID, quoteID uint32) (*OrderBook, error) { + bSym := dex.BipIDSymbol(baseID) + qSym := dex.BipIDSymbol(quoteID) + if bSym == "" || qSym == "" { + return nil, errors.New("could not find base or quote symbol") + } + prefix := fmt.Sprintf("%s-%s") + orderBookTable, err := db.Table(fmt.Sprintf("%s-orderbook", prefix)) if err != nil { return nil, fmt.Errorf("error constructing orderbook table: %w", err) } - orderbookOrderIDIdx, err := orderbookTable.AddIndex("orderbook-orderid", func(_, v encoding.BinaryMarshaler) ([]byte, error) { + + orderBookOrderIDIdx, err := orderBookTable.AddIndex(fmt.Sprintf("%s-orderbook-orderid", prefix), func(_, v encoding.BinaryMarshaler) ([]byte, error) { o, is := v.(*OrderUpdate) if !is { return nil, fmt.Errorf("wrong type %T", v) @@ -104,34 +120,42 @@ func New(dir string, log dex.Logger) (*DB, error) { oID := o.ID() return oID[:], nil }) - orderbookStampIdx, err := orderbookTable.AddIndex("orderbook-stamp", func(_, v encoding.BinaryMarshaler) ([]byte, error) { + + orderBookStampIdx, err := orderBookTable.AddIndex(fmt.Sprintf("%s-orderbook-stamp", prefix), func(_, v encoding.BinaryMarshaler) ([]byte, error) { o, is := v.(*OrderUpdate) if !is { return nil, fmt.Errorf("wrong type %T", v) } tB := make([]byte, 8) - binary.BigEndian.PutUint64(tB, uint64(o.Stamp.UnixMilli())) return tB, nil }) - orderbookPeerIDIdx, err := orderbookTable.AddIndex("orderbook-peerid", func(_, v encoding.BinaryMarshaler) ([]byte, error) { + + orderBookPeerIDIdx, err := orderBookTable.AddIndex(fmt.Sprintf("%s-orderbook-peerid", prefix), func(_, v encoding.BinaryMarshaler) ([]byte, error) { o, is := v.(*OrderUpdate) if !is { return nil, fmt.Errorf("wrong type %T", v) } return o.From[:], nil }) - return &DB{ - DB: db, - scores: scoreTable, - scoredIdx: scoredIdx, - log: log, - bonds: bondsTable, - bonderIdx: bonderIdx, - bondStampIdx: bondStampIdx, - orderbook: orderbookTable, - orderbookOrderIDIdx: orderbookOrderIDIdx, - orderbookStampIdx: orderbookStampIdx, - orderbookPeerIDIdx: orderbookPeerIDIdx, + + orderBookSellRateIdx, err := orderBookTable.AddIndex(fmt.Sprintf("%s-orderbook-sellrate", prefix), func(_, v encoding.BinaryMarshaler) ([]byte, error) { + o, is := v.(*OrderUpdate) + if !is { + return nil, fmt.Errorf("wrong type %T", v) + } + srB := make([]byte, 1+8) + if o.Sell { + srB[0] = 1 + } + copy(srB[1:], o.Rate) + return srB, nil + }) + + return &OrderBook{ + orderBookOrderIDIdx: orderBookOrderIDIdx, + orderBookStampIdx: orderBookStampIdx, + orderBookPeerIDIdx: orderBookPeerIDIdx, + orderBookSellRateIdx: orderBookSellRateIdx, }, nil } diff --git a/tatanka/db/orderbook.go b/tatanka/db/orderbook.go index 89aa827daa..0ccbfd6491 100644 --- a/tatanka/db/orderbook.go +++ b/tatanka/db/orderbook.go @@ -4,25 +4,15 @@ package db import ( + "errors" "fmt" "time" "decred.org/dcrdex/dex/encode" "decred.org/dcrdex/dex/lexi" - "decred.org/dcrdex/dex/order" "decred.org/dcrdex/tatanka/tanka" ) -const ( - PageLen = 100 -) - -type Page struct { - PageN uint64 `json:"pagen"` // from 0 - NPages uint64 `json:"npages"` - OrderIDs []order.OrderID `json:"orderids"` -} - type OrderUpdate struct { Sig []byte `json:"sig"` *tanka.Order `json:"order"` // updated status @@ -84,73 +74,92 @@ func (o *OrderUpdate) UnmarshalBinary(b []byte) error { } type OrderBooker interface { - Pages() ([]*Page, error) - Order(id order.OrderID) (*tanka.Order, error) - Orders(id []order.OrderID) ([]*tanka.Order, error) - FindOrders(filter *OrderFilter) ([]*tanka.Order, error) + OrderIDs(from, nOrders uint64) ([]tanka.ID32, bool, error) + Order(id tanka.ID32) (*OrderUpdate, error) + Orders(id []tanka.ID32) ([]*OrderUpdate, error) + FindOrders(filter *OrderFilter) ([]*OrderUpdate, error) Add(*OrderUpdate) error Update(*OrderUpdate) error - Delete(id order.OrderID) error + Delete(id tanka.ID32) error } -type OrderFilter struct{} +type OrderFilter struct { + isSell *bool + nOrders, rate, qty, lotSize, minFeeRate *uint64 + peerID *tanka.PeerID +} -var _ OrderBooker = (*DB)(nil) +var _ OrderBooker = (*OrderBook)(nil) -func (d *DB) Pages() ([]*Page, error) { +type OrderBook struct { + orderBook *lexi.Table + orderBookOrderIDIdx *lexi.Index + orderBookStampIdx *lexi.Index + orderBookPeerIDIdx *lexi.Index + orderBookSellRateIDIdx *lexi.Index +} + +func (ob *OrderBook) OrderIDs(from, nOrders uint64) (oids []tanka.ID32, all bool, err error) { var ( - page Page - pages []*Page - i uint64 + i uint64 + enoughErr = errors.New("enough") ) - err := d.orderbookOrderIDIdx.Iterate(nil, func(it *lexi.Iter) error { - if i%PageLen == 0 { - page = Page{ - PageN: i / PageLen, - } - pages = append(pages, &page) + all = true + err = ob.orderBookOrderIDIdx.Iterate([]byte{}, func(it *lexi.Iter) error { + if i < nOrders { + i++ + return nil + } + if i-from > nOrders { + all = false + return enoughErr } k, err := it.K() if err != nil { return fmt.Errorf("error getting order key: %w", err) } - var oid order.OrderID + var oid tanka.ID32 copy(oid[:], k) + oids = append(oids, oid) i++ return nil }) - if err != nil { - return nil, err + if err != nil && !errors.Is(err, enoughErr) { + return nil, false, err } - if len(pages) > 0 { - nPages := i - 1/PageLen - for _, p := range pages { - p.NPages = nPages - } - } - return pages, nil + return oids, all, nil } -func (*DB) Order(id order.OrderID) (*tanka.Order, error) { - return nil, nil +func (ob *OrderBook) Order(id tanka.ID32) (ou *OrderUpdate, err error) { + return ou, ob.orderBook.Get(id, ou) } -func (*DB) Orders(id []order.OrderID) ([]*tanka.Order, error) { - return nil, nil +func (ob *OrderBook) Orders(ids []tanka.ID32) ([]*OrderUpdate, error) { + ous := make([]*OrderUpdate, 0, len(ids)) + for _, id := range ids { + var ou *OrderUpdate + if err := ob.orderBook.Get(id, ou); err != nil { + return nil, err + } + ous = append(ous, ou) + } + return ous, nil } -func (*DB) FindOrders(filter *OrderFilter) ([]*tanka.Order, error) { +func (*OrderBook) FindOrders(filter *OrderFilter) ([]*OrderUpdate, error) { return nil, nil } -func (*DB) Add(*OrderUpdate) error { - return nil +func (ob *OrderBook) Add(ou *OrderUpdate) error { + id := ou.ID() + return ob.orderBook.Set(id, ou, lexi.WithReplace()) } -func (*DB) Update(*OrderUpdate) error { - return nil +func (ob *OrderBook) Update(ou *OrderUpdate) error { + id := ou.ID() + return ob.orderBook.Set(id, ou, lexi.WithReplace()) } -func (*DB) Delete(id order.OrderID) error { - return nil +func (ob *OrderBook) Delete(id tanka.ID32) error { + return ob.orderBook.Delete(id[:]) } diff --git a/tatanka/tanka/swaps.go b/tatanka/tanka/swaps.go index f5bcc208a2..eb9c238ac7 100644 --- a/tatanka/tanka/swaps.go +++ b/tatanka/tanka/swaps.go @@ -36,7 +36,7 @@ type Order struct { Expiration time.Time `json:"expiration"` } -func (ord *Order) ID() [32]byte { +func (ord *Order) ID() ID32 { const msgLen = 32 + 4 + 4 + 1 + 8 + 8 + 8 + 8 + 8 + 4 b := make([]byte, msgLen) copy(b[:32], ord.From[:]) @@ -62,6 +62,10 @@ func (i ID32) String() string { return hex.EncodeToString(i[:]) } +func (i ID32) MarshalBinary() ([]byte, error) { + return i[:], nil +} + type Match struct { From PeerID `json:"from"` OrderID ID32 `json:"orderID"`