decred.org/dcrdex@v1.0.5/dex/order/order.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 order defines the Order and Match types used throughout the DEX.
     5  package order
     6  
     7  import (
     8  	"database/sql"
     9  	"database/sql/driver"
    10  	"encoding/binary"
    11  	"encoding/hex"
    12  	"encoding/json"
    13  	"fmt"
    14  	"sync"
    15  	"time"
    16  
    17  	"decred.org/dcrdex/server/account"
    18  	"github.com/decred/dcrd/crypto/blake256"
    19  )
    20  
    21  // Several types including OrderID and Commitment are defined as a Blake256
    22  // hash. Define an internal hash type and hashSize for convenience.
    23  const hashSize = blake256.Size // 32
    24  type hash = [hashSize]byte
    25  
    26  // OrderIDSize defines the length in bytes of an OrderID.
    27  const OrderIDSize = hashSize
    28  
    29  // OrderID is the unique identifier for each order. It is defined as the
    30  // Blake256 hash of the serialized order.
    31  type OrderID hash
    32  
    33  // IDFromHex decodes an OrderID from a hexadecimal string.
    34  func IDFromHex(sid string) (OrderID, error) {
    35  	if len(sid) > OrderIDSize*2 {
    36  		return OrderID{}, fmt.Errorf("invalid order ID. too long %d > %d", len(sid), OrderIDSize*2)
    37  	}
    38  	oidB, err := hex.DecodeString(sid)
    39  	if err != nil {
    40  		return OrderID{}, fmt.Errorf("order ID decode error: %w", err)
    41  	}
    42  	var oid OrderID
    43  	copy(oid[OrderIDSize-len(oidB):], oidB)
    44  	return oid, nil
    45  }
    46  
    47  // IDFromBytes converts a byte slice to an OrderID.
    48  func IDFromBytes(b []byte) (OrderID, error) {
    49  	if len(b) != OrderIDSize {
    50  		return OrderID{}, fmt.Errorf("invalid order ID. wanted length %d but got %d", OrderIDSize, len(b))
    51  	}
    52  	var oid OrderID
    53  	copy(oid[:], b)
    54  	return oid, nil
    55  }
    56  
    57  // String returns a hexadecimal representation of the OrderID. String implements
    58  // fmt.Stringer.
    59  func (oid OrderID) String() string {
    60  	return hex.EncodeToString(oid[:])
    61  }
    62  
    63  // MarshalJSON satisfies the json.Marshaller interface, and will marshal the
    64  // id to a hex string.
    65  func (oid OrderID) MarshalJSON() ([]byte, error) {
    66  	return json.Marshal(oid.String())
    67  }
    68  
    69  // Bytes returns the order ID as a []byte.
    70  func (oid OrderID) Bytes() []byte {
    71  	return oid[:]
    72  }
    73  
    74  // Value implements the sql/driver.Valuer interface.
    75  func (oid OrderID) Value() (driver.Value, error) {
    76  	return oid[:], nil // []byte
    77  }
    78  
    79  // Scan implements the sql.Scanner interface.
    80  func (oid *OrderID) Scan(src any) error {
    81  	switch src := src.(type) {
    82  	case []byte:
    83  		copy(oid[:], src)
    84  		return nil
    85  		//case string:
    86  		// case nil:
    87  		// 	*oid = nil
    88  		// 	return nil
    89  	}
    90  
    91  	return fmt.Errorf("cannot convert %T to OrderID", src)
    92  }
    93  
    94  var zeroOrderID OrderID
    95  
    96  // IsZero returns true if the order ID is zeros.
    97  func (oid OrderID) IsZero() bool {
    98  	return oid == zeroOrderID
    99  }
   100  
   101  // OrderType distinguishes the different kinds of orders (e.g. limit, market,
   102  // cancel).
   103  type OrderType uint8
   104  
   105  // The different OrderType values.
   106  const (
   107  	UnknownOrderType OrderType = iota
   108  	LimitOrderType
   109  	MarketOrderType
   110  	CancelOrderType
   111  )
   112  
   113  // Value implements the sql/driver.Valuer interface.
   114  func (ot OrderType) Value() (driver.Value, error) {
   115  	return int64(ot), nil
   116  }
   117  
   118  // Scan implements the sql.Scanner interface.
   119  func (ot *OrderType) Scan(src any) error {
   120  	// Use sql.(NullInt32).Scan because it uses the unexported
   121  	// sql.convertAssignRows to coerce compatible types.
   122  	v := new(sql.NullInt32)
   123  	if err := v.Scan(src); err != nil {
   124  		return err
   125  	}
   126  	*ot = OrderType(v.Int32)
   127  	return nil
   128  }
   129  
   130  // String returns a string representation of the OrderType.
   131  func (ot OrderType) String() string {
   132  	switch ot {
   133  	case LimitOrderType:
   134  		return "limit"
   135  	case MarketOrderType:
   136  		return "market"
   137  	case CancelOrderType:
   138  		return "cancel"
   139  	default:
   140  		return "unknown"
   141  	}
   142  }
   143  
   144  // TimeInForce indicates how limit order execution is to be handled. That is,
   145  // when the order is not immediately matched during processing of the order's
   146  // epoch, the order may become a standing order or be revoked without a fill.
   147  type TimeInForce uint8
   148  
   149  // The TimeInForce is either ImmediateTiF, which prevents the order from
   150  // becoming a standing order if there is no match during epoch processing, or
   151  // StandingTiF, which allows limit orders to enter the order book if not
   152  // immediately matched during epoch processing.
   153  const (
   154  	ImmediateTiF TimeInForce = iota
   155  	StandingTiF
   156  )
   157  
   158  // String satisfies the Stringer interface.
   159  func (t TimeInForce) String() string {
   160  	switch t {
   161  	case ImmediateTiF:
   162  		return "immediate"
   163  	case StandingTiF:
   164  		return "standing"
   165  	}
   166  	return fmt.Sprintf("unknown (%d)", t)
   167  }
   168  
   169  // Order specifies the methods required for a type to function as a DEX order.
   170  // See the concrete implementations of MarketOrder, LimitOrder, and CancelOrder.
   171  type Order interface {
   172  	// Prefix returns the order *Prefix.
   173  	Prefix() *Prefix
   174  
   175  	// Trade returns the order *Trade if a limit or market order, else nil.
   176  	Trade() *Trade
   177  
   178  	// ID computes the Order's ID from its serialization. Serialization is
   179  	// detailed in the 'Client Order Management' section of the DEX
   180  	// specification.
   181  	ID() OrderID
   182  
   183  	// UID gives the string representation of the order ID. It is named to
   184  	// reflect the intent of providing a unique identifier.
   185  	UID() string
   186  
   187  	// User gives the user's account ID.
   188  	User() account.AccountID
   189  
   190  	// Serialize marshals the order. Serialization is detailed in the 'Client
   191  	// Order Management' section of the DEX specification.
   192  	Serialize() []byte
   193  
   194  	// Type indicates the Order's type (e.g. LimitOrder, MarketOrder, etc.).
   195  	Type() OrderType
   196  
   197  	// Time returns the Order's server time in milliseconds, when it was received by the server.
   198  	Time() int64
   199  
   200  	// SetTime sets the ServerTime field of the prefix.
   201  	SetTime(time.Time)
   202  
   203  	// Base returns the unique integer identifier of the base asset as defined
   204  	// in the asset package.
   205  	Base() uint32
   206  
   207  	// Quote returns the unique integer identifier of the quote asset as defined
   208  	// in the asset package.
   209  	Quote() uint32
   210  
   211  	// Commitment returns the order's preimage commitment.
   212  	Commitment() Commitment
   213  }
   214  
   215  // zeroTime is the Unix time for a Time where IsZero() == true.
   216  var zeroTime = time.Time{}.UnixMilli()
   217  
   218  // An order's ID is computed as the Blake-256 hash of the serialized order.
   219  func calcOrderID(order Order) OrderID {
   220  	sTime := order.Time()
   221  	if sTime == zeroTime {
   222  		panic("Order's ServerTime is unset")
   223  	}
   224  	return blake256.Sum256(order.Serialize())
   225  }
   226  
   227  // CoinID should be used to wrap a []byte so that it may be used as a map key.
   228  type CoinID []byte
   229  
   230  func (c CoinID) String() string {
   231  	return hex.EncodeToString(c)
   232  }
   233  
   234  // CommitmentSize is the length of the Commitment, a 32-byte Blake-256 hash
   235  // according to the DEX specification.
   236  const CommitmentSize = hashSize
   237  
   238  // Commitment is the Blake-256 hash of the Preimage.
   239  type Commitment hash
   240  
   241  var zeroCommit Commitment
   242  
   243  // IsZero indicates if the Commitment is the zero-value for the type.
   244  func (c *Commitment) IsZero() bool {
   245  	return *c == zeroCommit
   246  }
   247  
   248  // Value implements the sql/driver.Valuer interface. The zero-value Commitment
   249  // returns nil rather than a byte slice of zeros.
   250  func (c Commitment) Value() (driver.Value, error) {
   251  	if c.IsZero() {
   252  		return nil, nil // nil => NULL
   253  	}
   254  	return c[:], nil // []byte => BYTEA
   255  }
   256  
   257  // Scan implements the sql.Scanner interface. NULL table values are scanned as
   258  // the zero-value Commitment.
   259  func (c *Commitment) Scan(src any) error {
   260  	switch src := src.(type) {
   261  	case []byte:
   262  		copy(c[:], src)
   263  		return nil
   264  	case nil: // NULL in the table
   265  		*c = Commitment{}
   266  		return nil
   267  	}
   268  
   269  	return fmt.Errorf("cannot convert %T to Commitment", src)
   270  }
   271  
   272  // String returns a hexadecimal representation of the Commitment. String
   273  // implements fmt.Stringer.
   274  func (c Commitment) String() string {
   275  	return hex.EncodeToString(c[:])
   276  }
   277  
   278  // PreimageSize defines the length of the preimage, which is a 32-byte value
   279  // according to the DEX specification.
   280  const PreimageSize = 32
   281  
   282  // Preimage represents the 32-byte preimage as a byte slice.
   283  type Preimage [PreimageSize]byte
   284  
   285  // Commit computes the preimage commitment as the Blake-256 hash of the
   286  // Preimage.
   287  func (pi *Preimage) Commit() Commitment {
   288  	return blake256.Sum256(pi[:])
   289  }
   290  
   291  // Value implements the sql/driver.Valuer interface.
   292  func (pi Preimage) Value() (driver.Value, error) {
   293  	return pi[:], nil // []byte
   294  }
   295  
   296  // Scan implements the sql.Scanner interface.
   297  func (pi *Preimage) Scan(src any) error {
   298  	switch src := src.(type) {
   299  	case []byte:
   300  		copy(pi[:], src)
   301  		return nil
   302  	case nil: // NULL in the table
   303  		*pi = Preimage{}
   304  		return nil
   305  	}
   306  
   307  	return fmt.Errorf("cannot convert %T to Preimage", src)
   308  }
   309  
   310  // IsZero checks if the Preimage is the zero Preimage.
   311  func (pi *Preimage) IsZero() bool {
   312  	return *pi == Preimage{}
   313  }
   314  
   315  // Prefix is the order prefix containing data fields common to all orders.
   316  type Prefix struct {
   317  	AccountID  account.AccountID
   318  	BaseAsset  uint32
   319  	QuoteAsset uint32
   320  	OrderType  OrderType
   321  	ClientTime time.Time
   322  	ServerTime time.Time
   323  	Commit     Commitment
   324  
   325  	id *OrderID // cache of the order's OrderID
   326  }
   327  
   328  // P is an alias for Prefix. Embedding with the alias allows us to define a
   329  // method on the interface called Prefix that returns the *Prefix.
   330  type P = Prefix
   331  
   332  func (p *Prefix) Prefix() *Prefix {
   333  	return p
   334  }
   335  
   336  // PrefixLen is the length in bytes of the serialized order Prefix.
   337  const PrefixLen = account.HashSize + 4 + 4 + 1 + 8 + 8 + CommitmentSize
   338  
   339  // serializeSize returns the length of the serialized order Prefix.
   340  func (p *Prefix) serializeSize() int {
   341  	return PrefixLen
   342  }
   343  
   344  // Time returns the order prefix's server time as a UNIX epoch time in
   345  // milliseconds.
   346  func (p *Prefix) Time() int64 {
   347  	return p.ServerTime.UnixMilli()
   348  }
   349  
   350  // SetTime sets the order prefix's server time.
   351  func (p *Prefix) SetTime(t time.Time) {
   352  	p.ServerTime = t.UTC()
   353  	// SetTime should only ever be called once in practice, but in case it is
   354  	// necessary to restamp the ServerTime, clear any computed OrderID.
   355  	p.id = nil
   356  }
   357  
   358  // User gives the user's account ID.
   359  func (p *Prefix) User() account.AccountID {
   360  	return p.AccountID
   361  }
   362  
   363  // Serialize marshals the Prefix into a []byte.
   364  func (p *Prefix) Serialize() []byte {
   365  	b := make([]byte, PrefixLen)
   366  
   367  	// account ID
   368  	offset := len(p.AccountID)
   369  	copy(b[:offset], p.AccountID[:])
   370  
   371  	// base asset
   372  	binary.BigEndian.PutUint32(b[offset:offset+4], p.BaseAsset)
   373  	offset += 4
   374  
   375  	// quote asset
   376  	binary.BigEndian.PutUint32(b[offset:offset+4], p.QuoteAsset)
   377  	offset += 4
   378  
   379  	// order type (e.g. market, limit, cancel)
   380  	b[offset] = uint8(p.OrderType)
   381  	offset++
   382  
   383  	// client time
   384  	binary.BigEndian.PutUint64(b[offset:offset+8], uint64(p.ClientTime.UnixMilli()))
   385  	offset += 8
   386  
   387  	// server time
   388  	binary.BigEndian.PutUint64(b[offset:offset+8], uint64(p.ServerTime.UnixMilli()))
   389  	offset += 8
   390  
   391  	// commitment
   392  	copy(b[offset:offset+CommitmentSize], p.Commit[:])
   393  
   394  	return b
   395  }
   396  
   397  // Base returns the base asset integer ID.
   398  func (p *Prefix) Base() uint32 {
   399  	return p.BaseAsset
   400  }
   401  
   402  // Quote returns the quote asset integer ID.
   403  func (p *Prefix) Quote() uint32 {
   404  	return p.QuoteAsset
   405  }
   406  
   407  // Type returns the order type.
   408  func (p *Prefix) Type() OrderType {
   409  	return p.OrderType
   410  }
   411  
   412  // Commitment returns the order Commitment.
   413  func (p *Prefix) Commitment() Commitment {
   414  	return p.Commit
   415  }
   416  
   417  // Trade is information about a trade-type order. Both limit and market orders
   418  // are trade-type orders.
   419  type Trade struct {
   420  	Coins    []CoinID
   421  	Sell     bool
   422  	Quantity uint64
   423  	Address  string // TODO: provide later, maybe in the match ack
   424  
   425  	// FillAmt is not part of the order's serialization.
   426  	fillAmtMtx sync.RWMutex
   427  	FillAmt    uint64 // use Filled and AddFill methods for thread-safe access
   428  }
   429  
   430  // Copy makes a shallow copy of a Trade. This is useful when attempting to
   431  // assign a newly-created trade to an order's field without a linter warning
   432  // about copying a mutex (e.g. MarketOrder{T: *aNewTrade.Copy()}).
   433  func (t *Trade) Copy() *Trade {
   434  	return &Trade{
   435  		Coins:    t.Coins, // shallow
   436  		Sell:     t.Sell,
   437  		Quantity: t.Quantity,
   438  		Address:  t.Address,
   439  		FillAmt:  t.FillAmt,
   440  	}
   441  }
   442  
   443  // T is an alias for Trade. Embedding with the alias allows us to define a
   444  // method on the interface called Trade that returns the *Trade.
   445  type T = Trade
   446  
   447  // Trade returns a pointer to the orders embedded Trade.
   448  func (t *Trade) Trade() *Trade {
   449  	return t
   450  }
   451  
   452  // Remaining returns the remaining order amount.
   453  func (t *Trade) Remaining() uint64 {
   454  	t.fillAmtMtx.RLock()
   455  	defer t.fillAmtMtx.RUnlock()
   456  	return t.Quantity - t.FillAmt
   457  }
   458  
   459  // Filled returns the filled amount.
   460  func (t *Trade) Filled() uint64 {
   461  	t.fillAmtMtx.RLock()
   462  	defer t.fillAmtMtx.RUnlock()
   463  	return t.FillAmt
   464  }
   465  
   466  // AddFill increases the filled amount.
   467  func (t *Trade) AddFill(amt uint64) {
   468  	t.fillAmtMtx.Lock()
   469  	t.FillAmt += amt
   470  	t.fillAmtMtx.Unlock()
   471  }
   472  
   473  // SetFill sets the filled amount.
   474  func (t *Trade) SetFill(amt uint64) {
   475  	t.fillAmtMtx.Lock()
   476  	t.FillAmt = amt
   477  	t.fillAmtMtx.Unlock()
   478  }
   479  
   480  // SwapAddress returns the order's payment address.
   481  func (t *Trade) SwapAddress() string {
   482  	return t.Address
   483  }
   484  
   485  // FromAccount is the account that the order originates from. Only useful for
   486  // account-based assets. Use of this method assumes that account coin has
   487  // already been added.
   488  func (t *Trade) FromAccount() string {
   489  	if len(t.Coins) == 0 {
   490  		return "no coins?"
   491  	}
   492  	// The coin ID should be the UTF-8 encoded address string, not the address
   493  	// byte-array. t.Coins[0] = []byte(addrStr).
   494  	return string(t.Coins[0])
   495  }
   496  
   497  // ToAccount is the account that the order pays to. Only useful for
   498  // account-based assets.
   499  func (t *Trade) ToAccount() string {
   500  	return t.Address
   501  }
   502  
   503  // BaseAccount is the account address associated with the base asset for the
   504  // order. Only useful for account-based assets. Use of this method assumes that
   505  // account coin has already been added (when sell = true).
   506  func (t *Trade) BaseAccount() string {
   507  	if t.Sell {
   508  		return t.FromAccount()
   509  	}
   510  	return t.ToAccount()
   511  }
   512  
   513  // QuoteAccount is the account address associated with the quote asset for the
   514  // order. Only useful for account-based assets. Use of this method assumes that
   515  // account coin has already been added (when sell = false).
   516  func (t *Trade) QuoteAccount() string {
   517  	if t.Sell {
   518  		return t.ToAccount()
   519  	}
   520  	return t.FromAccount()
   521  }
   522  
   523  // serializeSize returns the length of the serialized Trade.
   524  func (t *Trade) serializeSize() int {
   525  	// Compute the size of the serialized Coin IDs.
   526  	var coinSz int
   527  	for _, coinID := range t.Coins {
   528  		coinSz += len(coinID)
   529  		// TODO: ensure all Coin IDs have the same size, indicating the same asset?
   530  	}
   531  	// The serialized order includes a byte for coin count, but this is implicit
   532  	// in coin slice length.
   533  	return 1 + coinSz + 1 + 8 + len(t.Address)
   534  }
   535  
   536  // Serialize marshals the Trade into a []byte.
   537  func (t *Trade) Serialize() []byte {
   538  	b := make([]byte, t.serializeSize())
   539  	offset := 0
   540  
   541  	// Coin count
   542  	b[offset] = uint8(len(t.Coins))
   543  	offset++
   544  
   545  	// Coins
   546  	for _, coinID := range t.Coins {
   547  		coinSz := len(coinID)
   548  		copy(b[offset:offset+coinSz], coinID)
   549  		offset += coinSz
   550  	}
   551  
   552  	// order side
   553  	var side uint8
   554  	if t.Sell {
   555  		side = 1
   556  	}
   557  	b[offset] = side
   558  	offset++
   559  
   560  	// order quantity
   561  	binary.BigEndian.PutUint64(b[offset:offset+8], t.Quantity)
   562  	offset += 8
   563  
   564  	// client address for received funds
   565  	copy(b[offset:offset+len(t.Address)], []byte(t.Address))
   566  	return b
   567  }
   568  
   569  // MarketOrder defines a market order in terms of a Prefix and the order
   570  // details, including the backing Coins, the order direction/side, order
   571  // quantity, and the address where the matched client will send funds. The order
   572  // quantity is in atoms of the base asset, and must be an integral multiple of
   573  // the asset's lot size, except for Market buy orders when it is in units of the
   574  // quote asset and is not bound by integral lot size multiple constraints.
   575  type MarketOrder struct {
   576  	P
   577  	T
   578  }
   579  
   580  // ID computes the order ID.
   581  func (o *MarketOrder) ID() OrderID {
   582  	if o.id != nil {
   583  		return *o.id
   584  	}
   585  	id := calcOrderID(o)
   586  	o.id = &id
   587  	return id
   588  }
   589  
   590  // UID computes the order ID, returning the string representation.
   591  func (o *MarketOrder) UID() string {
   592  	return o.ID().String()
   593  }
   594  
   595  // String is the same as UID. It is defined to satisfy Stringer.
   596  func (o *MarketOrder) String() string {
   597  	return o.UID()
   598  }
   599  
   600  // serializeSize returns the length of the serialized MarketOrder.
   601  func (o *MarketOrder) serializeSize() int {
   602  	// The serialized order includes a byte for coin count, but this is implicit
   603  	// in coin slice length.
   604  	return o.P.serializeSize() + o.T.serializeSize()
   605  }
   606  
   607  // Serialize marshals the LimitOrder into a []byte.
   608  func (o *MarketOrder) Serialize() []byte {
   609  	b := make([]byte, o.serializeSize())
   610  	// Prefix and data common with MarketOrder
   611  	offset := o.P.serializeSize()
   612  	copy(b[:offset], o.P.Serialize())
   613  	tradeLen := o.T.serializeSize()
   614  	copy(b[offset:offset+tradeLen], o.T.Serialize())
   615  	return b
   616  }
   617  
   618  // Ensure MarketOrder is an Order.
   619  var _ Order = (*MarketOrder)(nil)
   620  
   621  // LimitOrder defines a limit order in terms of a MarketOrder and limit-specific
   622  // data including rate (price) and time in force.
   623  type LimitOrder struct {
   624  	P
   625  	T
   626  	Rate  uint64 // price as atoms of quote asset, applied per 1e8 units of the base asset
   627  	Force TimeInForce
   628  }
   629  
   630  // ID computes the order ID.
   631  func (o *LimitOrder) ID() OrderID {
   632  	if o.id != nil {
   633  		return *o.id
   634  	}
   635  	id := calcOrderID(o)
   636  	o.id = &id
   637  	return id
   638  }
   639  
   640  // UID computes the order ID, returning the string representation.
   641  func (o *LimitOrder) UID() string {
   642  	return o.ID().String()
   643  }
   644  
   645  // String is the same as UID. It is defined to satisfy Stringer.
   646  func (o *LimitOrder) String() string {
   647  	return o.UID()
   648  }
   649  
   650  // serializeSize returns the length of the serialized LimitOrder.
   651  func (o *LimitOrder) serializeSize() int {
   652  	return o.P.serializeSize() + o.T.serializeSize() + 8 + 1
   653  }
   654  
   655  // Serialize marshals the LimitOrder into a []byte.
   656  func (o *LimitOrder) Serialize() []byte {
   657  	b := make([]byte, o.serializeSize())
   658  	// Prefix and data common with MarketOrder
   659  	offset := o.P.serializeSize()
   660  	copy(b[:offset], o.P.Serialize())
   661  	tradeLen := o.T.serializeSize()
   662  	copy(b[offset:offset+tradeLen], o.T.Serialize())
   663  	offset += tradeLen
   664  
   665  	// Price rate in atoms of quote asset
   666  	binary.BigEndian.PutUint64(b[offset:offset+8], o.Rate)
   667  	offset += 8
   668  
   669  	// Time in force
   670  	b[offset] = uint8(o.Force)
   671  	return b
   672  }
   673  
   674  // Ensure LimitOrder is an Order.
   675  var _ Order = (*LimitOrder)(nil)
   676  
   677  // Price returns the limit order's price rate.
   678  func (o *LimitOrder) Price() uint64 {
   679  	return o.Rate
   680  }
   681  
   682  // CancelOrder defines a cancel order in terms of an order Prefix and the ID of
   683  // the order to be canceled.
   684  type CancelOrder struct {
   685  	P
   686  	TargetOrderID OrderID
   687  }
   688  
   689  // ID computes the order ID.
   690  func (o *CancelOrder) ID() OrderID {
   691  	if o.id != nil {
   692  		return *o.id
   693  	}
   694  	id := calcOrderID(o)
   695  	o.id = &id
   696  	return id
   697  }
   698  
   699  // UID computes the order ID, returning the string representation.
   700  func (o *CancelOrder) UID() string {
   701  	return o.ID().String()
   702  }
   703  
   704  // Trade returns a pointer to the orders embedded Trade.
   705  func (o *CancelOrder) Trade() *Trade {
   706  	return nil
   707  }
   708  
   709  // String is the same as UID. It is defined to satisfy Stringer.
   710  func (o *CancelOrder) String() string {
   711  	return o.UID()
   712  }
   713  
   714  // serializeSize returns the length of the serialized CancelOrder.
   715  func (o *CancelOrder) serializeSize() int {
   716  	return o.P.serializeSize() + OrderIDSize
   717  }
   718  
   719  // Serialize marshals the CancelOrder into a []byte.
   720  func (o *CancelOrder) Serialize() []byte {
   721  	return append(o.P.Serialize(), o.TargetOrderID[:]...)
   722  }
   723  
   724  // Ensure CancelOrder is an Order.
   725  var _ Order = (*CancelOrder)(nil)
   726  
   727  // ValidateOrder ensures that the order with the given status for the specified
   728  // market is sensible. The ServerTime may not be set yet, so the OrderID cannot
   729  // be computed.
   730  func ValidateOrder(ord Order, status OrderStatus, lotSize uint64) error {
   731  	if ord.Base() == ord.Quote() {
   732  		return fmt.Errorf("same asset specified for base and quote")
   733  	}
   734  
   735  	// Each order type has different rules about status and lot size.
   736  	switch ot := ord.(type) {
   737  	case *MarketOrder:
   738  		// Market orders OK statuses: epoch and executed (NOT booked or
   739  		// canceled).
   740  		switch status {
   741  		case OrderStatusEpoch, OrderStatusExecuted, OrderStatusRevoked:
   742  		default:
   743  			return fmt.Errorf("invalid market order status %d -> %s", status, status)
   744  		}
   745  
   746  		if ot.OrderType != MarketOrderType {
   747  			return fmt.Errorf("market order has wrong order type %d -> %s", ot.OrderType, ot.OrderType)
   748  		}
   749  
   750  		// Market sell orders must respect lot size. Market buy orders must be
   751  		// of an amount sufficiently buffered beyond the minimum standing sell
   752  		// order's lot cost, but that is enforced by the order router.
   753  		if ot.Sell && (ot.Quantity%lotSize != 0 || ot.Remaining()%lotSize != 0) {
   754  			return fmt.Errorf("market sell order fails lot size requirement %d %% %d = %d", ot.Quantity, lotSize, ot.Quantity%lotSize)
   755  		}
   756  
   757  	case *CancelOrder:
   758  		// Cancel order OK statuses: epoch, executed (NOT booked or canceled),
   759  		// and revoked. Revoked status indicates the cancel order is
   760  		// server-generated and corresponds to a revoked trade order.
   761  		switch status {
   762  		case OrderStatusEpoch, OrderStatusExecuted, OrderStatusRevoked: // orderStatusFailed if we decide to export that
   763  		default:
   764  			return fmt.Errorf("invalid cancel order status %d -> %s", status, status)
   765  		}
   766  
   767  		if ot.OrderType != CancelOrderType {
   768  			return fmt.Errorf("cancel order has wrong order type %d -> %s", ot.OrderType, ot.OrderType)
   769  		}
   770  
   771  	case *LimitOrder:
   772  		// Limit order OK statuses: epoch, booked, executed, and canceled (same
   773  		// as market plus booked).
   774  		switch status {
   775  		case OrderStatusEpoch, OrderStatusExecuted, OrderStatusRevoked:
   776  		case OrderStatusBooked, OrderStatusCanceled:
   777  			// Immediate time in force limit orders may not be canceled, and may
   778  			// not be in the order book.
   779  			if ot.Force == ImmediateTiF {
   780  				return fmt.Errorf("invalid immediate limit order status %d -> %s", status, status)
   781  			}
   782  		default:
   783  			return fmt.Errorf("invalid limit order status %d -> %s", status, status)
   784  		}
   785  
   786  		if ot.OrderType != LimitOrderType {
   787  			return fmt.Errorf("limit order has wrong order type %d -> %s", ot.OrderType, ot.OrderType)
   788  		}
   789  
   790  		// All limit orders must respect lot size.
   791  		if ot.Quantity%lotSize != 0 || ot.Remaining()%lotSize != 0 {
   792  			return fmt.Errorf("limit order fails lot size requirement %d %% %d = %d", ot.Quantity, lotSize, ot.Quantity%lotSize)
   793  		}
   794  	default:
   795  		// cannot validate an unknown order type
   796  		return fmt.Errorf("unknown order type")
   797  	}
   798  
   799  	return nil
   800  }
   801  
   802  // ExtractAddress extracts the address from the order. If the order is a cancel
   803  // order, an empty string is returned.
   804  func ExtractAddress(ord Order) string {
   805  	trade := ord.Trade()
   806  	if trade == nil {
   807  		return ""
   808  	}
   809  	return trade.Address
   810  }