Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add account-based asset funding validation
Adds funding validation for account-based assets i.e. eth. dex: Add RedeemSize field to Asset. msgjson: Add RedeemSig field to Trade. RedeemSig should be populated when the redemption is to an account-based asset. This PR also indirectly proposes a protocol for specifying the funding account. The account address should be utf-8 encoded and passed as the coin ID of the only coin, along with the pubkey and signature. The data signed will be the serialized msgjson.Limit or msgjson.Market, with server stamp set to 0. Same signature input for the RedeemSig. dex/order: The Trade type has methods for accessing the account address. server/asset: AccountBalancer gets a ValidateSignature method. general scheme: Orders and matches involving account-based assets are indexed by Swapper and OrderPQ in the same way they are indexed for users (account.AccountID). When an order comes into the order router, stats for existing orders, including outstanding redemptions, is fetched from all markets and the swapper. These stats are used with the new order info to validate the users balance. The user must have sufficient balance to cover all outstanding orders and redemptions. server/book: All OrderPQ are given an AccountTracker set up for any account-based assets, or none. The AccountTracker is essentially the same as the userOrders index, but indexed by account address instead. This is important because more than one user could be using the same account. AccountTracker has methods for iterating booked orders, exposed via Book and Market methods used by Market as part of the MarketTunnel interface method, PendingAccount, described below. server/swap: Swapper gets an account index to mirror its account index. The new AccountStats method returns information about in-process matches that aren't yet swapped/redeemed for a particular asset. server/market: OrderRouter is refactored for improved re-use between handleLimit and handleMarket. For account based assets, OrderRouter checks the MarketTunnels and MatchNegotiator for outstanding order and match info, and then queries the backend (via DEXBalancer) to see if the account has sufficient balance. NewMarket handles balance checks on startup. This is accomplished by additional tracking through the orders loop, and then a balance check for account-based assets immediately before adding to the order book. utxo-based asset handling is unchanged except that the lot size compliance check it moved earlier in the loop.
- Loading branch information
Showing
25 changed files
with
1,972 additions
and
488 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// This code is available on the terms of the project LICENSE.md file, | ||
// also available online at https://blueoakcouncil.org/license/1.0.0. | ||
|
||
package book | ||
|
||
import ( | ||
"decred.org/dcrdex/dex/order" | ||
) | ||
|
||
// AccountTracking is a bitfield representing the assets which need account | ||
// tracking. | ||
type AccountTracking uint8 | ||
|
||
const ( | ||
// AccountTrackingBase should be included if the base asset is | ||
// account-based. | ||
AccountTrackingBase AccountTracking = 1 << iota | ||
// AccountTrackingQuote should be included if the quote asset is | ||
// account-based. | ||
AccountTrackingQuote | ||
) | ||
|
||
func (a AccountTracking) base() bool { | ||
return (a & AccountTrackingBase) > 0 | ||
} | ||
|
||
func (a AccountTracking) quote() bool { | ||
return (a & AccountTrackingQuote) > 0 | ||
} | ||
|
||
// accountTracker tracks orders for account-based assets. Account tracker only | ||
// tracks assets that are account-based, as specified in the constructor. | ||
// If neither base or quote is account-based, then all of accountTracker's | ||
// methods do nothing, so there's no harm in using a newAccountTracker(0) rather | ||
// than checking whether assets are actually account-based everywhere. | ||
// The accountTracker is not thread-safe. In use, synchronization is provided by | ||
// the *OrderPQ's mutex. | ||
type accountTracker struct { | ||
tracking AccountTracking | ||
base, quote map[string]map[order.OrderID]*order.LimitOrder | ||
} | ||
|
||
func newAccountTracker(tracking AccountTracking) *accountTracker { | ||
// nilness is used to signal that an asset is not account-based and does | ||
// not need tracking. | ||
var base, quote map[string]map[order.OrderID]*order.LimitOrder | ||
if tracking.base() { | ||
base = make(map[string]map[order.OrderID]*order.LimitOrder) | ||
} | ||
if tracking.quote() { | ||
quote = make(map[string]map[order.OrderID]*order.LimitOrder) | ||
} | ||
return &accountTracker{ | ||
tracking: tracking, | ||
base: base, | ||
quote: quote, | ||
} | ||
} | ||
|
||
// add an order to tracking. | ||
func (a *accountTracker) add(lo *order.LimitOrder) { | ||
if a.base != nil { | ||
addAccountOrder(lo.BaseAccount(), a.base, lo) | ||
} | ||
if a.quote != nil { | ||
addAccountOrder(lo.QuoteAccount(), a.quote, lo) | ||
} | ||
} | ||
|
||
// remove an order from tracking. | ||
func (a *accountTracker) remove(lo *order.LimitOrder) { | ||
if a.base != nil { | ||
removeAccountOrder(lo.BaseAccount(), a.base, lo.ID()) | ||
} | ||
if a.quote != nil { | ||
removeAccountOrder(lo.QuoteAccount(), a.quote, lo.ID()) | ||
} | ||
} | ||
|
||
// addAccountOrder adds the order to the account address -> orders map, creating | ||
// an entry if necessary. | ||
func addAccountOrder(addr string, acctOrds map[string]map[order.OrderID]*order.LimitOrder, lo *order.LimitOrder) { | ||
ords, found := acctOrds[addr] | ||
if !found { | ||
ords = make(map[order.OrderID]*order.LimitOrder) | ||
acctOrds[addr] = ords | ||
} | ||
ords[lo.ID()] = lo | ||
} | ||
|
||
// removeAccountOrder removes the order from the account address -> orders map, | ||
// deleting the map if empty. | ||
func removeAccountOrder(addr string, acctOrds map[string]map[order.OrderID]*order.LimitOrder, oid order.OrderID) { | ||
ords, found := acctOrds[addr] | ||
if !found { | ||
return | ||
} | ||
delete(ords, oid) | ||
if len(ords) == 0 { | ||
delete(acctOrds, addr) | ||
} | ||
} | ||
|
||
// iterateBaseAccount calls the provided function for every tracked order with a | ||
// base asset corresponding to the specified account address. | ||
func (a *accountTracker) iterateBaseAccount(acctAddr string, f func(*order.LimitOrder)) { | ||
if a.base == nil { | ||
return | ||
} | ||
for _, lo := range a.base[acctAddr] { | ||
f(lo) | ||
} | ||
} | ||
|
||
// iterateQuoteAccount calls the provided function for every tracked order with | ||
// a quote asset corresponding to the specified account address. | ||
func (a *accountTracker) iterateQuoteAccount(acctAddr string, f func(*order.LimitOrder)) { | ||
if a.quote == nil { | ||
return | ||
} | ||
for _, lo := range a.quote[acctAddr] { | ||
f(lo) | ||
} | ||
} |
Oops, something went wrong.