decred.org/dcrdex@v1.0.5/server/book/accounts.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package book
     5  
     6  import (
     7  	"decred.org/dcrdex/dex/order"
     8  )
     9  
    10  // AccountTracking is a bit field representing the assets which need account
    11  // tracking.
    12  type AccountTracking uint8
    13  
    14  const (
    15  	// AccountTrackingBase should be included if the base asset is
    16  	// account-based.
    17  	AccountTrackingBase AccountTracking = 1 << iota
    18  	// AccountTrackingQuote should be included if the quote asset is
    19  	// account-based.
    20  	AccountTrackingQuote
    21  )
    22  
    23  func (a AccountTracking) base() bool {
    24  	return (a & AccountTrackingBase) > 0
    25  }
    26  
    27  func (a AccountTracking) quote() bool {
    28  	return (a & AccountTrackingQuote) > 0
    29  }
    30  
    31  // accountTracker tracks orders for account-based assets. Account tracker only
    32  // tracks assets that are account-based, as specified in the constructor.
    33  // If neither base or quote is account-based, then all of accountTracker's
    34  // methods do nothing, so there's no harm in using a newAccountTracker(0) rather
    35  // than checking whether assets are actually account-based everywhere.
    36  // The accountTracker is not thread-safe. In use, synchronization is provided by
    37  // the *Books's mutex.
    38  type accountTracker struct {
    39  	tracking    AccountTracking
    40  	base, quote map[string]map[order.OrderID]*order.LimitOrder
    41  }
    42  
    43  func newAccountTracker(tracking AccountTracking) *accountTracker {
    44  	// nilness is used to signal that an asset is not account-based and does
    45  	// not need tracking.
    46  	var base, quote map[string]map[order.OrderID]*order.LimitOrder
    47  	if tracking.base() {
    48  		base = make(map[string]map[order.OrderID]*order.LimitOrder, initBookHalfCapacity)
    49  	}
    50  	if tracking.quote() {
    51  		quote = make(map[string]map[order.OrderID]*order.LimitOrder, initBookHalfCapacity)
    52  	}
    53  	return &accountTracker{
    54  		tracking: tracking,
    55  		base:     base,
    56  		quote:    quote,
    57  	}
    58  }
    59  
    60  // add an order to tracking.
    61  func (a *accountTracker) add(lo *order.LimitOrder) {
    62  	if a.base != nil {
    63  		addAccountOrder(lo.BaseAccount(), a.base, lo)
    64  	}
    65  	if a.quote != nil {
    66  		addAccountOrder(lo.QuoteAccount(), a.quote, lo)
    67  	}
    68  }
    69  
    70  // remove an order from tracking.
    71  func (a *accountTracker) remove(lo *order.LimitOrder) {
    72  	if a.base != nil {
    73  		removeAccountOrder(lo.BaseAccount(), a.base, lo.ID())
    74  	}
    75  	if a.quote != nil {
    76  		removeAccountOrder(lo.QuoteAccount(), a.quote, lo.ID())
    77  	}
    78  }
    79  
    80  // addAccountOrder adds the order to the account address -> orders map, creating
    81  // an entry if necessary.
    82  func addAccountOrder(addr string, acctOrds map[string]map[order.OrderID]*order.LimitOrder, lo *order.LimitOrder) {
    83  	ords, found := acctOrds[addr]
    84  	if !found {
    85  		ords = make(map[order.OrderID]*order.LimitOrder)
    86  		acctOrds[addr] = ords
    87  	}
    88  	ords[lo.ID()] = lo
    89  }
    90  
    91  // removeAccountOrder removes the order from the account address -> orders map,
    92  // deleting the map if empty.
    93  func removeAccountOrder(addr string, acctOrds map[string]map[order.OrderID]*order.LimitOrder, oid order.OrderID) {
    94  	ords, found := acctOrds[addr]
    95  	if !found {
    96  		return
    97  	}
    98  	delete(ords, oid)
    99  	if len(ords) == 0 {
   100  		delete(acctOrds, addr)
   101  	}
   102  }
   103  
   104  // iterateBaseAccount calls the provided function for every tracked order with a
   105  // base asset corresponding to the specified account address.
   106  func (a *accountTracker) iterateBaseAccount(acctAddr string, f func(*order.LimitOrder)) {
   107  	if a.base == nil {
   108  		return
   109  	}
   110  	for _, lo := range a.base[acctAddr] {
   111  		f(lo)
   112  	}
   113  }
   114  
   115  // iterateQuoteAccount calls the provided function for every tracked order with
   116  // a quote asset corresponding to the specified account address.
   117  func (a *accountTracker) iterateQuoteAccount(acctAddr string, f func(*order.LimitOrder)) {
   118  	if a.quote == nil {
   119  		return
   120  	}
   121  	for _, lo := range a.quote[acctAddr] {
   122  		f(lo)
   123  	}
   124  }