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 }