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 }