decred.org/dcrdex@v1.0.5/dex/order/serialize.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  	"bytes"
     9  	"fmt"
    10  
    11  	"decred.org/dcrdex/dex/encode"
    12  	"decred.org/dcrdex/server/account"
    13  )
    14  
    15  var uint32B = encode.Uint32Bytes
    16  var uint64B = encode.Uint64Bytes
    17  var intCoder = encode.IntCoder
    18  var bEqual = bytes.Equal
    19  
    20  // EncodePrefix encodes the order Prefix to a versioned blob.
    21  func EncodePrefix(p *Prefix) []byte {
    22  	return encode.BuildyBytes{0}.
    23  		AddData(p.AccountID[:]).
    24  		AddData(uint32B(p.BaseAsset)).
    25  		AddData(uint32B(p.QuoteAsset)).
    26  		AddData([]byte{byte(p.OrderType)}).
    27  		AddData(uint64B(uint64(p.ClientTime.UnixMilli()))).
    28  		AddData(uint64B(uint64(p.ServerTime.UnixMilli()))).
    29  		AddData(p.Commit[:])
    30  }
    31  
    32  // DecodePrefix decodes the versioned blob to a *Prefix.
    33  func DecodePrefix(b []byte) (prefix *Prefix, err error) {
    34  	ver, pushes, err := encode.DecodeBlob(b, 7)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	switch ver {
    39  	case 0:
    40  		return decodePrefix_v0(pushes)
    41  	}
    42  	return nil, fmt.Errorf("unknown Prefix version %d", ver)
    43  }
    44  
    45  // decodePrefix_v0 decodes the v0 payload into a *Prefix.
    46  func decodePrefix_v0(pushes [][]byte) (prefix *Prefix, err error) {
    47  	if len(pushes) != 7 {
    48  		return nil, fmt.Errorf("expected 6 prefix parts, got %d", len(pushes))
    49  	}
    50  	acctB, baseB, quoteB := pushes[0], pushes[1], pushes[2]
    51  	oTypeB, cTimeB, sTimeB := pushes[3], pushes[4], pushes[5]
    52  	commitB := pushes[6]
    53  
    54  	if len(acctB) != account.HashSize {
    55  		return nil, fmt.Errorf("expected account ID length %d, got %d",
    56  			account.HashSize, len(acctB))
    57  	}
    58  	var acctID account.AccountID
    59  	copy(acctID[:], acctB)
    60  
    61  	if len(commitB) != CommitmentSize {
    62  		return nil, fmt.Errorf("expected commitment length %d, got %d",
    63  			CommitmentSize, len(commitB))
    64  	}
    65  	var commit Commitment
    66  	copy(commit[:], commitB)
    67  
    68  	return &Prefix{
    69  		AccountID:  acctID,
    70  		BaseAsset:  intCoder.Uint32(baseB),
    71  		QuoteAsset: intCoder.Uint32(quoteB),
    72  		OrderType:  OrderType(oTypeB[0]),
    73  		ClientTime: encode.DecodeUTime(cTimeB),
    74  		ServerTime: encode.DecodeUTime(sTimeB),
    75  		Commit:     commit,
    76  	}, nil
    77  }
    78  
    79  // EncodeTrade encodes the MarketOrder information, but not the Prefix
    80  // structure.
    81  func EncodeTrade(ord *Trade) []byte {
    82  	sell := encode.ByteFalse
    83  	if ord.Sell {
    84  		sell = encode.ByteTrue
    85  	}
    86  	coins := encode.BuildyBytes{}
    87  	for _, coin := range ord.Coins {
    88  		coins = coins.AddData(coin)
    89  	}
    90  	return encode.BuildyBytes{0}.
    91  		AddData(coins).
    92  		AddData(sell).
    93  		AddData(uint64B(ord.Quantity)).
    94  		AddData([]byte(ord.Address)).
    95  		AddData(uint64B(ord.Filled()))
    96  }
    97  
    98  // DecodeTrade decodes the versioned-blob market order, but does not populate
    99  // the embedded Prefix.
   100  func DecodeTrade(b []byte) (trade *Trade, err error) {
   101  	ver, pushes, err := encode.DecodeBlob(b, 5)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	switch ver {
   106  	case 0:
   107  		return decodeTrade_v0(pushes)
   108  	}
   109  	return nil, fmt.Errorf("unknown MarketOrder version %d", ver)
   110  }
   111  
   112  // decodeTrade_v0 decodes the version 0 payload into a MarketOrder, but
   113  // does not populate the embedded Prefix.
   114  func decodeTrade_v0(pushes [][]byte) (mrkt *Trade, err error) {
   115  	if len(pushes) != 5 {
   116  		return nil, fmt.Errorf("expected 5 pushes, got %d", len(pushes))
   117  	}
   118  	coinsB, sellB, qtyB, addrB, filledB := pushes[0], pushes[1], pushes[2], pushes[3], pushes[4]
   119  	rawCoins, err := encode.ExtractPushes(coinsB)
   120  	if err != nil {
   121  		return nil, fmt.Errorf("error extracting coins: %w", err)
   122  	}
   123  	coins := make([]CoinID, 0, len(rawCoins))
   124  	for _, coin := range rawCoins {
   125  		coins = append(coins, coin)
   126  	}
   127  	sell := true
   128  	if bEqual(sellB, encode.ByteFalse) {
   129  		sell = false
   130  	}
   131  	if len(qtyB) != 8 {
   132  		return nil, fmt.Errorf("quantity bytes length %v, expected 8", len(qtyB))
   133  	}
   134  	if len(filledB) != 8 {
   135  		return nil, fmt.Errorf("filled amount bytes length %v, expected 8", len(filledB))
   136  	}
   137  	return &Trade{
   138  		Coins:    coins,
   139  		Sell:     sell,
   140  		Quantity: intCoder.Uint64(qtyB),
   141  		Address:  string(addrB),
   142  		FillAmt:  intCoder.Uint64(filledB),
   143  	}, nil
   144  }
   145  
   146  // EncodeMatch encodes the UserMatch to bytes suitable for binary storage or
   147  // communications.
   148  func EncodeMatch(match *UserMatch) []byte {
   149  	return encode.BuildyBytes{1}.
   150  		AddData(match.OrderID[:]).
   151  		AddData(match.MatchID[:]).
   152  		AddData(uint64B(match.Quantity)).
   153  		AddData(uint64B(match.Rate)).
   154  		AddData([]byte(match.Address)).
   155  		AddData([]byte{byte(match.Status)}).
   156  		AddData([]byte{byte(match.Side)}).
   157  		AddData(uint64B(match.FeeRateSwap))
   158  }
   159  
   160  // DecodeMatch decodes the versioned blob into a UserMatch.
   161  func DecodeMatch(b []byte) (match *UserMatch, ver uint8, err error) {
   162  	var pushes [][]byte
   163  	ver, pushes, err = encode.DecodeBlob(b, 8)
   164  	if err != nil {
   165  		return nil, 0, err
   166  	}
   167  	switch ver {
   168  	case 0, 1: // same encoding, just a flag
   169  		match, err = matchDecoder_v0(pushes)
   170  		return
   171  	}
   172  	return nil, 0, fmt.Errorf("unknown UserMatch version %d", ver)
   173  }
   174  
   175  // matchDecoder_v0 decodes the version 0 (and 1) payload into a *UserMatch.
   176  func matchDecoder_v0(pushes [][]byte) (*UserMatch, error) {
   177  	if len(pushes) != 8 {
   178  		return nil, fmt.Errorf("matchDecoder_v0: expected 8 pushes, got %d", len(pushes))
   179  	}
   180  	oidB, midB := pushes[0], pushes[1]
   181  	if len(oidB) != OrderIDSize {
   182  		return nil, fmt.Errorf("matchDecoder_v0: expected length %d order ID, got %d", OrderIDSize, len(oidB))
   183  	}
   184  	if len(midB) != MatchIDSize {
   185  		return nil, fmt.Errorf("matchDecoder_v0: expected length %d match ID, got %d", MatchIDSize, len(midB))
   186  	}
   187  	var oid OrderID
   188  	copy(oid[:], oidB)
   189  	var mid MatchID
   190  	copy(mid[:], midB)
   191  
   192  	statusB, sideB := pushes[5], pushes[6]
   193  	if len(statusB) != 1 || len(sideB) != 1 {
   194  		return nil, fmt.Errorf("matchDecoder_v0: status/side incorrect length %d/%d", len(statusB), len(sideB))
   195  	}
   196  	qtyB, rateB, swapFeeB := pushes[2], pushes[3], pushes[7]
   197  	if len(qtyB) != 8 || len(rateB) != 8 || len(swapFeeB) != 8 {
   198  		return nil, fmt.Errorf("matchDecoder_v0: quantity/rate/swapFee incorrect length %d/%d/%d",
   199  			len(qtyB), len(rateB), len(swapFeeB))
   200  	}
   201  	return &UserMatch{
   202  		OrderID:     oid,
   203  		MatchID:     mid,
   204  		Quantity:    intCoder.Uint64(qtyB),
   205  		Rate:        intCoder.Uint64(rateB),
   206  		Address:     string(pushes[4]),
   207  		Status:      MatchStatus(statusB[0]),
   208  		Side:        MatchSide(sideB[0]),
   209  		FeeRateSwap: intCoder.Uint64(swapFeeB),
   210  	}, nil
   211  }
   212  
   213  // Length-1 byte slices used as flags to indicate common order constants.
   214  var (
   215  	orderTypeLimit    = []byte{'l'}
   216  	orderTypeMarket   = []byte{'m'}
   217  	orderTypeCancel   = []byte{'c'}
   218  	orderTifImmediate = []byte{'i'}
   219  	orderTifStanding  = []byte{'s'}
   220  )
   221  
   222  // EncodeOrder encodes the order to bytes suitable for wire communications or
   223  // database storage. EncodeOrder accepts any type of Order.
   224  func EncodeOrder(ord Order) []byte {
   225  	switch o := ord.(type) {
   226  	case *LimitOrder:
   227  		tif := orderTifStanding
   228  		if o.Force == ImmediateTiF {
   229  			tif = orderTifImmediate
   230  		}
   231  		return encode.BuildyBytes{0}.
   232  			AddData(orderTypeLimit).
   233  			AddData(EncodePrefix(&o.P)).
   234  			AddData(EncodeTrade(&o.T)).
   235  			AddData(encode.BuildyBytes{}.
   236  				AddData(uint64B(o.Rate)).
   237  				AddData(tif),
   238  			)
   239  	case *MarketOrder:
   240  		return encode.BuildyBytes{0}.
   241  			AddData(orderTypeMarket).
   242  			AddData(EncodePrefix(&o.P)).
   243  			AddData(EncodeTrade(&o.T))
   244  	case *CancelOrder:
   245  		return encode.BuildyBytes{0}.
   246  			AddData(orderTypeCancel).
   247  			AddData(EncodePrefix(&o.P)).
   248  			AddData(o.TargetOrderID[:])
   249  	default:
   250  		panic("encodeOrder: unknown order type")
   251  	}
   252  }
   253  
   254  // DecodeOrder decodes the byte-encoded order. DecodeOrder accepts any type of
   255  // order.
   256  func DecodeOrder(b []byte) (ord Order, err error) {
   257  	ver, pushes, err := encode.DecodeBlob(b, 4) // pushes actually depends on order type
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  	switch ver {
   262  	case 0:
   263  		return decodeOrder_v0(pushes)
   264  	}
   265  	return nil, fmt.Errorf("unknown Order version %d", ver)
   266  }
   267  
   268  // decodeOrder_v0 decodes the version 0 payload into an Order.
   269  func decodeOrder_v0(pushes [][]byte) (Order, error) {
   270  	if len(pushes) == 0 {
   271  		return nil, fmt.Errorf("decodeOrder_v0: zero pushes for order")
   272  	}
   273  	// The first push should be the order type.
   274  	oType := pushes[0]
   275  	pushes = pushes[1:]
   276  	switch {
   277  	case bEqual(oType, orderTypeLimit):
   278  		if len(pushes) != 3 {
   279  			return nil, fmt.Errorf("decodeOrder_v0: expected 3 pushes for limit order, got %d", len(pushes))
   280  		}
   281  		prefixB, tradeB, limitFlagsB := pushes[0], pushes[1], pushes[2]
   282  		prefix, err := DecodePrefix(prefixB)
   283  		if err != nil {
   284  			return nil, err
   285  		}
   286  		trade, err := DecodeTrade(tradeB)
   287  		if err != nil {
   288  			return nil, err
   289  		}
   290  		flags, err := encode.ExtractPushes(limitFlagsB)
   291  		if err != nil {
   292  			return nil, fmt.Errorf("decodeOrder_v0: error extracting limit flags: %w", err)
   293  		}
   294  		if len(flags) != 2 {
   295  			return nil, fmt.Errorf("decodeOrder_v0: expected 2 error flags, got %d", len(flags))
   296  		}
   297  		rateB, tifB := flags[0], flags[1]
   298  		tif := ImmediateTiF
   299  		if bEqual(tifB, orderTifStanding) {
   300  			tif = StandingTiF
   301  		}
   302  		return &LimitOrder{
   303  			P:     *prefix,
   304  			T:     *trade.Copy(),
   305  			Rate:  intCoder.Uint64(rateB),
   306  			Force: tif,
   307  		}, nil
   308  
   309  	case bEqual(oType, orderTypeMarket):
   310  		if len(pushes) != 2 {
   311  			return nil, fmt.Errorf("decodeOrder_v0: expected 2 pushes for market order, got %d", len(pushes))
   312  		}
   313  		prefixB, tradeB := pushes[0], pushes[1]
   314  		prefix, err := DecodePrefix(prefixB)
   315  		if err != nil {
   316  			return nil, err
   317  		}
   318  		trade, err := DecodeTrade(tradeB)
   319  		if err != nil {
   320  			return nil, err
   321  		}
   322  		return &MarketOrder{
   323  			P: *prefix,
   324  			T: *trade.Copy(),
   325  		}, nil
   326  
   327  	case bEqual(oType, orderTypeCancel):
   328  		if len(pushes) != 2 {
   329  			return nil, fmt.Errorf("decodeOrder_v0: expected 2 pushes for cancel order, got %d", len(pushes))
   330  		}
   331  		prefixB, targetB := pushes[0], pushes[1]
   332  		prefix, err := DecodePrefix(prefixB)
   333  		if err != nil {
   334  			return nil, err
   335  		}
   336  		if len(targetB) != OrderIDSize {
   337  			return nil, fmt.Errorf("decodeOrder_v0: expected order ID of size %d, got %d", OrderIDSize, len(targetB))
   338  		}
   339  		var oID OrderID
   340  		copy(oID[:], targetB)
   341  		return &CancelOrder{
   342  			P:             *prefix,
   343  			TargetOrderID: oID,
   344  		}, nil
   345  
   346  	default:
   347  		return nil, fmt.Errorf("decodeOrder_v0: unknown order type %x", oType)
   348  	}
   349  }