OSDN Git Service

7d013ec8126771561ccc75c3795978c3e333ee59
[bytom/vapor.git] / application / mov / match / match_test.go
1 package match
2
3 import (
4         "testing"
5
6         "github.com/bytom/vapor/application/mov/common"
7         "github.com/bytom/vapor/application/mov/mock"
8         "github.com/bytom/vapor/protocol/bc"
9         "github.com/bytom/vapor/protocol/bc/types"
10         "github.com/bytom/vapor/protocol/validation"
11         "github.com/bytom/vapor/testutil"
12 )
13
14 func TestGenerateMatchedTxs(t *testing.T) {
15         btc2eth := &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH}
16         eth2btc := &common.TradePair{FromAssetID: &mock.ETH, ToAssetID: &mock.BTC}
17         eth2eos := &common.TradePair{FromAssetID: &mock.ETH, ToAssetID: &mock.EOS}
18         eos2btc := &common.TradePair{FromAssetID: &mock.EOS, ToAssetID: &mock.BTC}
19
20         cases := []struct {
21                 desc            string
22                 tradePairs      []*common.TradePair
23                 initStoreOrders []*common.Order
24                 wantMatchedTxs  []*types.Tx
25         }{
26                 {
27                         desc:       "full matched",
28                         tradePairs: []*common.TradePair{btc2eth, eth2btc},
29                         initStoreOrders: []*common.Order{
30                                 mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
31                                 mock.Eth2BtcOrders[0],
32                         },
33                         wantMatchedTxs: []*types.Tx{
34                                 mock.MatchedTxs[1],
35                         },
36                 },
37                 {
38                         desc:       "partial matched",
39                         tradePairs: []*common.TradePair{btc2eth, eth2btc},
40                         initStoreOrders: []*common.Order{
41                                 mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
42                                 mock.Eth2BtcOrders[1],
43                         },
44                         wantMatchedTxs: []*types.Tx{
45                                 mock.MatchedTxs[0],
46                         },
47                 },
48                 {
49                         desc:       "partial matched and continue to match",
50                         tradePairs: []*common.TradePair{btc2eth, eth2btc},
51                         initStoreOrders: []*common.Order{
52                                 mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
53                                 mock.Eth2BtcOrders[2],
54                         },
55                         wantMatchedTxs: []*types.Tx{
56                                 mock.MatchedTxs[2],
57                                 mock.MatchedTxs[3],
58                         },
59                 },
60                 {
61                         desc:       "unable to match",
62                         tradePairs: []*common.TradePair{btc2eth, eth2btc},
63                         initStoreOrders: []*common.Order{
64                                 mock.Btc2EthOrders[1],
65                                 mock.Eth2BtcOrders[0],
66                         },
67                         wantMatchedTxs: []*types.Tx{},
68                 },
69                 {
70                         desc:       "cycle match",
71                         tradePairs: []*common.TradePair{btc2eth, eth2eos, eos2btc},
72                         initStoreOrders: []*common.Order{
73                                 mock.Btc2EthOrders[0], mock.Eth2EosOrders[0], mock.Eos2BtcOrders[0],
74                         },
75                         wantMatchedTxs: []*types.Tx{
76                                 mock.MatchedTxs[6],
77                         },
78                 },
79         }
80
81         for i, c := range cases {
82                 movStore := mock.NewMovStore([]*common.TradePair{btc2eth, eth2btc}, c.initStoreOrders)
83                 matchEngine := NewEngine(NewOrderBook(movStore, nil, nil), 0.05, mock.NodeProgram)
84                 var gotMatchedTxs []*types.Tx
85                 for matchEngine.HasMatchedTx(c.tradePairs...) {
86                         matchedTx, err := matchEngine.NextMatchedTx(c.tradePairs...)
87                         if err != nil {
88                                 t.Fatal(err)
89                         }
90
91                         gotMatchedTxs = append(gotMatchedTxs, matchedTx)
92                 }
93
94                 if len(c.wantMatchedTxs) != len(gotMatchedTxs) {
95                         t.Errorf("#%d(%s) the length of got matched tx is not equals want matched tx", i, c.desc)
96                         continue
97                 }
98
99                 for i, gotMatchedTx := range gotMatchedTxs {
100                         if _, err := validation.ValidateTx(gotMatchedTx.Tx, &bc.Block{BlockHeader: &bc.BlockHeader{Version: 1}}); err != nil {
101                                 t.Fatal(err)
102                         }
103
104                         c.wantMatchedTxs[i].Version = 1
105                         byteData, err := c.wantMatchedTxs[i].MarshalText()
106                         if err != nil {
107                                 t.Fatal(err)
108                         }
109
110                         c.wantMatchedTxs[i].SerializedSize = uint64(len(byteData))
111                         wantMatchedTx := types.NewTx(c.wantMatchedTxs[i].TxData)
112                         if gotMatchedTx.ID != wantMatchedTx.ID {
113                                 t.Errorf("#%d(%s) the tx hash of got matched tx: %s is not equals want matched tx: %s", i, c.desc, gotMatchedTx.ID.String(), wantMatchedTx.ID.String())
114                         }
115                 }
116         }
117 }
118
119 func TestCalcMatchedTxFee(t *testing.T) {
120         cases := []struct {
121                 desc             string
122                 tx               *types.TxData
123                 maxFeeRate       float64
124                 wantMatchedTxFee map[bc.AssetID]*MatchedTxFee
125         }{
126                 {
127                         desc:             "fee less than max fee",
128                         maxFeeRate:       0.05,
129                         wantMatchedTxFee: map[bc.AssetID]*MatchedTxFee{mock.ETH: {FeeAmount: 10, MaxFeeAmount: 26}},
130                         tx:               &mock.MatchedTxs[1].TxData,
131                 },
132                 {
133                         desc:             "fee refund in tx",
134                         maxFeeRate:       0.05,
135                         wantMatchedTxFee: map[bc.AssetID]*MatchedTxFee{mock.ETH: {FeeAmount: 27, MaxFeeAmount: 27}},
136                         tx:               &mock.MatchedTxs[2].TxData,
137                 },
138                 {
139                         desc:             "fee is zero",
140                         maxFeeRate:       0.05,
141                         wantMatchedTxFee: map[bc.AssetID]*MatchedTxFee{},
142                         tx:               &mock.MatchedTxs[0].TxData,
143                 },
144         }
145
146         for i, c := range cases {
147                 gotMatchedTxFee, err := CalcMatchedTxFee(c.tx, c.maxFeeRate)
148                 if err != nil {
149                         t.Fatal(err)
150                 }
151
152                 if !testutil.DeepEqual(gotMatchedTxFee, c.wantMatchedTxFee) {
153                         t.Errorf("#%d(%s):fail to caculate matched tx fee, got (%v), want (%v)", i, c.desc, gotMatchedTxFee, c.wantMatchedTxFee)
154                 }
155         }
156 }
157
158 func TestValidateTradePairs(t *testing.T) {
159         cases := []struct {
160                 desc       string
161                 tradePairs []*common.TradePair
162                 wantError  bool
163         }{
164                 {
165                         desc: "valid case of two trade pairs",
166                         tradePairs: []*common.TradePair{
167                                 {
168                                         FromAssetID: &mock.BTC,
169                                         ToAssetID:   &mock.ETH,
170                                 },
171                                 {
172                                         FromAssetID: &mock.ETH,
173                                         ToAssetID:   &mock.BTC,
174                                 },
175                         },
176                         wantError: false,
177                 },
178                 {
179                         desc: "invalid case of two trade pairs",
180                         tradePairs: []*common.TradePair{
181                                 {
182                                         FromAssetID: &mock.BTC,
183                                         ToAssetID:   &mock.ETH,
184                                 },
185                                 {
186                                         FromAssetID: &mock.ETH,
187                                         ToAssetID:   &mock.EOS,
188                                 },
189                         },
190                         wantError: true,
191                 },
192                 {
193                         desc: "valid case of three trade pairs",
194                         tradePairs: []*common.TradePair{
195                                 {
196                                         FromAssetID: &mock.BTC,
197                                         ToAssetID:   &mock.ETH,
198                                 },
199                                 {
200                                         FromAssetID: &mock.ETH,
201                                         ToAssetID:   &mock.EOS,
202                                 },
203                                 {
204                                         FromAssetID: &mock.EOS,
205                                         ToAssetID:   &mock.BTC,
206                                 },
207                         },
208                         wantError: false,
209                 },
210                 {
211                         desc: "invalid case of three trade pairs",
212                         tradePairs: []*common.TradePair{
213                                 {
214                                         FromAssetID: &mock.BTC,
215                                         ToAssetID:   &mock.ETH,
216                                 },
217                                 {
218                                         FromAssetID: &mock.ETH,
219                                         ToAssetID:   &mock.BTC,
220                                 },
221                                 {
222                                         FromAssetID: &mock.BTC,
223                                         ToAssetID:   &mock.BTC,
224                                 },
225                         },
226                         wantError: true,
227                 },
228                 {
229                         desc: "valid case 2 of three trade pairs",
230                         tradePairs: []*common.TradePair{
231                                 {
232                                         FromAssetID: &mock.BTC,
233                                         ToAssetID:   &mock.ETH,
234                                 },
235                                 {
236                                         FromAssetID: &mock.EOS,
237                                         ToAssetID:   &mock.BTC,
238                                 },
239                                 {
240                                         FromAssetID: &mock.ETH,
241                                         ToAssetID:   &mock.EOS,
242                                 },
243                         },
244                         wantError: false,
245                 },
246         }
247
248         for i, c := range cases {
249                 err := validateTradePairs(c.tradePairs)
250                 if c.wantError && err == nil {
251                         t.Errorf("#%d(%s): want error, got no error", i, c.desc)
252                 }
253
254                 if !c.wantError && err != nil {
255                         t.Errorf("#%d(%s): want no error, got error (%v)", i, c.desc, err)
256                 }
257         }
258 }
259
260 func TestIsMatched(t *testing.T) {
261         cases := []struct {
262                 desc        string
263                 orders      []*common.Order
264                 wantMatched bool
265         }{
266                 {
267                         desc: "ratio is equals",
268                         orders: []*common.Order{
269                                 {
270                                         FromAssetID:      &mock.BTC,
271                                         ToAssetID:        &mock.ETH,
272                                         RatioNumerator:   6250,
273                                         RatioDenominator: 3,
274                                 },
275                                 {
276                                         FromAssetID:      &mock.ETH,
277                                         ToAssetID:        &mock.BTC,
278                                         RatioNumerator:   3,
279                                         RatioDenominator: 6250,
280                                 },
281                         },
282                         wantMatched: true,
283                 },
284                 {
285                         desc: "ratio is equals, and numerator and denominator are multiples of the opposite",
286                         orders: []*common.Order{
287                                 {
288                                         FromAssetID:      &mock.BTC,
289                                         ToAssetID:        &mock.ETH,
290                                         RatioNumerator:   6250,
291                                         RatioDenominator: 3,
292                                 },
293                                 {
294                                         FromAssetID:      &mock.ETH,
295                                         ToAssetID:        &mock.BTC,
296                                         RatioNumerator:   9,
297                                         RatioDenominator: 18750,
298                                 },
299                         },
300                         wantMatched: true,
301                 },
302                 {
303                         desc: "matched with a slight diff",
304                         orders: []*common.Order{
305                                 {
306                                         FromAssetID:      &mock.BTC,
307                                         ToAssetID:        &mock.ETH,
308                                         RatioNumerator:   62500000000000000,
309                                         RatioDenominator: 30000000000001,
310                                 },
311                                 {
312                                         FromAssetID:      &mock.ETH,
313                                         ToAssetID:        &mock.BTC,
314                                         RatioNumerator:   3,
315                                         RatioDenominator: 6250,
316                                 },
317                         },
318                         wantMatched: true,
319                 },
320                 {
321                         desc: "not matched with a slight diff",
322                         orders: []*common.Order{
323                                 {
324                                         FromAssetID:      &mock.BTC,
325                                         ToAssetID:        &mock.ETH,
326                                         RatioNumerator:   62500000000000001,
327                                         RatioDenominator: 30000000000000,
328                                 },
329                                 {
330                                         FromAssetID:      &mock.ETH,
331                                         ToAssetID:        &mock.BTC,
332                                         RatioNumerator:   3,
333                                         RatioDenominator: 6250,
334                                 },
335                         },
336                         wantMatched: false,
337                 },
338                 {
339                         desc: "ring matched",
340                         orders: []*common.Order{
341                                 {
342                                         FromAssetID:      &mock.BTC,
343                                         ToAssetID:        &mock.ETH,
344                                         RatioNumerator:   2000000003,
345                                         RatioDenominator: 100000001,
346                                 },
347                                 {
348                                         FromAssetID:      &mock.ETH,
349                                         ToAssetID:        &mock.EOS,
350                                         RatioNumerator:   400000000001,
351                                         RatioDenominator: 2000000003,
352                                 },
353                                 {
354                                         FromAssetID:      &mock.EOS,
355                                         ToAssetID:        &mock.BTC,
356                                         RatioNumerator:   100000001,
357                                         RatioDenominator: 400000000001,
358                                 },
359                         },
360                         wantMatched: true,
361                 },
362                 {
363                         desc: "ring matched with a slight diff",
364                         orders: []*common.Order{
365                                 {
366                                         FromAssetID:      &mock.BTC,
367                                         ToAssetID:        &mock.ETH,
368                                         RatioNumerator:   2000000003,
369                                         RatioDenominator: 100000001,
370                                 },
371                                 {
372                                         FromAssetID:      &mock.ETH,
373                                         ToAssetID:        &mock.EOS,
374                                         RatioNumerator:   400000000001,
375                                         RatioDenominator: 2000000003,
376                                 },
377                                 {
378                                         FromAssetID:      &mock.EOS,
379                                         ToAssetID:        &mock.BTC,
380                                         RatioNumerator:   100000000,
381                                         RatioDenominator: 400000000001,
382                                 },
383                         },
384                         wantMatched: true,
385                 },
386                 {
387                         desc: "ring fail matched with a slight diff",
388                         orders: []*common.Order{
389                                 {
390                                         FromAssetID:      &mock.BTC,
391                                         ToAssetID:        &mock.ETH,
392                                         RatioNumerator:   2000000003,
393                                         RatioDenominator: 100000001,
394                                 },
395                                 {
396                                         FromAssetID:      &mock.ETH,
397                                         ToAssetID:        &mock.EOS,
398                                         RatioNumerator:   400000000001,
399                                         RatioDenominator: 2000000003,
400                                 },
401                                 {
402                                         FromAssetID:      &mock.EOS,
403                                         ToAssetID:        &mock.BTC,
404                                         RatioNumerator:   100000002,
405                                         RatioDenominator: 400000000001,
406                                 },
407                         },
408                         wantMatched: false,
409                 },
410         }
411
412         for i, c := range cases {
413                 gotMatched := IsMatched(c.orders)
414                 if gotMatched != c.wantMatched {
415                         t.Errorf("#%d(%s) got matched:%v, want matched:%v", i, c.desc, gotMatched, c.wantMatched)
416                 }
417         }
418 }