9 "github.com/bytom/vapor/application/mov/common"
10 "github.com/bytom/vapor/application/mov/database"
11 "github.com/bytom/vapor/application/mov/match"
12 "github.com/bytom/vapor/application/mov/mock"
13 "github.com/bytom/vapor/consensus"
14 dbm "github.com/bytom/vapor/database/leveldb"
15 "github.com/bytom/vapor/protocol/bc"
16 "github.com/bytom/vapor/protocol/bc/types"
17 "github.com/bytom/vapor/protocol/vm"
18 "github.com/bytom/vapor/testutil"
21 var initBlockHeader = &types.BlockHeader{Height: 1, PreviousBlockHash: bc.Hash{}}
23 func TestApplyBlock(t *testing.T) {
28 initOrders []*common.Order
29 wantOrders []*common.Order
30 wantDBState *common.MovDatabaseState
34 desc: "apply block has pending order transaction",
36 BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
37 Transactions: []*types.Tx{
38 mock.Btc2EthMakerTxs[0], mock.Eth2BtcMakerTxs[0],
41 blockFunc: applyBlock,
42 wantOrders: []*common.Order{mock.MustNewOrderFromOutputV2(mock.Btc2EthMakerTxs[0], 0, 2, 0), mock.MustNewOrderFromOutputV2(mock.Eth2BtcMakerTxs[0], 0, 2, 1)},
43 wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
46 desc: "apply block has two different trade pairs & different trade pair won't affect each order",
48 BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
49 Transactions: []*types.Tx{
50 mock.Btc2EthMakerTxs[0],
51 mock.Eth2BtcMakerTxs[0],
52 mock.Eos2EtcMakerTxs[0],
53 mock.Eth2EosMakerTxs[0],
56 blockFunc: applyBlock,
57 wantOrders: []*common.Order{
58 mock.MustNewOrderFromOutputV2(mock.Btc2EthMakerTxs[0], 0, 2, 0),
59 mock.MustNewOrderFromOutputV2(mock.Eth2BtcMakerTxs[0], 0, 2, 1),
60 mock.MustNewOrderFromOutputV2(mock.Eos2EtcMakerTxs[0], 0, 2, 2),
61 mock.MustNewOrderFromOutputV2(mock.Eth2EosMakerTxs[0], 0, 2, 3),
63 wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
66 desc: "apply block has full matched transaction",
68 BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
69 Transactions: []*types.Tx{
73 blockFunc: applyBlock,
74 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[0]},
75 wantOrders: []*common.Order{mock.Btc2EthOrders[1]},
76 wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
79 desc: "apply block has partial matched transaction",
81 BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
82 Transactions: []*types.Tx{
86 blockFunc: applyBlock,
87 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[1]},
88 wantOrders: []*common.Order{mock.MustNewOrderFromOutputV2(mock.MatchedTxs[0], 1, 2, 0)},
89 wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
92 desc: "apply block has two partial matched transaction",
94 BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
95 Transactions: []*types.Tx{
96 mock.MatchedTxs[2], mock.MatchedTxs[3],
99 blockFunc: applyBlock,
100 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
101 wantOrders: []*common.Order{mock.MustNewOrderFromOutputV2(mock.MatchedTxs[3], 1, 2, 1)},
102 wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
105 desc: "apply block has partial matched transaction by pending orders from tx pool",
107 BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
108 Transactions: []*types.Tx{
109 mock.Btc2EthMakerTxs[0],
110 mock.Eth2BtcMakerTxs[1],
114 blockFunc: applyBlock,
115 initOrders: []*common.Order{},
116 wantOrders: []*common.Order{mock.MustNewOrderFromOutputV2(mock.MatchedTxs[4], 1, 2, 2)},
117 wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
120 desc: "apply block which node packed maker tx and match transaction in random orde",
122 BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
123 Transactions: []*types.Tx{
124 mock.Eos2EtcMakerTxs[0],
125 mock.Btc2EthMakerTxs[0],
126 mock.Eth2BtcMakerTxs[1],
128 mock.Eth2EosMakerTxs[0],
129 mock.Etc2EosMakerTxs[0],
133 blockFunc: applyBlock,
134 initOrders: []*common.Order{},
135 wantOrders: []*common.Order{
136 mock.MustNewOrderFromOutputV2(mock.MatchedTxs[4], 1, 2, 3),
137 mock.MustNewOrderFromOutputV2(mock.Eth2EosMakerTxs[0], 0, 2, 4),
139 wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
142 desc: "apply block has partial matched transaction chain",
144 BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
145 Transactions: []*types.Tx{
146 mock.Btc2EthMakerTxs[0],
147 mock.Eth2BtcMakerTxs[1],
149 mock.Eth2BtcMakerTxs[0],
153 blockFunc: applyBlock,
154 initOrders: []*common.Order{},
155 wantOrders: []*common.Order{mock.MustNewOrderFromOutputV2(mock.MatchedTxs[7], 2, 2, 4)},
156 wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
159 desc: "detach block has pending order transaction",
161 BlockHeader: *initBlockHeader,
162 Transactions: []*types.Tx{
163 mock.Btc2EthMakerTxs[0], mock.Eth2BtcMakerTxs[1],
166 blockFunc: detachBlock,
167 initOrders: []*common.Order{mock.MustNewOrderFromOutput(mock.Btc2EthMakerTxs[0], 0), mock.MustNewOrderFromOutput(mock.Eth2BtcMakerTxs[1], 0)},
168 wantOrders: []*common.Order{},
169 wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
172 desc: "detach block has two different trade pairs & different trade pair won't affect each order",
174 BlockHeader: *initBlockHeader,
175 Transactions: []*types.Tx{
176 mock.Btc2EthMakerTxs[0],
177 mock.Eth2BtcMakerTxs[0],
178 mock.Eos2EtcMakerTxs[0],
179 mock.Eth2EosMakerTxs[0],
182 blockFunc: detachBlock,
183 initOrders: []*common.Order{
184 mock.MustNewOrderFromOutput(mock.Btc2EthMakerTxs[0], 0),
185 mock.MustNewOrderFromOutput(mock.Eth2BtcMakerTxs[0], 0),
186 mock.MustNewOrderFromOutput(mock.Eos2EtcMakerTxs[0], 0),
187 mock.MustNewOrderFromOutput(mock.Eth2EosMakerTxs[0], 0),
189 wantOrders: []*common.Order{},
190 wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
193 desc: "detach block has full matched transaction",
195 BlockHeader: *initBlockHeader,
196 Transactions: []*types.Tx{
200 blockFunc: detachBlock,
201 initOrders: []*common.Order{mock.Btc2EthOrders[1]},
202 wantOrders: []*common.Order{
203 orderWithHeightAndTxIndex(mock.Btc2EthOrders[0], 0, 0),
204 mock.Btc2EthOrders[1],
205 orderWithHeightAndTxIndex(mock.Eth2BtcOrders[0], 0, 0),
207 wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
210 desc: "detach block has partial matched transaction",
212 BlockHeader: *initBlockHeader,
213 Transactions: []*types.Tx{
217 blockFunc: detachBlock,
218 initOrders: []*common.Order{mock.MustNewOrderFromOutputV2(mock.MatchedTxs[0], 1, 1, 0)},
219 wantOrders: []*common.Order{
220 orderWithHeightAndTxIndex(mock.Btc2EthOrders[0], 0, 0),
221 orderWithHeightAndTxIndex(mock.Eth2BtcOrders[1], 0, 0),
223 wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
226 desc: "detach block has two partial matched transaction",
228 BlockHeader: *initBlockHeader,
229 Transactions: []*types.Tx{
230 mock.MatchedTxs[2], mock.MatchedTxs[3],
233 blockFunc: detachBlock,
234 initOrders: []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[3], 1)},
235 wantOrders: []*common.Order{
236 orderWithHeightAndTxIndex(mock.Btc2EthOrders[0], 0, 0),
237 orderWithHeightAndTxIndex(mock.Btc2EthOrders[1], 0, 0),
238 orderWithHeightAndTxIndex(mock.Eth2BtcOrders[2], 0, 0),
240 wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
243 desc: "detach block which node packed maker tx and match transaction in random orde",
245 BlockHeader: *initBlockHeader,
246 Transactions: []*types.Tx{
247 mock.Eos2EtcMakerTxs[0],
248 mock.Btc2EthMakerTxs[0],
250 mock.Eth2EosMakerTxs[0],
251 mock.Eth2BtcMakerTxs[1],
253 mock.Etc2EosMakerTxs[0],
256 blockFunc: detachBlock,
257 initOrders: []*common.Order{
258 mock.MustNewOrderFromOutput(mock.MatchedTxs[4], 1),
259 mock.MustNewOrderFromOutput(mock.Eth2EosMakerTxs[0], 0),
261 wantOrders: []*common.Order{},
262 wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
266 defer os.RemoveAll("temp")
267 for i, c := range cases {
268 testDB := dbm.NewDB("testdb", "leveldb", "temp")
269 store := database.NewLevelDBMovStore(testDB)
270 if err := store.InitDBState(0, &bc.Hash{}); err != nil {
274 if err := store.ProcessOrders(c.initOrders, nil, initBlockHeader); err != nil {
278 movCore := &Core{movStore: store}
279 if err := c.blockFunc(movCore, c.block); err != c.wantError {
280 t.Errorf("#%d(%s):want error(%v), got error(%v)", i, c.desc, c.wantError, err)
283 gotOrders := queryAllOrders(store)
284 if !ordersEquals(c.wantOrders, gotOrders) {
285 t.Errorf("#%d(%s):want orders(%v), got orders(%v)", i, c.desc, c.wantOrders, gotOrders)
288 dbState, err := store.GetMovDatabaseState()
293 if !testutil.DeepEqual(c.wantDBState, dbState) {
294 t.Errorf("#%d(%s):want db state(%v), got db state(%v)", i, c.desc, c.wantDBState, dbState)
302 func TestValidateBlock(t *testing.T) {
303 consensus.ActiveNetParams.MovRewardPrograms = []consensus.MovRewardProgram{
307 Program: hex.EncodeToString(mock.RewardProgram),
314 verifyResults []*bc.TxVerifyResult
318 desc: "block only has maker tx",
320 Transactions: []*types.Tx{
321 mock.Eth2BtcMakerTxs[0],
322 mock.Btc2EthMakerTxs[0],
325 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: false}},
329 desc: "block only has matched tx",
331 Transactions: []*types.Tx{
337 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: false}, {StatusFail: false}},
341 desc: "block has maker tx and matched tx",
343 Transactions: []*types.Tx{
344 mock.Eth2BtcMakerTxs[0],
345 mock.Btc2EthMakerTxs[0],
351 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: false}, {StatusFail: false}, {StatusFail: false}, {StatusFail: false}},
355 desc: "status fail of maker tx is true",
357 Transactions: []*types.Tx{
358 mock.Eth2BtcMakerTxs[0],
359 mock.Btc2EthMakerTxs[0],
362 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: true}},
363 wantError: errStatusFailMustFalse,
366 desc: "status fail of matched tx is true",
368 Transactions: []*types.Tx{
373 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: true}},
374 wantError: errStatusFailMustFalse,
377 desc: "asset id in matched tx is not unique",
379 Transactions: []*types.Tx{
380 types.NewTx(types.TxData{
381 Inputs: []*types.TxInput{
382 types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram),
383 types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *mock.Eth2BtcOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Eth2BtcOrders[0].Utxo.Amount, mock.Eth2BtcOrders[0].Utxo.SourcePos, mock.Eth2BtcOrders[0].Utxo.ControlProgram),
385 Outputs: []*types.TxOutput{
386 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("51")),
387 types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("53")),
389 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 11, mock.RewardProgram),
390 types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 1, mock.RewardProgram),
395 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: true}},
396 wantError: errAssetIDMustUniqueInMatchedTx,
399 desc: "common input in the matched tx",
401 Transactions: []*types.Tx{
402 types.NewTx(types.TxData{
403 Inputs: []*types.TxInput{
404 types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram),
405 types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *mock.Eth2BtcOrders[0].Utxo.SourceID, *mock.Eth2BtcOrders[0].FromAssetID, mock.Eth2BtcOrders[0].Utxo.Amount, mock.Eth2BtcOrders[0].Utxo.SourcePos, mock.Eth2BtcOrders[0].Utxo.ControlProgram),
406 types.NewSpendInput(nil, testutil.MustDecodeHash("28b7b53d8dc90006bf97e0a4eaae2a72ec3d869873188698b694beaf20789f21"), *consensus.BTMAssetID, 100, 0, []byte{0x51}),
408 Outputs: []*types.TxOutput{
409 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("51")),
410 types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("53")),
411 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 11, mock.RewardProgram),
412 types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 1, mock.RewardProgram),
413 types.NewIntraChainOutput(*consensus.BTMAssetID, 100, []byte{0x51}),
418 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
419 wantError: errInputProgramMustP2WMCScript,
422 desc: "cancel order in the matched tx",
424 Transactions: []*types.Tx{
425 types.NewTx(types.TxData{
426 Inputs: []*types.TxInput{
427 types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram),
428 types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *mock.Eth2BtcOrders[0].Utxo.SourceID, *mock.Eth2BtcOrders[0].FromAssetID, mock.Eth2BtcOrders[0].Utxo.Amount, mock.Eth2BtcOrders[0].Utxo.SourcePos, mock.Eth2BtcOrders[0].Utxo.ControlProgram),
429 types.NewSpendInput([][]byte{{}, {}, vm.Int64Bytes(2)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram),
431 Outputs: []*types.TxOutput{
432 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("51")),
433 types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("53")),
434 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 11, mock.RewardProgram),
435 types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 1, mock.RewardProgram),
436 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, 10, testutil.MustDecodeHexString("51")),
441 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
442 wantError: errExistCancelOrderInMatchedTx,
445 desc: "common input in the cancel order tx",
447 Transactions: []*types.Tx{
448 types.NewTx(types.TxData{
449 Inputs: []*types.TxInput{
450 types.NewSpendInput([][]byte{{}, {}, vm.Int64Bytes(2)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram),
451 types.NewSpendInput(nil, testutil.MustDecodeHash("28b7b53d8dc90006bf97e0a4eaae2a72ec3d869873188698b694beaf20789f21"), *consensus.BTMAssetID, 100, 0, []byte{0x51}),
453 Outputs: []*types.TxOutput{
454 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, 10, testutil.MustDecodeHexString("51")),
455 types.NewIntraChainOutput(*consensus.BTMAssetID, 100, mock.RewardProgram),
460 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
461 wantError: errInputProgramMustP2WMCScript,
464 desc: "amount of fee greater than max fee amount",
466 Transactions: []*types.Tx{
467 types.NewTx(types.TxData{
468 Inputs: []*types.TxInput{
469 types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram),
470 types.NewSpendInput([][]byte{vm.Int64Bytes(10), vm.Int64Bytes(1), vm.Int64Bytes(0)}, *mock.Eth2BtcOrders[2].Utxo.SourceID, *mock.Eth2BtcOrders[2].FromAssetID, mock.Eth2BtcOrders[2].Utxo.Amount, mock.Eth2BtcOrders[2].Utxo.SourcePos, mock.Eth2BtcOrders[2].Utxo.ControlProgram),
472 Outputs: []*types.TxOutput{
473 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
474 types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
476 types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 270, mock.Eth2BtcOrders[2].Utxo.ControlProgram),
478 types.NewIntraChainOutput(*mock.Btc2EthOrders[2].ToAssetID, 41, mock.RewardProgram),
479 types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 1, mock.RewardProgram),
484 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
485 wantError: match.ErrInvalidAmountOfFee,
488 desc: "ratio numerator is zero",
490 Transactions: []*types.Tx{
491 types.NewTx(types.TxData{
492 Inputs: []*types.TxInput{types.NewSpendInput(nil, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, []byte{0x51})},
493 Outputs: []*types.TxOutput{types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.MustCreateP2WMCProgram(mock.ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"), 0, 1))},
497 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
498 wantError: errRatioOfTradeLessThanZero,
501 desc: "ratio denominator is zero",
503 Transactions: []*types.Tx{
504 types.NewTx(types.TxData{
505 Inputs: []*types.TxInput{types.NewSpendInput(nil, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, []byte{0x51})},
506 Outputs: []*types.TxOutput{types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.MustCreateP2WMCProgram(mock.ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"), 1, 0))},
510 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
511 wantError: errRatioOfTradeLessThanZero,
514 desc: "want amount is overflow",
516 Transactions: []*types.Tx{
517 types.NewTx(types.TxData{
518 Inputs: []*types.TxInput{types.NewSpendInput(nil, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, []byte{0x51})},
519 Outputs: []*types.TxOutput{types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.MustCreateP2WMCProgram(mock.ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"), math.MaxInt64, 1))},
523 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
524 wantError: errRequestAmountMath,
528 for i, c := range cases {
530 c.block.Height = 84000000
531 if err := movCore.ValidateBlock(c.block, c.verifyResults); err != c.wantError {
532 t.Errorf("#%d(%s):validate block want error(%v), got error(%v)", i, c.desc, c.wantError, err)
537 func TestCalcMatchedTxFee(t *testing.T) {
541 wantMatchedTxFee map[bc.AssetID]*matchedTxFee
544 desc: "fee less than max fee",
545 wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{
546 mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram},
547 mock.ETH: {amount: 2, rewardProgram: mock.RewardProgram},
549 tx: mock.MatchedTxs[1].TxData,
552 desc: "fee refund in tx",
553 wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{
554 mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram},
555 mock.ETH: {amount: 1, rewardProgram: mock.RewardProgram},
557 tx: mock.MatchedTxs[2].TxData,
560 desc: "no price diff",
561 wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{
562 mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram},
563 mock.ETH: {amount: 1, rewardProgram: mock.RewardProgram},
565 tx: mock.MatchedTxs[0].TxData,
569 for i, c := range cases {
570 gotMatchedTxFee, err := calcFeeAmount(types.NewTx(c.tx))
575 if !testutil.DeepEqual(gotMatchedTxFee, c.wantMatchedTxFee) {
576 t.Errorf("#%d(%s):fail to caculate matched tx fee, got (%v), want (%v)", i, c.desc, gotMatchedTxFee, c.wantMatchedTxFee)
581 func TestBeforeProposalBlock(t *testing.T) {
582 consensus.ActiveNetParams.MovRewardPrograms = []consensus.MovRewardProgram{
586 Program: hex.EncodeToString(mock.RewardProgram),
592 initOrders []*common.Order
594 wantMatchedTxs []*types.Tx
597 desc: "has matched tx, but gas left is zero",
598 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
600 wantMatchedTxs: []*types.Tx{},
603 desc: "has one matched tx, and gas is sufficient",
604 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
606 wantMatchedTxs: []*types.Tx{mock.MatchedTxs[1]},
609 desc: "has two matched tx, but gas is only enough to pack a matched tx",
610 initOrders: []*common.Order{
611 mock.Btc2EthOrders[0],
612 mock.Btc2EthOrders[1],
613 mock.Eth2BtcOrders[2],
616 wantMatchedTxs: []*types.Tx{mock.MatchedTxs[2]},
619 desc: "has two matched tx, and gas left is sufficient",
620 initOrders: []*common.Order{
621 mock.Btc2EthOrders[0],
622 mock.Btc2EthOrders[1],
623 mock.Eth2BtcOrders[2],
626 wantMatchedTxs: []*types.Tx{mock.MatchedTxs[2], mock.MatchedTxs[3]},
629 desc: "has multiple trade pairs, and gas left is sufficient",
630 initOrders: []*common.Order{
631 mock.Btc2EthOrders[0],
632 mock.Btc2EthOrders[1],
633 mock.Eth2BtcOrders[2],
634 mock.Eos2EtcOrders[0],
635 mock.Etc2EosOrders[0],
638 wantMatchedTxs: []*types.Tx{mock.MatchedTxs[2], mock.MatchedTxs[3], mock.MatchedTxs[5]},
642 for i, c := range cases {
643 testDB := dbm.NewDB("testdb", "leveldb", "temp")
644 store := database.NewLevelDBMovStore(testDB)
645 if err := store.InitDBState(0, &bc.Hash{}); err != nil {
649 if err := store.ProcessOrders(c.initOrders, nil, initBlockHeader); err != nil {
653 movCore := &Core{movStore: store}
654 gotMatchedTxs, err := movCore.BeforeProposalBlock(&types.Block{BlockHeader: types.BlockHeader{Height: 2}}, c.gasLeft, func() bool { return false })
659 gotMatchedTxMap := make(map[string]interface{})
660 for _, matchedTx := range gotMatchedTxs {
661 gotMatchedTxMap[matchedTx.ID.String()] = nil
664 wantMatchedTxMap := make(map[string]interface{})
665 for _, matchedTx := range c.wantMatchedTxs {
666 wantMatchedTxMap[matchedTx.ID.String()] = nil
669 if !testutil.DeepEqual(gotMatchedTxMap, wantMatchedTxMap) {
670 t.Errorf("#%d(%s):want matched tx(%v) is not equals got matched tx(%v)", i, c.desc, c.wantMatchedTxs, gotMatchedTxs)
678 func TestValidateMatchedTxSequence(t *testing.T) {
681 initOrders []*common.Order
686 desc: "both db orders and transactions is empty",
687 initOrders: []*common.Order{},
688 transactions: []*Tx{},
692 desc: "existing matched orders in db, and transactions is empty",
693 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
694 transactions: []*Tx{},
698 desc: "db orders is empty, but transactions has matched tx",
699 initOrders: []*common.Order{},
700 transactions: []*Tx{{rawTx: mock.MatchedTxs[1]}},
701 wantError: errNotMatchedOrder,
704 desc: "existing matched orders in db, and corresponding matched tx in transactions",
705 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
706 transactions: []*Tx{{rawTx: mock.MatchedTxs[1]}},
710 desc: "package matched tx, one order from db, and the another order from transactions",
711 initOrders: []*common.Order{mock.Btc2EthOrders[0]},
712 transactions: []*Tx{{rawTx: mock.Eth2BtcMakerTxs[0]}, {rawTx: mock.MatchedTxs[10]}},
716 desc: "two matched txs use the same orders",
717 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
718 transactions: []*Tx{{rawTx: mock.MatchedTxs[1]}, {rawTx: mock.MatchedTxs[1]}},
719 wantError: errNotMatchedOrder,
722 desc: "existing two matched orders in db, and only one corresponding matched tx in transactions",
723 initOrders: []*common.Order{
724 mock.Btc2EthOrders[3], mock.Eth2BtcOrders[2],
725 mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
727 transactions: []*Tx{{rawTx: mock.MatchedTxs[8]}},
731 desc: "existing two matched orders in db, and the sequence of match txs in incorrect",
732 initOrders: []*common.Order{
733 mock.Btc2EthOrders[3], mock.Eth2BtcOrders[2],
734 mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
736 transactions: []*Tx{{rawTx: mock.MatchedTxs[1]}, {rawTx: mock.MatchedTxs[8]}},
737 wantError: errSpendOutputIDIsIncorrect,
740 desc: "matched tx and orders from packaged transactions",
741 initOrders: []*common.Order{},
742 transactions: []*Tx{{rawTx: mock.Btc2EthMakerTxs[0]}, {rawTx: mock.Eth2BtcMakerTxs[1]}, {rawTx: mock.MatchedTxs[4]}},
746 desc: "package the matched tx first, then package match orders",
747 initOrders: []*common.Order{},
748 transactions: []*Tx{{rawTx: mock.MatchedTxs[4]}, {rawTx: mock.Btc2EthMakerTxs[0]}, {rawTx: mock.Eth2BtcMakerTxs[1]}},
749 wantError: errNotMatchedOrder,
752 desc: "cancel order in transactions",
753 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
754 transactions: []*Tx{{rawTx: mock.Btc2EthCancelTxs[0]}, {rawTx: mock.MatchedTxs[1]}},
755 wantError: errNotMatchedOrder,
758 desc: "package cancel order after match tx",
759 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
760 transactions: []*Tx{{rawTx: mock.MatchedTxs[1]}, {rawTx: mock.Btc2EthCancelTxs[0]}},
764 desc: "package matched txs of different trade pairs",
765 initOrders: []*common.Order{
766 mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
767 mock.Eos2EtcOrders[0], mock.Etc2EosOrders[0],
769 transactions: []*Tx{{rawTx: mock.MatchedTxs[1]}, {rawTx: mock.MatchedTxs[9]}},
773 desc: "package matched txs of different trade pairs in different sequence",
774 initOrders: []*common.Order{
775 mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
776 mock.Eos2EtcOrders[0], mock.Etc2EosOrders[0],
778 transactions: []*Tx{{rawTx: mock.MatchedTxs[9]}, {rawTx: mock.MatchedTxs[1]}},
782 desc: "package partial matched tx from db orders, and the re-pending order continue to match",
783 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
784 transactions: []*Tx{{rawTx: mock.MatchedTxs[2]}, {rawTx: mock.MatchedTxs[3]}},
788 desc: "cancel the re-pending order",
789 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
790 transactions: []*Tx{{rawTx: mock.MatchedTxs[2]}, {rawTx: mock.Btc2EthCancelTxs[1]}, {rawTx: mock.MatchedTxs[3]}},
791 wantError: errNotMatchedOrder,
795 for i, c := range cases {
796 testDB := dbm.NewDB("testdb", "leveldb", "temp")
797 store := database.NewLevelDBMovStore(testDB)
798 if err := store.InitDBState(0, &bc.Hash{}); err != nil {
802 if err := store.ProcessOrders(c.initOrders, nil, initBlockHeader); err != nil {
806 movCore := &Core{movStore: store}
807 if err := movCore.validateMatchedTxSequence(c.transactions); err != c.wantError {
808 t.Errorf("#%d(%s):wanet error(%v), got error(%v)", i, c.desc, c.wantError, err)
816 type testFun func(movCore *Core, block *types.Block) error
818 func applyBlock(movCore *Core, block *types.Block) error {
819 return movCore.ApplyBlock(block)
822 func detachBlock(movCore *Core, block *types.Block) error {
823 return movCore.DetachBlock(block)
826 func queryAllOrders(store *database.LevelDBMovStore) []*common.Order {
827 var orders []*common.Order
828 tradePairIterator := database.NewTradePairIterator(store)
829 for tradePairIterator.HasNext() {
830 orderIterator := database.NewOrderIterator(store, tradePairIterator.Next())
831 for orderIterator.HasNext() {
832 orders = append(orders, orderIterator.NextBatch()...)
838 func ordersEquals(orders1 []*common.Order, orders2 []*common.Order) bool {
839 orderMap1 := make(map[string]*common.Order)
840 for _, order := range orders1 {
841 orderMap1[order.Key()] = order
844 orderMap2 := make(map[string]*common.Order)
845 for _, order := range orders2 {
846 orderMap2[order.Key()] = order
848 return testutil.DeepEqual(orderMap1, orderMap2)
851 func hashPtr(hash bc.Hash) *bc.Hash {
855 func orderWithHeightAndTxIndex(order *common.Order, blockHeight, txIndex uint64) *common.Order {
856 order.BlockHeight = blockHeight
857 order.TxIndex = txIndex