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.MustNewOrderFromOutput(mock.Btc2EthMakerTxs[0], 0), mock.MustNewOrderFromOutput(mock.Eth2BtcMakerTxs[0], 0)},
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.MustNewOrderFromOutput(mock.Btc2EthMakerTxs[0], 0),
59 mock.MustNewOrderFromOutput(mock.Eth2BtcMakerTxs[0], 0),
60 mock.MustNewOrderFromOutput(mock.Eos2EtcMakerTxs[0], 0),
61 mock.MustNewOrderFromOutput(mock.Eth2EosMakerTxs[0], 0),
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.MustNewOrderFromOutput(mock.MatchedTxs[0], 1)},
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.MustNewOrderFromOutput(mock.MatchedTxs[3], 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.MustNewOrderFromOutput(mock.MatchedTxs[4], 1)},
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.MustNewOrderFromOutput(mock.MatchedTxs[4], 1),
137 mock.MustNewOrderFromOutput(mock.Eth2EosMakerTxs[0], 0),
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.MustNewOrderFromOutput(mock.MatchedTxs[7], 2)},
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{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[0]},
203 wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
206 desc: "detach block has partial matched transaction",
208 BlockHeader: *initBlockHeader,
209 Transactions: []*types.Tx{
213 blockFunc: detachBlock,
214 initOrders: []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[0], 1)},
215 wantOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[1]},
216 wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
219 desc: "detach block has two partial matched transaction",
221 BlockHeader: *initBlockHeader,
222 Transactions: []*types.Tx{
223 mock.MatchedTxs[2], mock.MatchedTxs[3],
226 blockFunc: detachBlock,
227 initOrders: []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[3], 1)},
228 wantOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
229 wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
232 desc: "detach block which node packed maker tx and match transaction in random orde",
234 BlockHeader: *initBlockHeader,
235 Transactions: []*types.Tx{
236 mock.Eos2EtcMakerTxs[0],
237 mock.Btc2EthMakerTxs[0],
239 mock.Eth2EosMakerTxs[0],
240 mock.Eth2BtcMakerTxs[1],
242 mock.Etc2EosMakerTxs[0],
245 blockFunc: detachBlock,
246 initOrders: []*common.Order{
247 mock.MustNewOrderFromOutput(mock.MatchedTxs[4], 1),
248 mock.MustNewOrderFromOutput(mock.Eth2EosMakerTxs[0], 0),
250 wantOrders: []*common.Order{},
251 wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
255 defer os.RemoveAll("temp")
256 for i, c := range cases {
257 testDB := dbm.NewDB("testdb", "leveldb", "temp")
258 store := database.NewLevelDBMovStore(testDB)
259 if err := store.InitDBState(0, &bc.Hash{}); err != nil {
263 if err := store.ProcessOrders(c.initOrders, nil, initBlockHeader); err != nil {
267 movCore := &MovCore{movStore: store}
268 if err := c.blockFunc(movCore, c.block); err != c.wantError {
269 t.Errorf("#%d(%s):apply block want error(%v), got error(%v)", i, c.desc, c.wantError, err)
272 gotOrders := queryAllOrders(store)
273 if !ordersEquals(c.wantOrders, gotOrders) {
274 t.Errorf("#%d(%s):apply block want orders(%v), got orders(%v)", i, c.desc, c.wantOrders, gotOrders)
277 dbState, err := store.GetMovDatabaseState()
282 if !testutil.DeepEqual(c.wantDBState, dbState) {
283 t.Errorf("#%d(%s):apply block want db state(%v), got db state(%v)", i, c.desc, c.wantDBState, dbState)
291 func TestValidateBlock(t *testing.T) {
292 consensus.ActiveNetParams.MovRewardPrograms = []consensus.MovRewardProgram{
296 Program: hex.EncodeToString(mock.RewardProgram),
303 verifyResults []*bc.TxVerifyResult
307 desc: "block only has maker tx",
309 Transactions: []*types.Tx{
310 mock.Eth2BtcMakerTxs[0],
311 mock.Btc2EthMakerTxs[0],
314 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: false}},
318 desc: "block only has matched tx",
320 Transactions: []*types.Tx{
326 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: false}, {StatusFail: false}},
330 desc: "block has maker tx and matched tx",
332 Transactions: []*types.Tx{
333 mock.Eth2BtcMakerTxs[0],
334 mock.Btc2EthMakerTxs[0],
340 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: false}, {StatusFail: false}, {StatusFail: false}, {StatusFail: false}},
344 desc: "status fail of maker tx is true",
346 Transactions: []*types.Tx{
347 mock.Eth2BtcMakerTxs[0],
348 mock.Btc2EthMakerTxs[0],
351 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: true}},
352 wantError: errStatusFailMustFalse,
355 desc: "status fail of matched tx is true",
357 Transactions: []*types.Tx{
362 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: true}},
363 wantError: errStatusFailMustFalse,
366 desc: "asset id in matched tx is not unique",
368 Transactions: []*types.Tx{
369 types.NewTx(types.TxData{
370 Inputs: []*types.TxInput{
371 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),
372 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),
374 Outputs: []*types.TxOutput{
375 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")),
376 types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("53")),
377 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 10, []byte{0x51}),
382 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: true}},
383 wantError: errAssetIDMustUniqueInMatchedTx,
386 desc: "common input in the matched tx",
388 Transactions: []*types.Tx{
389 types.NewTx(types.TxData{
390 Inputs: []*types.TxInput{
391 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),
392 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),
393 types.NewSpendInput(nil, testutil.MustDecodeHash("28b7b53d8dc90006bf97e0a4eaae2a72ec3d869873188698b694beaf20789f21"), *consensus.BTMAssetID, 100, 0, []byte{0x51}),
395 Outputs: []*types.TxOutput{
396 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")),
397 types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("53")),
398 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 10, []byte{0x51}),
399 types.NewIntraChainOutput(*consensus.BTMAssetID, 100, []byte{0x51}),
404 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
405 wantError: errInputProgramMustP2WMCScript,
408 desc: "cancel order in the matched tx",
410 Transactions: []*types.Tx{
411 types.NewTx(types.TxData{
412 Inputs: []*types.TxInput{
413 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),
414 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),
415 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),
417 Outputs: []*types.TxOutput{
418 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")),
419 types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("53")),
420 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 10, []byte{0x51}),
421 types.NewIntraChainOutput(*consensus.BTMAssetID, 100, mock.RewardProgram),
426 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
427 wantError: errExistCancelOrderInMatchedTx,
430 desc: "common input in the cancel order tx",
432 Transactions: []*types.Tx{
433 types.NewTx(types.TxData{
434 Inputs: []*types.TxInput{
435 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),
436 types.NewSpendInput(nil, testutil.MustDecodeHash("28b7b53d8dc90006bf97e0a4eaae2a72ec3d869873188698b694beaf20789f21"), *consensus.BTMAssetID, 100, 0, []byte{0x51}),
438 Outputs: []*types.TxOutput{
439 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, 10, testutil.MustDecodeHexString("51")),
440 types.NewIntraChainOutput(*consensus.BTMAssetID, 100, mock.RewardProgram),
445 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
446 wantError: errInputProgramMustP2WMCScript,
449 desc: "amount of fee greater than max fee amount",
451 Transactions: []*types.Tx{
452 types.NewTx(types.TxData{
453 Inputs: []*types.TxInput{
454 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),
455 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),
457 Outputs: []*types.TxOutput{
458 types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
459 types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
461 types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 270, mock.Eth2BtcOrders[2].Utxo.ControlProgram),
463 types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 40, mock.RewardProgram),
468 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
469 wantError: match.ErrAmountOfFeeExceedMaximum,
472 desc: "ratio numerator is zero",
474 Transactions: []*types.Tx{
475 types.NewTx(types.TxData{
476 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})},
477 Outputs: []*types.TxOutput{types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.MustCreateP2WMCProgram(mock.ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"), 0, 1))},
481 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
482 wantError: errRatioOfTradeLessThanZero,
485 desc: "ratio denominator is zero",
487 Transactions: []*types.Tx{
488 types.NewTx(types.TxData{
489 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})},
490 Outputs: []*types.TxOutput{types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.MustCreateP2WMCProgram(mock.ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"), 1, 0))},
494 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
495 wantError: errRatioOfTradeLessThanZero,
498 desc: "want amount is overflow",
500 Transactions: []*types.Tx{
501 types.NewTx(types.TxData{
502 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})},
503 Outputs: []*types.TxOutput{types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.MustCreateP2WMCProgram(mock.ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"), math.MaxInt64, 1))},
507 verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
508 wantError: errRequestAmountMath,
512 for i, c := range cases {
513 movCore := &MovCore{}
514 if err := movCore.ValidateBlock(c.block, c.verifyResults); err != c.wantError {
515 t.Errorf("#%d(%s):validate block want error(%v), got error(%v)", i, c.desc, c.wantError, err)
520 func TestCalcMatchedTxFee(t *testing.T) {
525 wantMatchedTxFee map[bc.AssetID]*matchedTxFee
528 desc: "fee less than max fee",
530 wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{mock.ETH: {amount: 10, rewardProgram: mock.RewardProgram}},
531 tx: mock.MatchedTxs[1].TxData,
534 desc: "fee refund in tx",
536 wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{mock.ETH: {amount: 25, rewardProgram: mock.RewardProgram}},
537 tx: mock.MatchedTxs[2].TxData,
542 wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{},
543 tx: mock.MatchedTxs[0].TxData,
547 for i, c := range cases {
548 gotMatchedTxFee, err := calcFeeAmount(types.NewTx(c.tx))
553 if !testutil.DeepEqual(gotMatchedTxFee, c.wantMatchedTxFee) {
554 t.Errorf("#%d(%s):fail to caculate matched tx fee, got (%v), want (%v)", i, c.desc, gotMatchedTxFee, c.wantMatchedTxFee)
559 func TestBeforeProposalBlock(t *testing.T) {
560 consensus.ActiveNetParams.MovRewardPrograms = []consensus.MovRewardProgram{
564 Program: hex.EncodeToString(mock.RewardProgram),
570 initOrders []*common.Order
572 wantMatchedTxs []*types.Tx
575 desc: "has matched tx, but gas left is zero",
576 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
578 wantMatchedTxs: []*types.Tx{},
581 desc: "has one matched tx, and gas is sufficient",
582 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
584 wantMatchedTxs: []*types.Tx{mock.MatchedTxs[1]},
587 desc: "has two matched tx, but gas is only enough to pack a matched tx",
588 initOrders: []*common.Order{
589 mock.Btc2EthOrders[0],
590 mock.Btc2EthOrders[1],
591 mock.Eth2BtcOrders[2],
594 wantMatchedTxs: []*types.Tx{mock.MatchedTxs[2]},
597 desc: "has two matched tx, and gas left is sufficient",
598 initOrders: []*common.Order{
599 mock.Btc2EthOrders[0],
600 mock.Btc2EthOrders[1],
601 mock.Eth2BtcOrders[2],
604 wantMatchedTxs: []*types.Tx{mock.MatchedTxs[2], mock.MatchedTxs[3]},
607 desc: "has multiple trade pairs, and gas left is sufficient",
608 initOrders: []*common.Order{
609 mock.Btc2EthOrders[0],
610 mock.Btc2EthOrders[1],
611 mock.Eth2BtcOrders[2],
612 mock.Eos2EtcOrders[0],
613 mock.Etc2EosOrders[0],
616 wantMatchedTxs: []*types.Tx{mock.MatchedTxs[2], mock.MatchedTxs[3], mock.MatchedTxs[5]},
620 for i, c := range cases {
621 testDB := dbm.NewDB("testdb", "leveldb", "temp")
622 store := database.NewLevelDBMovStore(testDB)
623 if err := store.InitDBState(0, &bc.Hash{}); err != nil {
627 if err := store.ProcessOrders(c.initOrders, nil, initBlockHeader); err != nil {
631 movCore := &MovCore{movStore: store}
632 gotMatchedTxs, err := movCore.BeforeProposalBlock(nil, 2, c.gasLeft, func() bool { return false })
637 gotMatchedTxMap := make(map[string]interface{})
638 for _, matchedTx := range gotMatchedTxs {
639 gotMatchedTxMap[matchedTx.ID.String()] = nil
642 wantMatchedTxMap := make(map[string]interface{})
643 for _, matchedTx := range c.wantMatchedTxs {
644 wantMatchedTxMap[matchedTx.ID.String()] = nil
647 if !testutil.DeepEqual(gotMatchedTxMap, wantMatchedTxMap) {
648 t.Errorf("#%d(%s):want matched tx(%v) is not equals got matched tx(%v)", i, c.desc, c.wantMatchedTxs, gotMatchedTxs)
656 func TestValidateMatchedTxSequence(t *testing.T) {
659 initOrders []*common.Order
660 transactions []*types.Tx
664 desc: "both db orders and transactions is empty",
665 initOrders: []*common.Order{},
666 transactions: []*types.Tx{},
670 desc: "existing matched orders in db, and transactions is empty",
671 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
672 transactions: []*types.Tx{},
676 desc: "db orders is empty, but transactions has matched tx",
677 initOrders: []*common.Order{},
678 transactions: []*types.Tx{mock.MatchedTxs[1]},
679 wantError: errNotMatchedOrder,
682 desc: "existing matched orders in db, and corresponding matched tx in transactions",
683 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
684 transactions: []*types.Tx{mock.MatchedTxs[1]},
688 desc: "package matched tx, one order from db, and the another order from transactions",
689 initOrders: []*common.Order{mock.Btc2EthOrders[0]},
690 transactions: []*types.Tx{mock.Eth2BtcMakerTxs[0], mock.MatchedTxs[10]},
694 desc: "two matched txs use the same orders",
695 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
696 transactions: []*types.Tx{mock.MatchedTxs[1], mock.MatchedTxs[1]},
697 wantError: errNotMatchedOrder,
700 desc: "existing two matched orders in db, and only one corresponding matched tx in transactions",
701 initOrders: []*common.Order{
702 mock.Btc2EthOrders[3], mock.Eth2BtcOrders[2],
703 mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
705 transactions: []*types.Tx{mock.MatchedTxs[8]},
709 desc: "existing two matched orders in db, and the sequence of match txs in incorrect",
710 initOrders: []*common.Order{
711 mock.Btc2EthOrders[3], mock.Eth2BtcOrders[2],
712 mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
714 transactions: []*types.Tx{mock.MatchedTxs[1], mock.MatchedTxs[8]},
715 wantError: errSpendOutputIDIsIncorrect,
718 desc: "matched tx and orders from packaged transactions",
719 initOrders: []*common.Order{},
720 transactions: []*types.Tx{mock.Btc2EthMakerTxs[0], mock.Eth2BtcMakerTxs[1], mock.MatchedTxs[4]},
724 desc: "package the matched tx first, then package match orders",
725 initOrders: []*common.Order{},
726 transactions: []*types.Tx{mock.MatchedTxs[4], mock.Btc2EthMakerTxs[0], mock.Eth2BtcMakerTxs[1]},
727 wantError: errNotMatchedOrder,
730 desc: "cancel order in transactions",
731 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
732 transactions: []*types.Tx{mock.Btc2EthCancelTxs[0], mock.MatchedTxs[1]},
733 wantError: errNotMatchedOrder,
736 desc: "package cancel order after match tx",
737 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
738 transactions: []*types.Tx{mock.MatchedTxs[1], mock.Btc2EthCancelTxs[0]},
742 desc: "package matched txs of different trade pairs",
743 initOrders: []*common.Order{
744 mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
745 mock.Eos2EtcOrders[0], mock.Etc2EosOrders[0],
747 transactions: []*types.Tx{mock.MatchedTxs[1], mock.MatchedTxs[9]},
751 desc: "package matched txs of different trade pairs in different sequence",
752 initOrders: []*common.Order{
753 mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
754 mock.Eos2EtcOrders[0], mock.Etc2EosOrders[0],
756 transactions: []*types.Tx{mock.MatchedTxs[9], mock.MatchedTxs[1]},
760 desc: "package partial matched tx from db orders, and the re-pending order continue to match",
761 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
762 transactions: []*types.Tx{mock.MatchedTxs[2], mock.MatchedTxs[3]},
766 desc: "cancel the re-pending order",
767 initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
768 transactions: []*types.Tx{mock.MatchedTxs[2], mock.Btc2EthCancelTxs[1], mock.MatchedTxs[3]},
769 wantError: errNotMatchedOrder,
773 for i, c := range cases {
774 testDB := dbm.NewDB("testdb", "leveldb", "temp")
775 store := database.NewLevelDBMovStore(testDB)
776 if err := store.InitDBState(0, &bc.Hash{}); err != nil {
780 if err := store.ProcessOrders(c.initOrders, nil, initBlockHeader); err != nil {
784 movCore := &MovCore{movStore: store}
785 if err := movCore.validateMatchedTxSequence(c.transactions); err != c.wantError {
786 t.Errorf("#%d(%s):wanet error(%v), got error(%v)", i, c.desc, c.wantError, err)
794 type testFun func(movCore *MovCore, block *types.Block) error
796 func applyBlock(movCore *MovCore, block *types.Block) error {
797 return movCore.ApplyBlock(block)
800 func detachBlock(movCore *MovCore, block *types.Block) error {
801 return movCore.DetachBlock(block)
804 func queryAllOrders(store *database.LevelDBMovStore) []*common.Order {
805 var orders []*common.Order
806 tradePairIterator := database.NewTradePairIterator(store)
807 for tradePairIterator.HasNext() {
808 orderIterator := database.NewOrderIterator(store, tradePairIterator.Next())
809 for orderIterator.HasNext() {
810 orders = append(orders, orderIterator.NextBatch()...)
816 func ordersEquals(orders1 []*common.Order, orders2 []*common.Order) bool {
817 orderMap1 := make(map[string]*common.Order)
818 for _, order := range orders1 {
819 orderMap1[order.Key()] = order
822 orderMap2 := make(map[string]*common.Order)
823 for _, order := range orders2 {
824 orderMap2[order.Key()] = order
826 return testutil.DeepEqual(orderMap1, orderMap2)
829 func hashPtr(hash bc.Hash) *bc.Hash {