decred.org/dcrdex@v1.0.5/server/db/interface.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 db
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	"decred.org/dcrdex/dex"
    11  	"decred.org/dcrdex/dex/candles"
    12  	"decred.org/dcrdex/dex/order"
    13  	"decred.org/dcrdex/server/account"
    14  )
    15  
    16  // EpochResults represents the outcome of epoch order processing, including
    17  // preimage collection, and computation of commitment checksum and shuffle seed.
    18  // MatchTime is the time at which order matching is executed.
    19  type EpochResults struct {
    20  	MktBase, MktQuote uint32
    21  	Idx               int64
    22  	Dur               int64
    23  	MatchTime         int64
    24  	CSum              []byte
    25  	Seed              []byte
    26  	OrdersRevealed    []order.OrderID
    27  	OrdersMissed      []order.OrderID
    28  	MatchVolume       uint64
    29  	QuoteVolume       uint64
    30  	BookBuys          uint64
    31  	BookBuys5         uint64
    32  	BookBuys25        uint64
    33  	BookSells         uint64
    34  	BookSells5        uint64
    35  	BookSells25       uint64
    36  	HighRate          uint64
    37  	LowRate           uint64
    38  	StartRate         uint64
    39  	EndRate           uint64
    40  }
    41  
    42  // OrderStatus is the current status of an order.
    43  type OrderStatus struct {
    44  	ID     order.OrderID
    45  	Status order.OrderStatus
    46  }
    47  
    48  // PreimageResult is the outcome of preimage collection for an order in an epoch
    49  // that closed at a certain time.
    50  type PreimageResult struct {
    51  	Miss bool
    52  	Time int64
    53  	ID   order.OrderID
    54  }
    55  
    56  // KeyIndexer are the functions required to track an extended public key and
    57  // derived children by index.
    58  type KeyIndexer interface {
    59  	KeyIndex(xpub string) (uint32, error)
    60  	SetKeyIndex(idx uint32, xpub string) error
    61  }
    62  
    63  // DEXArchivist will be composed of several different interfaces. Starting with
    64  // OrderArchiver.
    65  type DEXArchivist interface {
    66  	// LastErr should returns any fatal or unexpected error encountered by the
    67  	// archivist backend. This may be used to check if the database had an
    68  	// unrecoverable error (disconnect, etc.).
    69  	LastErr() error
    70  
    71  	// Fatal provides select semantics like Context.Done when there is a fatal
    72  	// backend error. Use LastErr to get the error.
    73  	Fatal() <-chan struct{}
    74  
    75  	// Close should gracefully shutdown the backend, returning when complete.
    76  	Close() error
    77  
    78  	// InsertEpoch stores the results of a newly-processed epoch.
    79  	InsertEpoch(ed *EpochResults) error
    80  
    81  	// LastEpochRate gets the EndRate of the last EpochResults inserted for the
    82  	// market. If the database is empty, no error and a rate of zero are
    83  	// returned.
    84  	LastEpochRate(b, q uint32) (uint64, error)
    85  
    86  	// LoadEpochStats reads all market epoch history from the database.
    87  	LoadEpochStats(uint32, uint32, []*candles.Cache) error
    88  	LastCandleEndStamp(base, quote uint32, candleDur uint64) (uint64, error)
    89  	InsertCandles(base, quote uint32, dur uint64, cs []*candles.Candle) error
    90  
    91  	OrderArchiver
    92  	AccountArchiver
    93  	KeyIndexer
    94  	MatchArchiver
    95  	SwapArchiver
    96  }
    97  
    98  // OrderArchiver is the interface required for storage and retrieval of all
    99  // order data.
   100  type OrderArchiver interface {
   101  	// Order retrieves an order with the given OrderID, stored for the market
   102  	// specified by the given base and quote assets.
   103  	Order(oid order.OrderID, base, quote uint32) (order.Order, order.OrderStatus, error)
   104  
   105  	// BookOrders returns all book orders for a market.
   106  	BookOrders(base, quote uint32) ([]*order.LimitOrder, error)
   107  
   108  	// EpochOrders returns all epoch orders for a market.
   109  	EpochOrders(base, quote uint32) ([]order.Order, error)
   110  
   111  	// FlushBook revokes all booked orders for a market.
   112  	FlushBook(base, quote uint32) (sellsRemoved, buysRemoved []order.OrderID, err error)
   113  
   114  	// ActiveOrderCoins retrieves a CoinID slice for each active order.
   115  	ActiveOrderCoins(base, quote uint32) (baseCoins, quoteCoins map[order.OrderID][]order.CoinID, err error)
   116  
   117  	// UserOrders retrieves all orders for the given account in the market
   118  	// specified by a base and quote asset.
   119  	UserOrders(ctx context.Context, aid account.AccountID, base, quote uint32) ([]order.Order, []order.OrderStatus, error)
   120  
   121  	// UserOrderStatuses retrieves the statuses and filled amounts of the orders
   122  	// with the provided order IDs for the given account in the market specified
   123  	// by a base and quote asset.
   124  	// The number and ordering of the returned statuses is not necessarily the
   125  	// same as the number and ordering of the provided order IDs. It is not an
   126  	// error if any or all of the provided order IDs cannot be found for the
   127  	// given account in the specified market.
   128  	UserOrderStatuses(aid account.AccountID, base, quote uint32, oids []order.OrderID) ([]*OrderStatus, error)
   129  
   130  	// ActiveUserOrderStatuses retrieves the statuses and filled amounts of all
   131  	// active orders for a user across all markets.
   132  	ActiveUserOrderStatuses(aid account.AccountID) ([]*OrderStatus, error)
   133  
   134  	// CompletedUserOrders retrieves the N most recently completed orders for a
   135  	// user across all markets.
   136  	CompletedUserOrders(aid account.AccountID, N int) (oids []order.OrderID, compTimes []int64, err error)
   137  
   138  	// PreimageStats retrieves the N most recent results of preimage requests
   139  	// for the user across all markets.
   140  	PreimageStats(user account.AccountID, lastN int) ([]*PreimageResult, error)
   141  
   142  	// ExecutedCancelsForUser retrieves up to N executed cancel orders for a
   143  	// given user. These may be user-initiated cancels, or cancels created by
   144  	// the server (revokes). Executed cancel orders from all markets are
   145  	// returned.
   146  	ExecutedCancelsForUser(aid account.AccountID, N int) ([]*CancelRecord, error)
   147  
   148  	// OrderWithCommit searches all markets' trade and cancel orders, both
   149  	// active and archived, for an order with the given Commitment.
   150  	OrderWithCommit(ctx context.Context, commit order.Commitment) (found bool, oid order.OrderID, err error)
   151  
   152  	// OrderStatus gets the status, ID, and filled amount of the given order.
   153  	OrderStatus(order.Order) (order.OrderStatus, order.OrderType, int64, error)
   154  
   155  	// NewEpochOrder stores a new order with epoch status. Such orders are
   156  	// pending execution or insertion on a book (standing limit orders with a
   157  	// remaining unfilled amount). For trade orders, the epoch gap should be
   158  	// db.EpochGapNA, while for cancel orders it is the number of epochs since
   159  	// the targeted order was placed, as described in the docs for CancelRecord.
   160  	NewEpochOrder(ord order.Order, epochIdx, epochDur int64, epochGap int32) error
   161  
   162  	// StorePreimage stores the preimage associated with an existing order.
   163  	StorePreimage(ord order.Order, pi order.Preimage) error
   164  
   165  	// BookOrder books the given order. If the order was already stored (i.e.
   166  	// NewEpochOrder), it's status and filled amount are updated, otherwise it
   167  	// is inserted. See also UpdateOrderFilled.
   168  	BookOrder(*order.LimitOrder) error
   169  
   170  	// ExecuteOrder puts the order into the executed state, and sets the filled
   171  	// amount for market and limit orders. For unmatched cancel orders, use
   172  	// FailCancelOrder instead.
   173  	ExecuteOrder(ord order.Order) error
   174  
   175  	// CancelOrder puts a limit order into the canceled state. Market orders
   176  	// must use ExecuteOrder since they may not be canceled. Similarly, cancel
   177  	// orders must use ExecuteOrder or FailCancelOrder. Orders that are
   178  	// terminated by the DEX rather than via a cancel order are considered
   179  	// "revoked", and RevokeOrder should be used to set this status.
   180  	CancelOrder(*order.LimitOrder) error
   181  
   182  	// RevokeOrder puts an order into the revoked state, and generates a cancel
   183  	// order to record the action. Orders should be revoked by the DEX according
   184  	// to policy on failed orders. For canceling an order that was matched with
   185  	// a cancel order, use CancelOrder.
   186  	RevokeOrder(order.Order) (cancelID order.OrderID, t time.Time, err error)
   187  
   188  	// RevokeOrderUncounted is like RevokeOrder except that the generated cancel
   189  	// order will not be counted against the user. i.e. ExecutedCancelsForUser
   190  	// should not return the cancel orders created this way.
   191  	RevokeOrderUncounted(order.Order) (cancelID order.OrderID, t time.Time, err error)
   192  
   193  	// NewArchivedCancel stores a cancel order directly in the executed state. This
   194  	// is used for orders that are canceled when the market is suspended, and therefore
   195  	// do not need to be matched.
   196  	NewArchivedCancel(ord *order.CancelOrder, epochID, epochDur int64) error
   197  
   198  	// FailCancelOrder puts an unmatched cancel order into the executed state.
   199  	// For matched cancel orders, use ExecuteOrder.
   200  	FailCancelOrder(*order.CancelOrder) error
   201  
   202  	// UpdateOrderFilled updates the filled amount of the given order. This
   203  	// function applies only to limit orders, not cancel or market orders. The
   204  	// filled amount of a market order should be updated by ExecuteOrder.
   205  	UpdateOrderFilled(*order.LimitOrder) error
   206  
   207  	// UpdateOrderStatus updates the status and filled amount of the given
   208  	// order.
   209  	UpdateOrderStatus(order.Order, order.OrderStatus) error
   210  
   211  	// SetOrderCompleteTime sets the successful completion time for an existing
   212  	// order. This will follow the final step in swap negotiation, for an order
   213  	// that is not on the book.
   214  	SetOrderCompleteTime(ord order.Order, compTimeMs int64) error
   215  }
   216  
   217  // Account holds data returned by Accounts.
   218  type Account struct {
   219  	AccountID account.AccountID `json:"accountid"`
   220  	Pubkey    dex.Bytes         `json:"pubkey"`
   221  }
   222  
   223  // Bond represents a time-locked fidelity bond posted by a user.
   224  type Bond struct {
   225  	Version  uint16
   226  	AssetID  uint32
   227  	CoinID   []byte
   228  	Amount   int64
   229  	Strength uint32 // Amount / <bond increment at time of acceptance>
   230  	LockTime int64
   231  
   232  	// Will we need to store asset-specific data, like the redeem script for
   233  	// UTXO assets or a contract address or bond key for account assets? Or will
   234  	// that info be conveyed by Version and CoinID?
   235  	//
   236  	// Data []byte
   237  }
   238  
   239  // AccountArchiver is the interface required for storage and retrieval of all
   240  // account data.
   241  type AccountArchiver interface {
   242  	// Account retrieves the account information for the specified account ID. A
   243  	// nil pointer will be returned for unknown or closed accounts. Bond and
   244  	// registration fee payment status is returned as well. A bond is active if
   245  	// its lockTime is after the lockTimeThresh Time, which should be
   246  	// time.Now().Add(bondExpiry). The legacy bool return refers to the legacy
   247  	// registration fee system, and legacyPaid indicates if the account has a
   248  	// recorded fee coin (paid legacy fee).
   249  	Account(acctID account.AccountID, lockTimeThresh time.Time) (acct *account.Account, activeBonds []*Bond)
   250  
   251  	// CreateAccountWithBond creates a new account with the given bond. This is
   252  	// used for the new postbond request protocol. The bond tx should be
   253  	// fully-confirmed.
   254  	CreateAccountWithBond(acct *account.Account, bond *Bond) error
   255  
   256  	// AddBond stores a new Bond, which is uniquely identified by (asset ID,
   257  	// coin ID), for an existing account.
   258  	AddBond(acct account.AccountID, bond *Bond) error
   259  
   260  	// DeleteBond deletes a bond which should generally be expired.
   261  	DeleteBond(assetID uint32, coinID []byte) error
   262  
   263  	FetchPrepaidBond(bondCoinID []byte) (strength uint32, lockTime int64, err error)
   264  	DeletePrepaidBond(coinID []byte) error
   265  	StorePrepaidBonds(coinIDs [][]byte, strength uint32, lockTime int64) error
   266  
   267  	// AccountInfo returns data for an account.
   268  	AccountInfo(account.AccountID) (*Account, error)
   269  }
   270  
   271  // MatchData represents an order pair match, but with just the order IDs instead
   272  // of the full orders. The actual orders may be retrieved by ID.
   273  type MatchData struct {
   274  	ID        order.MatchID
   275  	Taker     order.OrderID
   276  	TakerAcct account.AccountID
   277  	TakerAddr string
   278  	TakerSell bool
   279  	Maker     order.OrderID
   280  	MakerAcct account.AccountID
   281  	MakerAddr string
   282  	Epoch     order.EpochID
   283  	Quantity  uint64
   284  	Rate      uint64
   285  	BaseRate  uint64
   286  	QuoteRate uint64
   287  	Active    bool              // match negotiation in progress, not yet completed or failed
   288  	Status    order.MatchStatus // note that failed swaps, where Active=false, can have any status
   289  }
   290  
   291  // MatchDataWithCoins pairs MatchData (embedded) with the encode swap and redeem
   292  // coin IDs blobs for both maker and taker.
   293  type MatchDataWithCoins struct {
   294  	MatchData
   295  	MakerSwapCoin   []byte
   296  	MakerRedeemCoin []byte
   297  	TakerSwapCoin   []byte
   298  	TakerRedeemCoin []byte
   299  }
   300  
   301  // MatchStatus is the current status of a match, its known contracts and coin
   302  // IDs, and its secret, if known.
   303  type MatchStatus struct {
   304  	ID            order.MatchID
   305  	Status        order.MatchStatus
   306  	MakerContract []byte
   307  	TakerContract []byte
   308  	MakerSwap     []byte
   309  	TakerSwap     []byte
   310  	MakerRedeem   []byte
   311  	TakerRedeem   []byte
   312  	Secret        []byte
   313  	Active        bool
   314  	TakerSell     bool
   315  	IsTaker       bool
   316  	IsMaker       bool
   317  }
   318  
   319  // SwapData contains the data generated by the clients during swap negotiation.
   320  type SwapData struct {
   321  	SigMatchAckMaker []byte
   322  	SigMatchAckTaker []byte
   323  	ContractA        []byte // contains the secret hash used by both parties
   324  	ContractACoinID  []byte
   325  	ContractATime    int64
   326  	ContractAAckSig  []byte // B's signature of contract A data
   327  	ContractB        []byte
   328  	ContractBCoinID  []byte
   329  	ContractBTime    int64
   330  	ContractBAckSig  []byte // A's signature of contract B data
   331  	RedeemACoinID    []byte
   332  	RedeemASecret    []byte // the secret revealed in A's redeem, also used in B's redeem
   333  	RedeemATime      int64
   334  	RedeemAAckSig    []byte // B's signature of redeem A data
   335  	RedeemBCoinID    []byte
   336  	RedeemBTime      int64
   337  }
   338  
   339  // SwapDataFull combines a MatchData, SwapData, and the Base/Quote asset IDs.
   340  type SwapDataFull struct {
   341  	Base, Quote uint32
   342  	*MatchData
   343  	*SwapData
   344  }
   345  
   346  // MarketMatchID designates a MatchID for a certain market by the market's
   347  // base-quote asset IDs.
   348  type MarketMatchID struct {
   349  	order.MatchID
   350  	Base, Quote uint32 // market
   351  }
   352  
   353  // MatchID constructs a MarketMatchID from an order.Match.
   354  func MatchID(match *order.Match) MarketMatchID {
   355  	return MarketMatchID{
   356  		MatchID: match.ID(),
   357  		Base:    match.Maker.BaseAsset, // same for taker's redeem as BaseAsset refers to the market
   358  		Quote:   match.Maker.QuoteAsset,
   359  	}
   360  }
   361  
   362  // MatchOutcome pairs an inactive match's status with a timestamp. In the case
   363  // of a successful match for the user, this is when their redeem was received.
   364  // In the case of an at-fault match failure for the user, this corresponds to
   365  // the time of the previous match action. The previous action times are: match
   366  // time, swap txn validated times, and initiator redeem validated time. Note
   367  // that this does not directly correspond to match revocation times where
   368  // inaction deadline references the time when the swap txns reach the required
   369  // confirms. These times must match the reference times provided to the auth
   370  // manager when registering new swap outcomes.
   371  type MatchOutcome struct {
   372  	Status      order.MatchStatus
   373  	ID          order.MatchID
   374  	Fail        bool // taker must reach MatchComplete, maker succeeds at MakerRedeemed
   375  	Time        int64
   376  	Value       uint64
   377  	Base, Quote uint32 // the market
   378  }
   379  
   380  // MatchFail is a failed match and the effect on the user's score
   381  type MatchFail struct {
   382  	ID     order.MatchID
   383  	Status order.MatchStatus
   384  }
   385  
   386  // MatchArchiver is the interface required for storage and retrieval of all
   387  // match data.
   388  type MatchArchiver interface {
   389  	InsertMatch(match *order.Match) error
   390  	MatchByID(mid order.MatchID, base, quote uint32) (*MatchData, error)
   391  	UserMatches(aid account.AccountID, base, quote uint32) ([]*MatchData, error)
   392  	CompletedAndAtFaultMatchStats(aid account.AccountID, lastN int) ([]*MatchOutcome, error)
   393  	UserMatchFails(aid account.AccountID, lastN int) ([]*MatchFail, error)
   394  	ForgiveMatchFail(mid order.MatchID) (bool, error)
   395  	AllActiveUserMatches(aid account.AccountID) ([]*MatchData, error)
   396  	MarketMatches(base, quote uint32) ([]*MatchDataWithCoins, error)
   397  	MarketMatchesStreaming(base, quote uint32, includeInactive bool, N int64, f func(*MatchDataWithCoins) error) (int, error)
   398  	MatchStatuses(aid account.AccountID, base, quote uint32, matchIDs []order.MatchID) ([]*MatchStatus, error)
   399  }
   400  
   401  // SwapArchiver is the interface required for storage and retrieval of swap
   402  // counterparty data.
   403  //
   404  // In the swap process, the counterparties are:
   405  // - Initiator or party A on chain X. This is the maker in the DEX.
   406  // - Participant or party B on chain Y. This is the taker in the DEX.
   407  //
   408  // For each match, a successful swap will generate the following data that must
   409  // be stored:
   410  //   - 5 client signatures. Both parties sign the data to acknowledge (1) the
   411  //     match ack, and (2) the counterparty's contract script and contract
   412  //     transaction. Plus the taker acks the maker's redemption transaction.
   413  //   - 2 swap contracts and the associated transaction outputs (more generally,
   414  //     coinIDs), one on each party's blockchain.
   415  //   - 2 redemption transaction outputs (coinIDs).
   416  //
   417  // The methods for saving this data are defined below in the order in which the
   418  // data is expected from the parties.
   419  type SwapArchiver interface {
   420  	// ActiveSwaps loads the full details for all active swaps across all markets.
   421  	ActiveSwaps() ([]*SwapDataFull, error)
   422  
   423  	// SwapData retrieves the swap/match status and the current SwapData.
   424  	SwapData(mid MarketMatchID) (order.MatchStatus, *SwapData, error)
   425  
   426  	// Match acknowledgement message signatures.
   427  
   428  	// SaveMatchAckSigA records the match data acknowledgement signature from
   429  	// swap party A (the initiator), which is the maker in the DEX.
   430  	SaveMatchAckSigA(mid MarketMatchID, sig []byte) error
   431  
   432  	// SaveMatchAckSigB records the match data acknowledgement signature from
   433  	// swap party B (the participant), which is the taker in the DEX.
   434  	SaveMatchAckSigB(mid MarketMatchID, sig []byte) error
   435  
   436  	// Swap contracts, and counterparty audit acknowledgement signatures.
   437  
   438  	// SaveContractA records party A's swap contract script and the coinID (e.g.
   439  	// transaction output) containing the contract on chain X. Note that this
   440  	// contract contains the secret hash.
   441  	SaveContractA(mid MarketMatchID, contract []byte, coinID []byte, timestamp int64) error
   442  
   443  	// SaveAuditAckSigB records party B's signature acknowledging their audit of
   444  	// A's swap contract.
   445  	SaveAuditAckSigB(mid MarketMatchID, sig []byte) error
   446  
   447  	// SaveContractB records party B's swap contract script and the coinID (e.g.
   448  	// transaction output) containing the contract on chain Y.
   449  	SaveContractB(mid MarketMatchID, contract []byte, coinID []byte, timestamp int64) error
   450  
   451  	// SaveAuditAckSigA records party A's signature acknowledging their audit of
   452  	// B's swap contract.
   453  	SaveAuditAckSigA(mid MarketMatchID, sig []byte) error
   454  
   455  	// Redemption transactions, and counterparty acknowledgement signatures.
   456  
   457  	// SaveRedeemA records party A's redemption coinID (e.g. transaction
   458  	// output), which spends party B's swap contract on chain Y. Note that this
   459  	// transaction will contain the secret, which party B extracts.
   460  	SaveRedeemA(mid MarketMatchID, coinID, secret []byte, timestamp int64) error
   461  
   462  	// SaveRedeemAckSigB records party B's signature acknowledging party A's
   463  	// redemption, which spent their swap contract on chain Y and revealed the
   464  	// secret.
   465  	SaveRedeemAckSigB(mid MarketMatchID, sig []byte) error
   466  
   467  	// SaveRedeemB records party B's redemption coinID (e.g. transaction
   468  	// output), which spends party A's swap contract on chain X. This should
   469  	// also flag the match as inactive.
   470  	SaveRedeemB(mid MarketMatchID, coinID []byte, timestamp int64) error
   471  
   472  	// SetMatchInactive sets the swap as done/inactive. This can be because of a
   473  	// failed or successfully completed swap, but in practice this will be used
   474  	// for failed swaps since SaveRedeemB flags the swap as done/inactive. If
   475  	// the match is being marked as inactive prior to MatchComplete (the match
   476  	// was revoked) but the user is not at fault, the forgive bool may be set to
   477  	// true so the outcome will not count against the user who would have the
   478  	// next action in the swap.
   479  	SetMatchInactive(mid MarketMatchID, forgive bool) error
   480  }
   481  
   482  // ValidateOrder ensures that the order with the given status for the specified
   483  // market is sensible. This function is in the database package because the
   484  // concept of a valid order-status-market state is dependent on the semantics of
   485  // order archival. The ServerTime may not be set yet, so the OrderID cannot be
   486  // computed.
   487  func ValidateOrder(ord order.Order, status order.OrderStatus, mkt *dex.MarketInfo) bool {
   488  	// Orders with status OrderStatusUnknown should never reach the database.
   489  	if status == order.OrderStatusUnknown {
   490  		return false
   491  	}
   492  
   493  	// Bad MarketInfo!
   494  	if mkt.Base == mkt.Quote {
   495  		panic("MarketInfo specifies market with same base and quote assets")
   496  	}
   497  
   498  	return order.ValidateOrder(ord, status, mkt.LotSize) == nil
   499  }
   500  
   501  // EpochGapNA is a specifier for an epoch gap (epochs between limit order and
   502  // cancel order) when such a designation doesn't apply in-context. For instance,
   503  // revocations are treated in many places like cancel orders, but there is no
   504  // reason to consider the epoch gap.
   505  const EpochGapNA int32 = -1
   506  
   507  // CancelRecord is info about a cancel order and when it matched.
   508  type CancelRecord struct {
   509  	ID        order.OrderID
   510  	TargetID  order.OrderID
   511  	MatchTime int64
   512  	// EpochGap is the number of epochs passed since the targeted trade order
   513  	// was placed, where 0 means canceled in the same epoch, 1 means canceled in
   514  	// the next epoch, etc.
   515  	EpochGap int32
   516  }