• R/O
  • HTTP
  • SSH
  • HTTPS

vapor: 提交

Golang implemented sidechain for Bytom


Commit MetaInfo

修订版9f9a32de63521df77fa919fecb5a4adbf3c24c34 (tree)
时间2020-02-18 21:35:14
作者ipqhjjybj <250657661@qq.c...>
Commiteripqhjjybj

Log Message

Merge branch 'rollback_pr1' of https://github.com/Bytom/vapor into rollback_pr1

更改概述

差异

--- a/application/mov/common/type.go
+++ b/application/mov/common/type.go
@@ -28,16 +28,17 @@ type Order struct {
2828 RatioDenominator int64
2929 }
3030
31+// Rate return the exchange represented by float64
3132 func (o *Order) Rate() float64 {
3233 if o.RatioDenominator == 0 {
3334 return 0
3435 }
35- rate := big.NewFloat(0).SetInt64(o.RatioNumerator)
36- rate.Quo(rate, big.NewFloat(0).SetInt64(o.RatioDenominator))
36+ rate := big.NewRat(o.RatioNumerator, o.RatioDenominator)
3737 result, _ := rate.Float64()
3838 return result
3939 }
4040
41+// Cmp compares x and y and returns -1 if x < y, 0 if x == y, +1 if x > y
4142 func (o *Order) Cmp(other *Order) int {
4243 rate := big.NewRat(o.RatioNumerator, o.RatioDenominator)
4344 otherRate := big.NewRat(other.RatioNumerator, other.RatioDenominator)
--- a/application/mov/common/util.go
+++ b/application/mov/common/util.go
@@ -12,7 +12,7 @@ func IsMatchedTx(tx *types.Tx) bool {
1212 return false
1313 }
1414 for _, input := range tx.Inputs {
15- if input.InputType() == types.SpendInputType && contract.IsTradeClauseSelector(input) && segwit.IsP2WMCScript(input.ControlProgram()) {
15+ if input.InputType() == types.SpendInputType && segwit.IsP2WMCScript(input.ControlProgram()) && contract.IsTradeClauseSelector(input) {
1616 return true
1717 }
1818 }
@@ -22,7 +22,7 @@ func IsMatchedTx(tx *types.Tx) bool {
2222 // IsCancelOrderTx check if this transaction has cancel mov order input
2323 func IsCancelOrderTx(tx *types.Tx) bool {
2424 for _, input := range tx.Inputs {
25- if input.InputType() == types.SpendInputType && contract.IsCancelClauseSelector(input) && segwit.IsP2WMCScript(input.ControlProgram()) {
25+ if input.InputType() == types.SpendInputType && segwit.IsP2WMCScript(input.ControlProgram()) && contract.IsCancelClauseSelector(input) {
2626 return true
2727 }
2828 }
--- a/application/mov/database/mov_store.go
+++ b/application/mov/database/mov_store.go
@@ -121,7 +121,6 @@ func (m *LevelDBMovStore) ListOrders(orderAfter *common.Order) ([]*common.Order,
121121 orderPrefix = append(orderPrefix, orderAfter.ToAssetID.Bytes()...)
122122
123123 var startKey []byte
124-
125124 if orderAfter.Rate() > 0 {
126125 startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, orderAfter.UTXOHash(), orderAfter.Rate())
127126 }
--- a/application/mov/match/match.go
+++ b/application/mov/match/match.go
@@ -113,9 +113,7 @@ func (e *Engine) addPartialTradeOrder(tx *types.Tx) error {
113113 return err
114114 }
115115
116- if err := e.orderBook.AddOrder(order); err != nil {
117- return err
118- }
116+ e.orderBook.AddOrder(order)
119117 }
120118 return nil
121119 }
@@ -208,6 +206,7 @@ func addMatchTxOutput(txData *types.TxData, txInput *types.TxInput, order *commo
208206 return nil
209207 }
210208
209+// CalcRequestAmount is from amount * numerator / ratioDenominator
211210 func CalcRequestAmount(fromAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 {
212211 res := big.NewInt(0).SetUint64(fromAmount)
213212 res.Mul(res, big.NewInt(contractArg.RatioNumerator)).Quo(res, big.NewInt(contractArg.RatioDenominator))
@@ -234,6 +233,7 @@ func calcOppositeIndex(size int, selfIdx int) int {
234233 return (selfIdx + 1) % size
235234 }
236235
236+// IsMatched check does the orders can be exchange
237237 func IsMatched(orders []*common.Order) bool {
238238 sortedOrders := sortOrders(orders)
239239 if len(sortedOrders) == 0 {
@@ -283,6 +283,10 @@ func validateTradePairs(tradePairs []*common.TradePair) error {
283283 }
284284
285285 func sortOrders(orders []*common.Order) []*common.Order {
286+ if len(orders) == 0 {
287+ return nil
288+ }
289+
286290 orderMap := make(map[bc.AssetID]*common.Order)
287291 firstOrder := orders[0]
288292 for i := 1; i < len(orders); i++ {
--- a/application/mov/match/order_book.go
+++ b/application/mov/match/order_book.go
@@ -6,7 +6,6 @@ import (
66
77 "github.com/bytom/vapor/application/mov/common"
88 "github.com/bytom/vapor/application/mov/database"
9- "github.com/bytom/vapor/errors"
109 )
1110
1211 // OrderBook is used to handle the mov orders in memory like stack
@@ -23,6 +22,28 @@ type OrderBook struct {
2322 arrivalDelOrders *sync.Map
2423 }
2524
25+func arrangeArrivalAddOrders(orders []*common.Order) *sync.Map {
26+ orderMap := make(map[string][]*common.Order)
27+ for _, order := range orders {
28+ orderMap[order.TradePair().Key()] = append(orderMap[order.TradePair().Key()], order)
29+ }
30+
31+ arrivalOrderMap := &sync.Map{}
32+ for key, orders := range orderMap {
33+ sort.Sort(sort.Reverse(common.OrderSlice(orders)))
34+ arrivalOrderMap.Store(key, orders)
35+ }
36+ return arrivalOrderMap
37+}
38+
39+func arrangeArrivalDelOrders(orders []*common.Order) *sync.Map {
40+ arrivalDelOrderMap := &sync.Map{}
41+ for _, order := range orders {
42+ arrivalDelOrderMap.Store(order.Key(), order)
43+ }
44+ return arrivalDelOrderMap
45+}
46+
2647 // NewOrderBook create a new OrderBook object
2748 func NewOrderBook(movStore database.MovStore, arrivalAddOrders, arrivalDelOrders []*common.Order) *OrderBook {
2849 return &OrderBook{
@@ -35,17 +56,23 @@ func NewOrderBook(movStore database.MovStore, arrivalAddOrders, arrivalDelOrders
3556 }
3657 }
3758
38-// AddOrder add the in memory temp order to order table
39-func (o *OrderBook) AddOrder(order *common.Order) error {
59+// AddOrder add the in memory temp order to order table, because temp order is what left for the
60+// partial trade order, so the price should be lowest.
61+func (o *OrderBook) AddOrder(order *common.Order) {
4062 tradePairKey := order.TradePair().Key()
4163 orders := o.getArrivalAddOrders(tradePairKey)
42- if len(orders) > 0 && order.Cmp(orders[len(orders)-1]) > 0 {
43- return errors.New("rate of order must less than the min order in order table")
44- }
45-
64+ // use binary search to find the insert position
65+ i := sort.Search(len(orders), func(i int) bool { return order.Cmp(orders[i]) > 0 })
4666 orders = append(orders, order)
67+ copy(orders[i+1:], orders[i:])
68+ orders[i] = order
69+
4770 o.arrivalAddOrders.Store(tradePairKey, orders)
48- return nil
71+}
72+
73+// DelOrder mark the order has been deleted in order book
74+func (o *OrderBook) DelOrder(order *common.Order) {
75+ o.arrivalDelOrders.Store(order.Key(), order)
4976 }
5077
5178 // PeekOrder return the next lowest order of given trade pair
@@ -95,12 +122,13 @@ func (o *OrderBook) PopOrder(tradePair *common.TradePair) {
95122
96123 orders := o.getDBOrders(tradePair.Key())
97124 if len(orders) != 0 && orders[len(orders)-1].Key() == order.Key() {
98- o.dbOrders.Store(tradePair.Key(), orders[0 : len(orders)-1])
125+ o.dbOrders.Store(tradePair.Key(), orders[0:len(orders)-1])
126+ return
99127 }
100128
101129 arrivalOrders := o.getArrivalAddOrders(tradePair.Key())
102130 if len(arrivalOrders) != 0 && arrivalOrders[len(arrivalOrders)-1].Key() == order.Key() {
103- o.arrivalAddOrders.Store(tradePair.Key(), arrivalOrders[0 : len(arrivalOrders)-1])
131+ o.arrivalAddOrders.Store(tradePair.Key(), arrivalOrders[0:len(arrivalOrders)-1])
104132 }
105133 }
106134
@@ -134,29 +162,6 @@ func (o *OrderBook) getArrivalDelOrders(orderKey string) *common.Order {
134162 return nil
135163 }
136164
137-func arrangeArrivalAddOrders(orders []*common.Order) *sync.Map {
138- orderMap := make(map[string][]*common.Order)
139- for _, order := range orders {
140- orderMap[order.TradePair().Key()] = append(orderMap[order.TradePair().Key()], order)
141- }
142-
143- arrivalOrderMap := &sync.Map{}
144- for key, orders := range orderMap {
145- sort.Sort(sort.Reverse(common.OrderSlice(orders)))
146- arrivalOrderMap.Store(key, orders)
147-
148- }
149- return arrivalOrderMap
150-}
151-
152-func arrangeArrivalDelOrders(orders []*common.Order) *sync.Map {
153- arrivalDelOrderMap := &sync.Map{}
154- for _, order := range orders {
155- arrivalDelOrderMap.Store(order.Key(), order)
156- }
157- return arrivalDelOrderMap
158-}
159-
160165 func (o *OrderBook) extendDBOrders(tradePair *common.TradePair) {
161166 iterator, ok := o.orderIterators.Load(tradePair.Key())
162167 if !ok {
@@ -173,8 +178,23 @@ func (o *OrderBook) extendDBOrders(tradePair *common.TradePair) {
173178 }
174179
175180 func (o *OrderBook) peekArrivalOrder(tradePair *common.TradePair) *common.Order {
176- if arrivalAddOrders := o.getArrivalAddOrders(tradePair.Key()); len(arrivalAddOrders) > 0 {
177- return arrivalAddOrders[len(arrivalAddOrders)-1]
181+ orders := o.getArrivalAddOrders(tradePair.Key())
182+ delPos := len(orders)
183+ for i := delPos - 1; i >= 0; i-- {
184+ if o.getArrivalDelOrders(orders[i].Key()) != nil {
185+ delPos = i
186+ } else {
187+ break
188+ }
189+ }
190+
191+ if delPos < len(orders) {
192+ orders = orders[:delPos]
193+ o.arrivalAddOrders.Store(tradePair.Key(), orders)
194+ }
195+
196+ if size := len(orders); size > 0 {
197+ return orders[size-1]
178198 }
179199 return nil
180200 }
--- a/application/mov/match/order_book_test.go
+++ b/application/mov/match/order_book_test.go
@@ -6,6 +6,7 @@ import (
66 "github.com/bytom/vapor/application/mov/common"
77 "github.com/bytom/vapor/application/mov/database"
88 "github.com/bytom/vapor/application/mov/mock"
9+ "github.com/bytom/vapor/testutil"
910 )
1011
1112 var (
@@ -172,9 +173,7 @@ func TestOrderBook(t *testing.T) {
172173 for i, c := range cases {
173174 orderBook := NewOrderBook(c.initMovStore, c.initArrivalAddOrders, c.initArrivalDelOrders)
174175 for _, order := range c.addOrders {
175- if err := orderBook.AddOrder(order); err != nil {
176- t.Fatal(err)
177- }
176+ orderBook.AddOrder(order)
178177 }
179178
180179 for _, tradePair := range c.popOrders {
@@ -188,8 +187,190 @@ func TestOrderBook(t *testing.T) {
188187 }
189188
190189 if gotOrder.Key() != wantOrder.Key() {
191- t.Errorf("#%d(%s):the key of got order(%v) is not equals key of want order(%v)", i, c.desc, gotOrder, wantOrder)
190+ t.Fatalf("#%d(%s):the key of got order(%v) is not equals key of want order(%v)", i, c.desc, gotOrder, wantOrder)
192191 }
193192 }
194193 }
195194 }
195+
196+func TestPeekArrivalOrder(t *testing.T) {
197+ cases := []struct {
198+ desc string
199+ initArrivalAddOrders []*common.Order
200+ initArrivalDelOrders []*common.Order
201+ peekTradePair *common.TradePair
202+ wantArrivalAddOrders []*common.Order
203+ wantOrder *common.Order
204+ }{
205+ {
206+ desc: "empty peek",
207+ initArrivalAddOrders: []*common.Order{},
208+ initArrivalDelOrders: []*common.Order{},
209+ peekTradePair: btc2eth,
210+ wantArrivalAddOrders: []*common.Order{},
211+ wantOrder: nil,
212+ },
213+ {
214+ desc: "1 element regular peek",
215+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
216+ initArrivalDelOrders: []*common.Order{},
217+ peekTradePair: btc2eth,
218+ wantArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
219+ wantOrder: mock.Btc2EthOrders[0],
220+ },
221+ {
222+ desc: "4 element regular peek with",
223+ initArrivalAddOrders: []*common.Order{
224+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
225+ },
226+ initArrivalDelOrders: []*common.Order{},
227+ peekTradePair: btc2eth,
228+ wantArrivalAddOrders: []*common.Order{
229+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
230+ },
231+ wantOrder: mock.Btc2EthOrders[3],
232+ },
233+ {
234+ desc: "1 element peek with 1 unrelated deleted order",
235+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
236+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[1]},
237+ peekTradePair: btc2eth,
238+ wantArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
239+ wantOrder: mock.Btc2EthOrders[0],
240+ },
241+ {
242+ desc: "1 element peek with 1 related deleted order",
243+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
244+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[0]},
245+ peekTradePair: btc2eth,
246+ wantArrivalAddOrders: []*common.Order{},
247+ wantOrder: nil,
248+ },
249+ {
250+ desc: "4 element peek with first 3 deleted order",
251+ initArrivalAddOrders: []*common.Order{
252+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
253+ },
254+ initArrivalDelOrders: []*common.Order{
255+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
256+ },
257+ peekTradePair: btc2eth,
258+ wantArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[1]},
259+ wantOrder: mock.Btc2EthOrders[1],
260+ },
261+ {
262+ desc: "4 element peek with first 1 deleted order",
263+ initArrivalAddOrders: []*common.Order{
264+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
265+ },
266+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3]},
267+ peekTradePair: btc2eth,
268+ wantArrivalAddOrders: []*common.Order{
269+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
270+ },
271+ wantOrder: mock.Btc2EthOrders[0],
272+ },
273+ {
274+ desc: "4 element peek with first 2th deleted order",
275+ initArrivalAddOrders: []*common.Order{
276+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
277+ },
278+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[0]},
279+ peekTradePair: btc2eth,
280+ wantArrivalAddOrders: []*common.Order{
281+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
282+ },
283+ wantOrder: mock.Btc2EthOrders[3],
284+ },
285+ }
286+
287+ for i, c := range cases {
288+ orderBook := NewOrderBook(mock.NewMovStore(nil, nil), c.initArrivalAddOrders, c.initArrivalDelOrders)
289+ gotOrder := orderBook.PeekOrder(c.peekTradePair)
290+ if !testutil.DeepEqual(gotOrder, c.wantOrder) {
291+ t.Fatalf("#%d(%s):the key of got order(%v) is not equals key of want order(%v)", i, c.desc, gotOrder, c.wantOrder)
292+ }
293+
294+ wantAddOrders, _ := arrangeArrivalAddOrders(c.wantArrivalAddOrders).Load(c.peekTradePair.Key())
295+ gotAddOrders := orderBook.getArrivalAddOrders(c.peekTradePair.Key())
296+ if !testutil.DeepEqual(gotAddOrders, wantAddOrders) {
297+ t.Fatalf("#%d(%s): the got arrivalAddOrders(%v) is differnt than want arrivalAddOrders(%v)", i, c.desc, gotAddOrders, wantAddOrders)
298+ }
299+ }
300+}
301+
302+func TestAddOrder(t *testing.T) {
303+ cases := []struct {
304+ initOrders []*common.Order
305+ wantOrders []*common.Order
306+ addOrder *common.Order
307+ }{
308+ {
309+ initOrders: []*common.Order{},
310+ addOrder: &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 53, RatioDenominator: 1},
311+ wantOrders: []*common.Order{
312+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 53, RatioDenominator: 1},
313+ },
314+ },
315+ {
316+ initOrders: []*common.Order{
317+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
318+ },
319+ addOrder: &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
320+ wantOrders: []*common.Order{
321+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
322+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
323+ },
324+ },
325+ {
326+ initOrders: []*common.Order{
327+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
328+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
329+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 52, RatioDenominator: 1},
330+ },
331+ addOrder: &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 53, RatioDenominator: 1},
332+ wantOrders: []*common.Order{
333+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 53, RatioDenominator: 1},
334+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 52, RatioDenominator: 1},
335+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
336+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
337+ },
338+ },
339+ {
340+ initOrders: []*common.Order{
341+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
342+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
343+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 52, RatioDenominator: 1},
344+ },
345+ addOrder: &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 49, RatioDenominator: 1},
346+ wantOrders: []*common.Order{
347+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 52, RatioDenominator: 1},
348+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
349+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
350+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 49, RatioDenominator: 1},
351+ },
352+ },
353+ {
354+ initOrders: []*common.Order{
355+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 52, RatioDenominator: 1},
356+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
357+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 49, RatioDenominator: 1},
358+ },
359+ addOrder: &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
360+ wantOrders: []*common.Order{
361+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 52, RatioDenominator: 1},
362+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 51, RatioDenominator: 1},
363+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 50, RatioDenominator: 1},
364+ &common.Order{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH, RatioNumerator: 49, RatioDenominator: 1},
365+ },
366+ },
367+ }
368+
369+ for i, c := range cases {
370+ orderBook := NewOrderBook(mock.NewMovStore(nil, nil), c.initOrders, nil)
371+ orderBook.AddOrder(c.addOrder)
372+ if gotOrders := orderBook.getArrivalAddOrders(btc2eth.Key()); !testutil.DeepEqual(gotOrders, c.wantOrders) {
373+ t.Fatalf("#%d: the gotOrders(%v) is differnt than wantOrders(%v)", i, gotOrders, c.wantOrders)
374+ }
375+ }
376+}
--- a/application/mov/match_collector.go
+++ b/application/mov/match_collector.go
@@ -16,11 +16,11 @@ type matchCollector struct {
1616 gasLeft int64
1717 isTimeout func() bool
1818
19- workerNum int
20- workerNumCh chan int
21- processCh chan *matchTxResult
22- tradePairCh chan *common.TradePair
23- closeCh chan struct{}
19+ workerNum int
20+ endWorkCh chan int
21+ tradePairCh chan *common.TradePair
22+ matchResultCh chan *matchTxResult
23+ closeCh chan struct{}
2424 }
2525
2626 type matchTxResult struct {
@@ -33,13 +33,13 @@ func newMatchTxCollector(engine *match.Engine, iterator *database.TradePairItera
3333 return &matchCollector{
3434 engine: engine,
3535 tradePairIterator: iterator,
36+ gasLeft: gasLeft,
37+ isTimeout: isTimeout,
3638 workerNum: workerNum,
37- workerNumCh: make(chan int, workerNum),
38- processCh: make(chan *matchTxResult),
39+ endWorkCh: make(chan int, workerNum),
3940 tradePairCh: make(chan *common.TradePair, workerNum),
41+ matchResultCh: make(chan *matchTxResult),
4042 closeCh: make(chan struct{}),
41- gasLeft: gasLeft,
42- isTimeout: isTimeout,
4343 }
4444 }
4545
@@ -63,10 +63,9 @@ func (m *matchCollector) collect() ([]*types.Tx, error) {
6363 defer close(m.closeCh)
6464
6565 var matchedTxs []*types.Tx
66- completed := 0
67- for !m.isTimeout() {
66+ for completed := 0; !m.isTimeout(); {
6867 select {
69- case data := <-m.processCh:
68+ case data := <-m.matchResultCh:
7069 if data.err != nil {
7170 return nil, data.err
7271 }
@@ -77,7 +76,7 @@ func (m *matchCollector) collect() ([]*types.Tx, error) {
7776 } else {
7877 return matchedTxs, nil
7978 }
80- case <-m.workerNumCh:
79+ case <-m.endWorkCh:
8180 if completed++; completed == m.workerNum {
8281 return matchedTxs, nil
8382 }
@@ -113,7 +112,7 @@ func (m *matchCollector) tradePairProducer(wg *sync.WaitGroup) {
113112
114113 func (m *matchCollector) matchTxWorker(wg *sync.WaitGroup) {
115114 defer func() {
116- m.workerNumCh <- 1
115+ m.endWorkCh <- 1
117116 wg.Done()
118117 }()
119118
@@ -122,22 +121,26 @@ func (m *matchCollector) matchTxWorker(wg *sync.WaitGroup) {
122121 case <-m.closeCh:
123122 return
124123 case tradePair := <-m.tradePairCh:
124+ // end worker due to all trade pair has been matched
125125 if tradePair == nil {
126126 return
127127 }
128+
128129 for m.engine.HasMatchedTx(tradePair, tradePair.Reverse()) {
129130 matchedTx, err := m.engine.NextMatchedTx(tradePair, tradePair.Reverse())
130- data := &matchTxResult{matchedTx: matchedTx, err: err}
131131 select {
132132 case <-m.closeCh:
133133 return
134- case m.processCh <- data:
135- if data.err != nil {
134+ case m.matchResultCh <- &matchTxResult{matchedTx: matchedTx, err: err}:
135+ if err != nil {
136136 return
137137 }
138138 }
139139 }
140140 }
141-
142141 }
143142 }
143+
144+func calcMatchedTxGasUsed(tx *types.Tx) int64 {
145+ return int64(len(tx.Inputs))*150 + int64(tx.SerializedSize)
146+}
--- a/application/mov/mock/mock.go
+++ b/application/mov/mock/mock.go
@@ -166,6 +166,20 @@ var (
166166 },
167167 }
168168
169+ Btc2EthCancelTxs = []*types.Tx{
170+ // Btc2EthOrders[0]
171+ types.NewTx(types.TxData{
172+ Inputs: []*types.TxInput{types.NewSpendInput([][]byte{{}, {}, vm.Int64Bytes(2)}, *Btc2EthOrders[0].Utxo.SourceID, *Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, Btc2EthOrders[0].Utxo.SourcePos, Btc2EthOrders[0].Utxo.ControlProgram)},
173+ Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"))},
174+ }),
175+
176+ // output 2 of MatchedTxs[2]
177+ types.NewTx(types.TxData{
178+ Inputs: []*types.TxInput{types.NewSpendInput([][]byte{{}, {}, vm.Int64Bytes(2)}, *MustNewOrderFromOutput(MatchedTxs[2], 2).Utxo.SourceID, *Eth2BtcOrders[2].FromAssetID, 270, 2, Eth2BtcOrders[2].Utxo.ControlProgram)},
179+ Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, Eth2BtcOrders[2].Utxo.Amount, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255"))},
180+ }),
181+ }
182+
169183 Btc2EthMakerTxs = []*types.Tx{
170184 // Btc2EthOrders[0]
171185 types.NewTx(types.TxData{
@@ -304,7 +318,7 @@ var (
304318 },
305319 }),
306320
307- // full matched transaction from Eos2Etc[0] Etc2Eos[0]
321+ // full matched transaction from Eos2EtcMakerTxs[0] Etc2EosMakerTxs[0]
308322 types.NewTx(types.TxData{
309323 Inputs: []*types.TxInput{
310324 types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Eos2EtcMakerTxs[0], 0).Utxo.SourceID, *Eos2EtcOrders[0].FromAssetID, Eos2EtcOrders[0].Utxo.Amount, Eos2EtcOrders[0].Utxo.SourcePos, Eos2EtcOrders[0].Utxo.ControlProgram),
@@ -329,6 +343,61 @@ var (
329343 types.NewIntraChainOutput(BTC, 10, testutil.MustDecodeHexString("00144d0dfc8a0c5ce41d31d4f61d99aff70588bff8bc")),
330344 },
331345 }),
346+
347+ // partial matched transaction from MatchedTxs[4], Eth2BtcMakerTxs[0]
348+ types.NewTx(types.TxData{
349+ Inputs: []*types.TxInput{
350+ types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, testutil.MustDecodeHash("ed810e1672c3b9de27a1db23e017e6b9cc23334b6e3dbd25dfe8857e289b7f06"), *Btc2EthOrders[0].FromAssetID, 2, 1, Btc2EthOrders[0].Utxo.ControlProgram),
351+ types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Eth2BtcMakerTxs[0], 0).Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, 0, Eth2BtcOrders[0].Utxo.ControlProgram),
352+ },
353+ Outputs: []*types.TxOutput{
354+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
355+ types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 2, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
356+ // re-order
357+ types.NewIntraChainOutput(*Eth2BtcOrders[0].FromAssetID, 404, Eth2BtcOrders[0].Utxo.ControlProgram),
358+ },
359+ }),
360+
361+ // partial matched transaction from Btc2EthOrders[3], Eth2BtcOrders[2]
362+ types.NewTx(types.TxData{
363+ Inputs: []*types.TxInput{
364+ types.NewSpendInput([][]byte{vm.Int64Bytes(810), vm.Int64Bytes(0), vm.Int64Bytes(0)}, *Btc2EthOrders[3].Utxo.SourceID, *Btc2EthOrders[3].FromAssetID, Btc2EthOrders[3].Utxo.Amount, Btc2EthOrders[3].Utxo.SourcePos, Btc2EthOrders[3].Utxo.ControlProgram),
365+ types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *Eth2BtcOrders[2].Utxo.SourceID, *Eth2BtcOrders[2].FromAssetID, Eth2BtcOrders[2].Utxo.Amount, Eth2BtcOrders[2].Utxo.SourcePos, Eth2BtcOrders[2].Utxo.ControlProgram),
366+ },
367+ Outputs: []*types.TxOutput{
368+ types.NewIntraChainOutput(*Btc2EthOrders[3].ToAssetID, 810, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")),
369+ // re-order
370+ types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, 1, Btc2EthOrders[3].Utxo.ControlProgram),
371+ types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 15, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
372+ // fee
373+ types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, 1, NodeProgram),
374+ },
375+ }),
376+
377+ // full matched transaction from Eos2EtcOrders[0] Etc2EosOrders[0]
378+ types.NewTx(types.TxData{
379+ Inputs: []*types.TxInput{
380+ types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *Eos2EtcOrders[0].Utxo.SourceID, *Eos2EtcOrders[0].FromAssetID, Eos2EtcOrders[0].Utxo.Amount, Eos2EtcOrders[0].Utxo.SourcePos, Eos2EtcOrders[0].Utxo.ControlProgram),
381+ types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *Etc2EosOrders[0].Utxo.SourceID, *Etc2EosOrders[0].FromAssetID, Etc2EosOrders[0].Utxo.Amount, Etc2EosOrders[0].Utxo.SourcePos, Etc2EosOrders[0].Utxo.ControlProgram),
382+ },
383+ Outputs: []*types.TxOutput{
384+ types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 50, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
385+ types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")),
386+ },
387+ }),
388+
389+ // full matched transaction from Btc2EthOrders[0], Eth2BtcMakerTxs[0]
390+ types.NewTx(types.TxData{
391+ Inputs: []*types.TxInput{
392+ types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *Btc2EthOrders[0].Utxo.SourceID, *Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, Btc2EthOrders[0].Utxo.SourcePos, Btc2EthOrders[0].Utxo.ControlProgram),
393+ types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Eth2BtcMakerTxs[0], 0).Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, 0, Eth2BtcOrders[0].Utxo.ControlProgram),
394+ },
395+ Outputs: []*types.TxOutput{
396+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
397+ types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
398+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 10, NodeProgram),
399+ },
400+ }),
332401 }
333402 )
334403
--- a/application/mov/mov_core.go
+++ b/application/mov/mov_core.go
@@ -53,15 +53,13 @@ func (m *MovCore) ApplyBlock(block *types.Block) error {
5353 if err := m.movStore.InitDBState(block.Height, &blockHash); err != nil {
5454 return err
5555 }
56-
57- return nil
5856 }
5957
6058 if err := m.validateMatchedTxSequence(block.Transactions); err != nil {
6159 return err
6260 }
6361
64- addOrders, deleteOrders, err := applyTransactions(block.Transactions)
62+ addOrders, deleteOrders, err := decodeTxsOrders(block.Transactions)
6563 if err != nil {
6664 return err
6765 }
@@ -99,11 +97,11 @@ func (m *MovCore) ChainStatus() (uint64, *bc.Hash, error) {
9997 // DetachBlock parse pending order and cancel from the the transactions of block
10098 // and add cancel order to the dex db, remove pending order from dex db.
10199 func (m *MovCore) DetachBlock(block *types.Block) error {
102- if block.Height <= m.startBlockHeight {
100+ if block.Height < m.startBlockHeight {
103101 return nil
104102 }
105103
106- deleteOrders, addOrders, err := applyTransactions(block.Transactions)
104+ deleteOrders, addOrders, err := decodeTxsOrders(block.Transactions)
107105 if err != nil {
108106 return err
109107 }
@@ -147,15 +145,13 @@ func (m *MovCore) ValidateTxs(txs []*types.Tx, verifyResults []*bc.TxVerifyResul
147145 return nil
148146 }
149147
150-// ValidateTxs validate one transaction.
148+// ValidateTx validate one transaction.
151149 func (m *MovCore) ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error {
152150 if common.IsMatchedTx(tx) {
153151 if err := validateMatchedTx(tx, verifyResult); err != nil {
154152 return err
155153 }
156- }
157-
158- if common.IsCancelOrderTx(tx) {
154+ } else if common.IsCancelOrderTx(tx) {
159155 if err := validateCancelOrderTx(tx, verifyResult); err != nil {
160156 return err
161157 }
@@ -260,60 +256,50 @@ func validateMatchedTxFeeAmount(tx *types.Tx) error {
260256 }
261257
262258 func (m *MovCore) validateMatchedTxSequence(txs []*types.Tx) error {
263- var matchedTxs []*types.Tx
259+ orderBook := match.NewOrderBook(m.movStore, nil, nil)
264260 for _, tx := range txs {
265261 if common.IsMatchedTx(tx) {
266- matchedTxs = append(matchedTxs, tx)
267- }
268- }
269-
270- if len(matchedTxs) == 0 {
271- return nil
272- }
273-
274- orderBook, err := buildOrderBook(m.movStore, txs)
275- if err != nil {
276- return err
277- }
278-
279- for _, matchedTx := range matchedTxs {
280- tradePairs, err := getTradePairsFromMatchedTx(matchedTx)
281- if err != nil {
282- return err
283- }
284-
285- orders := orderBook.PeekOrders(tradePairs)
286- if !match.IsMatched(orders) {
287- return errNotMatchedOrder
288- }
289-
290- if err := validateSpendOrders(matchedTx, orders); err != nil {
291- return err
292- }
293-
294- orderBook.PopOrders(tradePairs)
262+ tradePairs, err := getTradePairsFromMatchedTx(tx)
263+ if err != nil {
264+ return err
265+ }
295266
296- for i, output := range matchedTx.Outputs {
297- if !segwit.IsP2WMCScript(output.ControlProgram()) {
298- continue
267+ orders := orderBook.PeekOrders(tradePairs)
268+ if err := validateSpendOrders(tx, orders); err != nil {
269+ return err
299270 }
300271
301- order, err := common.NewOrderFromOutput(matchedTx, i)
272+ orderBook.PopOrders(tradePairs)
273+ } else if common.IsCancelOrderTx(tx) {
274+ orders, err := getDeleteOrdersFromTx(tx)
302275 if err != nil {
303276 return err
304277 }
305278
306- if err := orderBook.AddOrder(order); err != nil {
307- return err
279+ for _, order := range orders {
280+ orderBook.DelOrder(order)
308281 }
309282 }
283+
284+ addOrders, err := getAddOrdersFromTx(tx)
285+ if err != nil {
286+ return err
287+ }
288+
289+ for _, order := range addOrders {
290+ orderBook.AddOrder(order)
291+ }
310292 }
311293 return nil
312294 }
313295
314-func validateSpendOrders(matchedTx *types.Tx, orders []*common.Order) error {
296+func validateSpendOrders(tx *types.Tx, orders []*common.Order) error {
297+ if len(tx.Inputs) != len(orders) {
298+ return errNotMatchedOrder
299+ }
300+
315301 spendOutputIDs := make(map[string]bool)
316- for _, input := range matchedTx.Inputs {
302+ for _, input := range tx.Inputs {
317303 spendOutputID, err := input.SpentOutputID()
318304 if err != nil {
319305 return err
@@ -331,7 +317,7 @@ func validateSpendOrders(matchedTx *types.Tx, orders []*common.Order) error {
331317 return nil
332318 }
333319
334-func applyTransactions(txs []*types.Tx) ([]*common.Order, []*common.Order, error) {
320+func decodeTxsOrders(txs []*types.Tx) ([]*common.Order, []*common.Order, error) {
335321 deleteOrderMap := make(map[string]*common.Order)
336322 addOrderMap := make(map[string]*common.Order)
337323 for _, tx := range txs {
@@ -385,10 +371,6 @@ func buildOrderBook(store database.MovStore, txs []*types.Tx) (*match.OrderBook,
385371 return match.NewOrderBook(store, arrivalAddOrders, arrivalDelOrders), nil
386372 }
387373
388-func calcMatchedTxGasUsed(tx *types.Tx) int64 {
389- return int64(len(tx.Inputs))*150 + int64(tx.SerializedSize)
390-}
391-
392374 func getAddOrdersFromTx(tx *types.Tx) ([]*common.Order, error) {
393375 var orders []*common.Order
394376 for i, output := range tx.Outputs {
--- a/application/mov/mov_core_test.go
+++ b/application/mov/mov_core_test.go
@@ -121,11 +121,11 @@ func TestApplyBlock(t *testing.T) {
121121 Transactions: []*types.Tx{
122122 mock.Eos2EtcMakerTxs[0],
123123 mock.Btc2EthMakerTxs[0],
124+ mock.Eth2BtcMakerTxs[1],
124125 mock.MatchedTxs[4],
125126 mock.Eth2EosMakerTxs[0],
126- mock.Eth2BtcMakerTxs[1],
127- mock.MatchedTxs[5],
128127 mock.Etc2EosMakerTxs[0],
128+ mock.MatchedTxs[5],
129129 },
130130 },
131131 blockFunc: applyBlock,
@@ -137,6 +137,23 @@ func TestApplyBlock(t *testing.T) {
137137 wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
138138 },
139139 {
140+ desc: "apply block has partial matched transaction chain",
141+ block: &types.Block{
142+ BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
143+ Transactions: []*types.Tx{
144+ mock.Btc2EthMakerTxs[0],
145+ mock.Eth2BtcMakerTxs[1],
146+ mock.MatchedTxs[4],
147+ mock.Eth2BtcMakerTxs[0],
148+ mock.MatchedTxs[7],
149+ },
150+ },
151+ blockFunc: applyBlock,
152+ initOrders: []*common.Order{},
153+ wantOrders: []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[7], 2)},
154+ wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
155+ },
156+ {
140157 desc: "detach block has pending order transaction",
141158 block: &types.Block{
142159 BlockHeader: *initBlockHeader,
@@ -579,6 +596,144 @@ func TestBeforeProposalBlock(t *testing.T) {
579596 }
580597 }
581598
599+func TestValidateMatchedTxSequence(t *testing.T) {
600+ cases := []struct {
601+ desc string
602+ initOrders []*common.Order
603+ transactions []*types.Tx
604+ wantError error
605+ }{
606+ {
607+ desc: "both db orders and transactions is empty",
608+ initOrders: []*common.Order{},
609+ transactions: []*types.Tx{},
610+ wantError: nil,
611+ },
612+ {
613+ desc: "existing matched orders in db, and transactions is empty",
614+ initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
615+ transactions: []*types.Tx{},
616+ wantError: nil,
617+ },
618+ {
619+ desc: "db orders is empty, but transactions has matched tx",
620+ initOrders: []*common.Order{},
621+ transactions: []*types.Tx{mock.MatchedTxs[1]},
622+ wantError: errNotMatchedOrder,
623+ },
624+ {
625+ desc: "existing matched orders in db, and corresponding matched tx in transactions",
626+ initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
627+ transactions: []*types.Tx{mock.MatchedTxs[1]},
628+ wantError: nil,
629+ },
630+ {
631+ desc: "package matched tx, one order from db, and the another order from transactions",
632+ initOrders: []*common.Order{mock.Btc2EthOrders[0]},
633+ transactions: []*types.Tx{mock.Eth2BtcMakerTxs[0], mock.MatchedTxs[10]},
634+ wantError: nil,
635+ },
636+ {
637+ desc: "two matched txs use the same orders",
638+ initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
639+ transactions: []*types.Tx{mock.MatchedTxs[1], mock.MatchedTxs[1]},
640+ wantError: errNotMatchedOrder,
641+ },
642+ {
643+ desc: "existing two matched orders in db, and only one corresponding matched tx in transactions",
644+ initOrders: []*common.Order{
645+ mock.Btc2EthOrders[3], mock.Eth2BtcOrders[2],
646+ mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
647+ },
648+ transactions: []*types.Tx{mock.MatchedTxs[8]},
649+ wantError: nil,
650+ },
651+ {
652+ desc: "existing two matched orders in db, and the sequence of match txs in incorrect",
653+ initOrders: []*common.Order{
654+ mock.Btc2EthOrders[3], mock.Eth2BtcOrders[2],
655+ mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
656+ },
657+ transactions: []*types.Tx{mock.MatchedTxs[1], mock.MatchedTxs[8]},
658+ wantError: errSpendOutputIDIsIncorrect,
659+ },
660+ {
661+ desc: "matched tx and orders from packaged transactions",
662+ initOrders: []*common.Order{},
663+ transactions: []*types.Tx{mock.Btc2EthMakerTxs[0], mock.Eth2BtcMakerTxs[1], mock.MatchedTxs[4]},
664+ wantError: nil,
665+ },
666+ {
667+ desc: "package the matched tx first, then package match orders",
668+ initOrders: []*common.Order{},
669+ transactions: []*types.Tx{mock.MatchedTxs[4], mock.Btc2EthMakerTxs[0], mock.Eth2BtcMakerTxs[1]},
670+ wantError: errNotMatchedOrder,
671+ },
672+ {
673+ desc: "cancel order in transactions",
674+ initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
675+ transactions: []*types.Tx{mock.Btc2EthCancelTxs[0], mock.MatchedTxs[1]},
676+ wantError: errNotMatchedOrder,
677+ },
678+ {
679+ desc: "package cancel order after match tx",
680+ initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
681+ transactions: []*types.Tx{mock.MatchedTxs[1], mock.Btc2EthCancelTxs[0]},
682+ wantError: nil,
683+ },
684+ {
685+ desc: "package matched txs of different trade pairs",
686+ initOrders: []*common.Order{
687+ mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
688+ mock.Eos2EtcOrders[0], mock.Etc2EosOrders[0],
689+ },
690+ transactions: []*types.Tx{mock.MatchedTxs[1], mock.MatchedTxs[9]},
691+ wantError: nil,
692+ },
693+ {
694+ desc: "package matched txs of different trade pairs in different sequence",
695+ initOrders: []*common.Order{
696+ mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
697+ mock.Eos2EtcOrders[0], mock.Etc2EosOrders[0],
698+ },
699+ transactions: []*types.Tx{mock.MatchedTxs[9], mock.MatchedTxs[1]},
700+ wantError: nil,
701+ },
702+ {
703+ desc: "package partial matched tx from db orders, and the re-pending order continue to match",
704+ initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
705+ transactions: []*types.Tx{mock.MatchedTxs[2], mock.MatchedTxs[3]},
706+ wantError: nil,
707+ },
708+ {
709+ desc: "cancel the re-pending order",
710+ initOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
711+ transactions: []*types.Tx{mock.MatchedTxs[2], mock.Btc2EthCancelTxs[1], mock.MatchedTxs[3]},
712+ wantError: errNotMatchedOrder,
713+ },
714+ }
715+
716+ for i, c := range cases {
717+ testDB := dbm.NewDB("testdb", "leveldb", "temp")
718+ store := database.NewLevelDBMovStore(testDB)
719+ if err := store.InitDBState(0, &bc.Hash{}); err != nil {
720+ t.Fatal(err)
721+ }
722+
723+ if err := store.ProcessOrders(c.initOrders, nil, initBlockHeader); err != nil {
724+ t.Fatal(err)
725+ }
726+
727+ movCore := &MovCore{movStore: store}
728+ if err := movCore.validateMatchedTxSequence(c.transactions); err != c.wantError {
729+ t.Errorf("#%d(%s):wanet error(%v), got error(%v)", i, c.desc, c.wantError, err)
730+ }
731+
732+ testDB.Close()
733+ os.RemoveAll("temp")
734+ }
735+}
736+
582737 type testFun func(movCore *MovCore, block *types.Block) error
583738
584739 func applyBlock(movCore *MovCore, block *types.Block) error {
--- a/cmd/vapord/commands/run_node.go
+++ b/cmd/vapord/commands/run_node.go
@@ -21,6 +21,7 @@ var runNodeCmd = &cobra.Command{
2121 func init() {
2222 runNodeCmd.Flags().String("prof_laddr", config.ProfListenAddress, "Use http to profile vapord programs")
2323 runNodeCmd.Flags().Bool("mining", config.Mining, "Enable mining")
24+ runNodeCmd.Flags().String("cross_chain.asset_whitelist", config.CrossChain.AssetWhitelist, "Cross-chain-allowed asset whitelist")
2425
2526 runNodeCmd.Flags().Bool("auth.disable", config.Auth.Disable, "Disable rpc access authenticate")
2627
--- a/config/config.go
+++ b/config/config.go
@@ -28,6 +28,7 @@ type Config struct {
2828 Web *WebConfig `mapstructure:"web"`
2929 Websocket *WebsocketConfig `mapstructure:"ws"`
3030 Federation *FederationConfig `mapstructure:"federation"`
31+ CrossChain *CrossChainConfig `mapstructure:"cross_chain"`
3132 }
3233
3334 // Default configurable parameters.
@@ -40,6 +41,7 @@ func DefaultConfig() *Config {
4041 Web: DefaultWebConfig(),
4142 Websocket: DefaultWebsocketConfig(),
4243 Federation: DefaultFederationConfig(),
44+ CrossChain: DefaultCrossChainConfig(),
4345 }
4446 }
4547
@@ -214,6 +216,10 @@ type FederationConfig struct {
214216 Quorum int `json:"quorum"`
215217 }
216218
219+type CrossChainConfig struct {
220+ AssetWhitelist string `mapstructure:"asset_whitelist"`
221+}
222+
217223 // Default configurable rpc's auth parameters.
218224 func DefaultRPCAuthConfig() *RPCAuthConfig {
219225 return &RPCAuthConfig{
@@ -238,6 +244,7 @@ func DefaultWalletConfig() *WalletConfig {
238244 }
239245 }
240246
247+// Default configurable websocket parameters.
241248 func DefaultWebsocketConfig() *WebsocketConfig {
242249 return &WebsocketConfig{
243250 MaxNumWebsockets: 25,
@@ -245,6 +252,7 @@ func DefaultWebsocketConfig() *WebsocketConfig {
245252 }
246253 }
247254
255+// Default configurable federation parameters.
248256 func DefaultFederationConfig() *FederationConfig {
249257 return &FederationConfig{
250258 Xpubs: []chainkd.XPub{
@@ -257,6 +265,11 @@ func DefaultFederationConfig() *FederationConfig {
257265 }
258266 }
259267
268+// Default configurable crosschain parameters.
269+func DefaultCrossChainConfig() *CrossChainConfig {
270+ return &CrossChainConfig{}
271+}
272+
260273 func xpub(str string) (xpub chainkd.XPub) {
261274 if err := xpub.UnmarshalText([]byte(str)); err != nil {
262275 log.Panicf("Fail converts a string to xpub")
--- a/config/toml.go
+++ b/config/toml.go
@@ -31,18 +31,24 @@ var mainNetConfigTmpl = `chain_id = "mainnet"
3131 [p2p]
3232 laddr = "tcp://0.0.0.0:56656"
3333 seeds = "47.103.79.68:56656,47.103.13.86:56656,47.102.193.119:56656,47.103.17.22:56656"
34+[cross_chain]
35+asset_whitelist = ""
3436 `
3537
3638 var testNetConfigTmpl = `chain_id = "testnet"
3739 [p2p]
3840 laddr = "tcp://0.0.0.0:56657"
3941 seeds = "52.82.7.233:56657,52.82.109.252:56657,52.82.29.30:56657"
42+[cross_chain]
43+asset_whitelist = ""
4044 `
4145
4246 var soloNetConfigTmpl = `chain_id = "solonet"
4347 [p2p]
4448 laddr = "tcp://0.0.0.0:56658"
4549 seeds = ""
50+[cross_chain]
51+asset_whitelist = ""
4652 `
4753
4854 // Select network seeds to merge a new string.
--- a/node/node.go
+++ b/node/node.go
@@ -97,7 +97,8 @@ func NewNode(config *cfg.Config) *Node {
9797
9898 dispatcher := event.NewDispatcher()
9999 movCore := mov.NewMovCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight)
100- txPool := protocol.NewTxPool(store, []protocol.DustFilterer{movCore}, dispatcher)
100+ assetFilter := protocol.NewAssetFilter(config.CrossChain.AssetWhitelist)
101+ txPool := protocol.NewTxPool(store, []protocol.DustFilterer{movCore, assetFilter}, dispatcher)
101102 chain, err := protocol.NewChain(store, txPool, []protocol.Protocoler{movCore}, dispatcher)
102103 if err != nil {
103104 cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))
--- a/proposal/proposal.go
+++ b/proposal/proposal.go
@@ -94,6 +94,7 @@ func (b *blockBuilder) applyCoinbaseTransaction() error {
9494 b.gasLeft -= gasState.GasUsed
9595 return nil
9696 }
97+
9798 func (b *blockBuilder) applyTransactions(txs []*types.Tx, timeoutStatus uint8) error {
9899 tempTxs := []*types.Tx{}
99100 for i := 0; i < len(txs); i++ {
@@ -258,6 +259,7 @@ func createCoinbaseTxByReward(accountManager *account.Manager, blockHeight uint6
258259 if err = builder.AddInput(types.NewCoinbaseInput(arbitrary), &txbuilder.SigningInstruction{}); err != nil {
259260 return nil, err
260261 }
262+
261263 if err = builder.AddOutput(types.NewIntraChainOutput(*consensus.BTMAssetID, 0, script)); err != nil {
262264 return nil, err
263265 }
@@ -294,7 +296,6 @@ type validateTxResult struct {
294296
295297 func preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoViewpoint, gasLeft int64) ([]*validateTxResult, int64) {
296298 var results []*validateTxResult
297-
298299 bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: chain.BestBlockHeight() + 1}}
299300 bcTxs := make([]*bc.Tx, len(txs))
300301 for i, tx := range txs {
--- /dev/null
+++ b/protocol/asset_filter.go
@@ -0,0 +1,41 @@
1+package protocol
2+
3+import (
4+ "strings"
5+
6+ "github.com/bytom/vapor/consensus"
7+ "github.com/bytom/vapor/protocol/bc/types"
8+)
9+
10+type assetFilter struct {
11+ whitelist map[string]struct{}
12+}
13+
14+// NewAssetFilter returns a assetFilter according a whitelist,
15+// which is a strings list cancated via comma
16+func NewAssetFilter(whitelist string) *assetFilter {
17+ af := &assetFilter{whitelist: make(map[string]struct{})}
18+ af.whitelist[consensus.BTMAssetID.String()] = struct{}{}
19+ for _, assetID := range strings.Split(whitelist, ",") {
20+ af.whitelist[strings.ToLower(assetID)] = struct{}{}
21+ }
22+ return af
23+}
24+
25+// IsDust implements the DustFilterer interface.
26+// It filters a transaction as long as there is one asset neither BTM or in the whitelist
27+// No need to check the output assets types becauese they must have been cover in input assets types
28+func (af *assetFilter) IsDust(tx *types.Tx) bool {
29+ for _, input := range tx.Inputs {
30+ if _, ok := input.TypedInput.(*types.CrossChainInput); !ok {
31+ continue
32+ }
33+
34+ assetID := input.AssetID()
35+ if _, ok := af.whitelist[assetID.String()]; !ok {
36+ return true
37+ }
38+ }
39+
40+ return false
41+}
Show on old repository browser