Skip to content

Commit

Permalink
market: implement MarketTunnel
Browse files Browse the repository at this point in the history
This implements Market.MidGap and Market.CoinLocked, and adds a CoinLocker.

swap: Create LockableAsset and use in the coins map.
LockOrdersCoins separates orders by locked asset coins internally.
Unlock coins in Swapper.revoke and in processBlock for completed matches.

book: Use a mutex for atomic buy and sell operations (i.e. Buys()).

pg: Add ActiveOrderCoins to retrieve coins for active orders.
Also, fix shallow copy and avoid buffer reuse in (*dbCoins).Scan.

CI: Add structcheck and replace gofmt with goimports.

matcher: add doneOK output, where doneOK = passed - booked
Fix passed not including unmatched standing orders.
  • Loading branch information
chappjc authored Dec 11, 2019
1 parent 4749de6 commit cc08b9b
Show file tree
Hide file tree
Showing 25 changed files with 1,212 additions and 153 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
GO111MODULE: "on"
run: |
export PATH=${PATH}:$(go env GOPATH)/bin
golangci-lint run --disable-all --deadline=10m --enable=gofmt --enable=gosimple --enable=unconvert --enable=ineffassign --enable=govet
golangci-lint run --disable-all --deadline=10m --enable=goimports --enable=gosimple --enable=unconvert --enable=ineffassign --enable=govet --enable=structcheck
- name: Test
env:
GO111MODULE: "on"
Expand Down
49 changes: 46 additions & 3 deletions dex/order/order.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,23 +153,41 @@ type Order interface {
// Quote returns the unique integer identifier of the quote asset as defined
// in the asset package.
Quote() uint32

// IsSell indicates if the order is selling the base asset (false indicates
// selling the quote asset). This helps identify the asset of the backing
// coins returned by CoinIDs(). Note that a cancel order will return false.
IsSell() bool

// Coins returns the backing coins of either base or quote asset depending
// on IsSell.
//
// TODO!!! The orders must be updated to track the current backing coins,
// not just the original backing coins. This is critical for partially
// filled orders where each fill creates change that must then be tracked as
// the new backing coins.
CoinIDs() []CoinID
}

// zeroTime is the Unix time for a Time where IsZero() == true.
var zeroTime = time.Time{}.Unix()

// An order's ID is computed as the Blake-256 hash of the serialized order.
func calcOrderID(order Order) OrderID {
stime := order.Time()
if stime == zeroTime {
sTime := order.Time()
if sTime == zeroTime {
panic("Order's ServerTime is unset")
}
return blake256.Sum256(order.Serialize())
}

// CoinID identifies value on the blockchain.
// CoinID should be used to wrap a []byte so that it may be used as a map key.
type CoinID []byte

func (c CoinID) String() string {
return hex.EncodeToString(c)
}

// Prefix is the order prefix containing data fields common to all orders.
type Prefix struct {
AccountID account.AccountID
Expand Down Expand Up @@ -359,6 +377,16 @@ func (o *MarketOrder) Remaining() uint64 {
return o.Quantity - o.Filled
}

// IsSell indicates if the order is selling the base asset.
func (o *MarketOrder) IsSell() bool {
return o.Sell
}

// CoinIDs returns the order's backing coins.
func (o *MarketOrder) CoinIDs() []CoinID {
return o.Coins
}

// Ensure MarketOrder is an Order.
var _ Order = (*MarketOrder)(nil)

Expand Down Expand Up @@ -434,6 +462,11 @@ func (o *LimitOrder) Price() uint64 {
return o.Rate
}

// IsSell indicates if the order is selling the base asset.
func (o *LimitOrder) IsSell() bool {
return o.Sell
}

// CancelOrder defines a cancel order in terms of an order Prefix and the ID of
// the order to be canceled.
type CancelOrder struct {
Expand Down Expand Up @@ -497,5 +530,15 @@ func (o *CancelOrder) FilledAmt() uint64 {
return 0
}

// IsSell is always false for a CancelOrder.
func (o *CancelOrder) IsSell() bool {
return false
}

// CoinIDs always returns a nil slice for a CancelOrder.
func (o *CancelOrder) CoinIDs() []CoinID {
return nil
}

// Ensure CancelOrder is an Order.
var _ Order = (*CancelOrder)(nil)
1 change: 0 additions & 1 deletion server/asset/btc/btc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,6 @@ func testMakeMsgTx(segwit bool) *testMsgTx {

type testMsgTxSwap struct {
tx *wire.MsgTx
vout uint32
contract []byte
recipient btcutil.Address
}
Expand Down
1 change: 0 additions & 1 deletion server/asset/dcr/dcr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,6 @@ func testMsgTxRegular(sigType dcrec.SignatureType) *testMsgTx {
// Information about a swap contract.
type testMsgTxSwap struct {
tx *wire.MsgTx
vout uint32
contract []byte
recipient dcrutil.Address
}
Expand Down
19 changes: 19 additions & 0 deletions server/book/book.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
package book

import (
"sync"

"decred.org/dcrdex/dex/order"
)

Expand All @@ -21,6 +23,7 @@ const (
// allow constant time access to the best orders, and log time insertion and
// removal of orders.
type Book struct {
mtx sync.RWMutex
lotSize uint64
buys *OrderPQ
sells *OrderPQ
Expand All @@ -44,8 +47,10 @@ func New(lotSize uint64, halfCapacity ...uint32) *Book {
// Realloc changes the capacity of the order book given the specified capacity
// of both buy and sell sides of the book.
func (b *Book) Realloc(newHalfCap uint32) {
b.mtx.Lock()
b.buys.Realloc(newHalfCap)
b.sells.Realloc(newHalfCap)
b.mtx.Unlock()
}

// LotSize returns the Book's configured lot size in atoms of the base asset.
Expand Down Expand Up @@ -75,6 +80,16 @@ func (b *Book) BestBuy() *order.LimitOrder {
return b.buys.PeekBest()
}

// Best returns pointers to the best buy and sell order in the order book. The
// orders are NOT removed from the book.
func (b *Book) Best() (bestBuy, bestSell *order.LimitOrder) {
b.mtx.RLock()
bestBuy = b.buys.PeekBest()
bestSell = b.sells.PeekBest()
b.mtx.RUnlock()
return
}

// Insert attempts to insert the provided order into the order book, returning a
// boolean indicating if the insertion was successful. If the order is not an
// integer multiple of the Book's lot size, the order will not be inserted.
Expand All @@ -84,6 +99,8 @@ func (b *Book) Insert(o *order.LimitOrder) bool {
"quantity that is not a multiple of lot size.")
return false
}
b.mtx.Lock()
defer b.mtx.Unlock()
if o.Sell {
return b.sells.Insert(o)
}
Expand All @@ -93,6 +110,8 @@ func (b *Book) Insert(o *order.LimitOrder) bool {
// Remove attempts to remove the order with the given OrderID from the book.
func (b *Book) Remove(oid order.OrderID) (*order.LimitOrder, bool) {
uid := oid.String()
b.mtx.Lock()
defer b.mtx.Unlock()
if removed, ok := b.sells.RemoveOrderUID(uid); ok {
return removed, true
}
Expand Down
Loading

0 comments on commit cc08b9b

Please sign in to comment.