• R/O
  • HTTP
  • SSH
  • HTTPS

vapor: Commit

Golang implemented sidechain for Bytom


Commit MetaInfo

Revisionc673035bba5f28d1b2b0432a3d6485315eb91460 (tree)
Time2019-11-06 01:59:00
Authorpaladz <453256728@qq.c...>
Commiterpaladz

Log Message

same change while go over the codes

Change Summary

Incremental Difference

--- a/application/mov/common/type.go
+++ b/application/mov/common/type.go
@@ -10,6 +10,7 @@ import (
1010 "github.com/vapor/protocol/bc/types"
1111 )
1212
13+// MovUtxo store the utxo information for mov order
1314 type MovUtxo struct {
1415 SourceID *bc.Hash
1516 SourcePos uint64
@@ -17,6 +18,7 @@ type MovUtxo struct {
1718 ControlProgram []byte
1819 }
1920
21+// Order store all the order information
2022 type Order struct {
2123 FromAssetID *bc.AssetID
2224 ToAssetID *bc.AssetID
@@ -24,14 +26,11 @@ type Order struct {
2426 Rate float64
2527 }
2628
29+// OrderSlice is define for order's sort
2730 type OrderSlice []*Order
2831
29-func (o OrderSlice) Len() int {
30- return len(o)
31-}
32-func (o OrderSlice) Swap(i, j int) {
33- o[i], o[j] = o[j], o[i]
34-}
32+func (o OrderSlice) Len() int { return len(o) }
33+func (o OrderSlice) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
3534 func (o OrderSlice) Less(i, j int) bool {
3635 if o[i].Rate == o[j].Rate {
3736 return hex.EncodeToString(o[i].UTXOHash().Bytes()) < hex.EncodeToString(o[j].UTXOHash().Bytes())
@@ -39,6 +38,7 @@ func (o OrderSlice) Less(i, j int) bool {
3938 return o[i].Rate < o[j].Rate
4039 }
4140
41+// NewOrderFromOutput convert txinput to order
4242 func NewOrderFromOutput(tx *types.Tx, outputIndex int) (*Order, error) {
4343 outputID := tx.OutputID(outputIndex)
4444 output, err := tx.IntraChainOutput(*outputID)
@@ -65,6 +65,7 @@ func NewOrderFromOutput(tx *types.Tx, outputIndex int) (*Order, error) {
6565 }, nil
6666 }
6767
68+// NewOrderFromInput convert txoutput to order
6869 func NewOrderFromInput(tx *types.Tx, inputIndex int) (*Order, error) {
6970 input, ok := tx.Inputs[inputIndex].TypedInput.(*types.SpendInput)
7071 if !ok {
@@ -83,12 +84,23 @@ func NewOrderFromInput(tx *types.Tx, inputIndex int) (*Order, error) {
8384 Utxo: &MovUtxo{
8485 SourceID: &input.SourceID,
8586 Amount: input.Amount,
86- SourcePos: input.SourcePosition,
87+ SourcePos: input.SourcePosition,
8788 ControlProgram: input.ControlProgram,
8889 },
8990 }, nil
9091 }
9192
93+// Key return the unique key for representing this order
94+func (o *Order) Key() string {
95+ return fmt.Sprintf("%s:%d", o.Utxo.SourceID, o.Utxo.SourcePos)
96+}
97+
98+// TradePair return the trade pair info
99+func (o *Order) TradePair() *TradePair {
100+ return &TradePair{FromAssetID: o.FromAssetID, ToAssetID: o.ToAssetID}
101+}
102+
103+// UTXOHash calculate the utxo hash of this order
92104 func (o *Order) UTXOHash() *bc.Hash {
93105 prog := &bc.Program{VmVersion: 1, Code: o.Utxo.ControlProgram}
94106 src := &bc.ValueSource{
@@ -100,20 +112,19 @@ func (o *Order) UTXOHash() *bc.Hash {
100112 return &hash
101113 }
102114
103-func (o *Order) TradePair() *TradePair {
104- return &TradePair{FromAssetID: o.FromAssetID, ToAssetID: o.ToAssetID}
105-}
106-
107-func (o *Order) Key() string {
108- return fmt.Sprintf("%s:%d", o.Utxo.SourceID, o.Utxo.SourcePos)
109-}
110-
115+// TradePair is the object for record trade pair info
111116 type TradePair struct {
112117 FromAssetID *bc.AssetID
113118 ToAssetID *bc.AssetID
114119 Count int
115120 }
116121
122+// Key return the unique key for representing this trade pair
123+func (t *TradePair) Key() string {
124+ return fmt.Sprintf("%s:%s", t.FromAssetID, t.ToAssetID)
125+}
126+
127+// Reverse return the reverse trade pair object
117128 func (t *TradePair) Reverse() *TradePair {
118129 return &TradePair{
119130 FromAssetID: t.ToAssetID,
@@ -121,10 +132,7 @@ func (t *TradePair) Reverse() *TradePair {
121132 }
122133 }
123134
124-func (t *TradePair) Key() string {
125- return fmt.Sprintf("%s:%s", t.FromAssetID, t.ToAssetID)
126-}
127-
135+// MovDatabaseState is object to record DB image status
128136 type MovDatabaseState struct {
129137 Height uint64
130138 Hash *bc.Hash
--- a/application/mov/common/util.go
+++ b/application/mov/common/util.go
@@ -6,6 +6,7 @@ import (
66 "github.com/vapor/protocol/bc/types"
77 )
88
9+// IsMatchedTx check if this transaction has trade mov order input
910 func IsMatchedTx(tx *types.Tx) bool {
1011 if len(tx.Inputs) < 2 {
1112 return false
@@ -18,6 +19,7 @@ func IsMatchedTx(tx *types.Tx) bool {
1819 return false
1920 }
2021
22+// IsCancelOrderTx check if this transaction has cancel mov order input
2123 func IsCancelOrderTx(tx *types.Tx) bool {
2224 for _, input := range tx.Inputs {
2325 if input.InputType() == types.SpendInputType && contract.IsCancelClauseSelector(input) && segwit.IsP2WMCScript(input.ControlProgram()) {
@@ -26,4 +28,3 @@ func IsCancelOrderTx(tx *types.Tx) bool {
2628 }
2729 return false
2830 }
29-
--- a/application/mov/contract/contract.go
+++ b/application/mov/contract/contract.go
@@ -8,29 +8,34 @@ import (
88 )
99
1010 const (
11- sizeOfCancelClauseArgs = 3
11+ sizeOfCancelClauseArgs = 3
1212 sizeOfPartialTradeClauseArgs = 3
13- sizeOfFullTradeClauseArgs = 2
13+ sizeOfFullTradeClauseArgs = 2
1414 )
1515
16+// smart contract clause select for differnet unlock method
1617 const (
1718 PartialTradeClauseSelector int64 = iota
1819 FullTradeClauseSelector
1920 CancelClauseSelector
2021 )
2122
23+// IsCancelClauseSelector check if input select cancel clause
2224 func IsCancelClauseSelector(input *types.TxInput) bool {
23- return len(input.Arguments()) == sizeOfCancelClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments()) - 1]) == hex.EncodeToString(vm.Int64Bytes(CancelClauseSelector))
25+ return len(input.Arguments()) == sizeOfCancelClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments())-1]) == hex.EncodeToString(vm.Int64Bytes(CancelClauseSelector))
2426 }
2527
28+// IsTradeClauseSelector check if input select is partial trade clause or full trade clause
2629 func IsTradeClauseSelector(input *types.TxInput) bool {
2730 return IsPartialTradeClauseSelector(input) || IsFullTradeClauseSelector(input)
2831 }
2932
33+// IsPartialTradeClauseSelector check if input select partial trade clause
3034 func IsPartialTradeClauseSelector(input *types.TxInput) bool {
31- return len(input.Arguments()) == sizeOfPartialTradeClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments()) - 1]) == hex.EncodeToString(vm.Int64Bytes(PartialTradeClauseSelector))
35+ return len(input.Arguments()) == sizeOfPartialTradeClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments())-1]) == hex.EncodeToString(vm.Int64Bytes(PartialTradeClauseSelector))
3236 }
3337
38+// IsFullTradeClauseSelector check if input select full trade clause
3439 func IsFullTradeClauseSelector(input *types.TxInput) bool {
35- return len(input.Arguments()) == sizeOfFullTradeClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments()) - 1]) == hex.EncodeToString(vm.Int64Bytes(FullTradeClauseSelector))
40+ return len(input.Arguments()) == sizeOfFullTradeClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments())-1]) == hex.EncodeToString(vm.Int64Bytes(FullTradeClauseSelector))
3641 }
--- a/application/mov/database/mov_iterator.go
+++ b/application/mov/database/mov_iterator.go
@@ -7,21 +7,25 @@ import (
77 "github.com/vapor/protocol/bc"
88 )
99
10+// TradePairIterator wrap read trade pair from DB action
1011 type TradePairIterator struct {
1112 movStore MovStore
1213 tradePairs []*common.TradePair
1314 tradePairIndex int
1415 }
1516
17+// NewTradePairIterator create the new TradePairIterator object
1618 func NewTradePairIterator(movStore MovStore) *TradePairIterator {
1719 return &TradePairIterator{movStore: movStore}
1820 }
1921
22+// HasNext check if there are more trade pairs in memory or DB
2023 func (t *TradePairIterator) HasNext() bool {
2124 tradePairSize := len(t.tradePairs)
2225 if t.tradePairIndex < tradePairSize {
2326 return true
2427 }
28+
2529 var fromAssetID, toAssetID *bc.AssetID
2630 if len(t.tradePairs) > 0 {
2731 lastTradePair := t.tradePairs[tradePairSize-1]
@@ -44,6 +48,7 @@ func (t *TradePairIterator) HasNext() bool {
4448 return true
4549 }
4650
51+// Next return the next available trade pair in memory or DB
4752 func (t *TradePairIterator) Next() *common.TradePair {
4853 if !t.HasNext() {
4954 return nil
@@ -54,12 +59,14 @@ func (t *TradePairIterator) Next() *common.TradePair {
5459 return tradePair
5560 }
5661
62+// OrderIterator wrap read order from DB action
5763 type OrderIterator struct {
5864 movStore MovStore
5965 lastOrder *common.Order
6066 orders []*common.Order
6167 }
6268
69+// NewOrderIterator create the new OrderIterator object
6370 func NewOrderIterator(movStore MovStore, tradePair *common.TradePair) *OrderIterator {
6471 return &OrderIterator{
6572 movStore: movStore,
@@ -67,6 +74,7 @@ func NewOrderIterator(movStore MovStore, tradePair *common.TradePair) *OrderIter
6774 }
6875 }
6976
77+// HasNext check if there are more orders in memory or DB
7078 func (o *OrderIterator) HasNext() bool {
7179 if len(o.orders) == 0 {
7280 orders, err := o.movStore.ListOrders(o.lastOrder)
@@ -84,6 +92,7 @@ func (o *OrderIterator) HasNext() bool {
8492 return true
8593 }
8694
95+// NextBatch return the next batch of orders in memory or DB
8796 func (o *OrderIterator) NextBatch() []*common.Order {
8897 if !o.HasNext() {
8998 return nil
--- a/application/mov/database/mov_iterator_test.go
+++ b/application/mov/database/mov_iterator_test.go
@@ -126,26 +126,26 @@ func TestOrderIterator(t *testing.T) {
126126 wantOrders []*common.Order
127127 }{
128128 {
129- desc: "normal case",
130- tradePair: &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2},
129+ desc: "normal case",
130+ tradePair: &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2},
131131 storeOrders: []*common.Order{order1, order2, order3},
132132 wantOrders: []*common.Order{order1, order2, order3},
133133 },
134134 {
135- desc: "num of orders more than one return",
136- tradePair: &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2},
135+ desc: "num of orders more than one return",
136+ tradePair: &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2},
137137 storeOrders: []*common.Order{order1, order2, order3, order4, order5},
138138 wantOrders: []*common.Order{order1, order2, order3, order4, order5},
139139 },
140140 {
141- desc: "only one order",
142- tradePair: &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2},
141+ desc: "only one order",
142+ tradePair: &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2},
143143 storeOrders: []*common.Order{order1},
144144 wantOrders: []*common.Order{order1},
145145 },
146146 {
147- desc: "store is empty",
148- tradePair: &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2},
147+ desc: "store is empty",
148+ tradePair: &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2},
149149 storeOrders: []*common.Order{},
150150 wantOrders: []*common.Order{},
151151 },
--- a/application/mov/match/match.go
+++ b/application/mov/match/match.go
@@ -15,16 +15,19 @@ import (
1515 "github.com/vapor/protocol/vm/vmutil"
1616 )
1717
18+// Engine is used to generate math transactions
1819 type Engine struct {
1920 orderTable *OrderTable
2021 maxFeeRate float64
2122 nodeProgram []byte
2223 }
2324
25+// NewEngine return a new Engine
2426 func NewEngine(orderTable *OrderTable, maxFeeRate float64, nodeProgram []byte) *Engine {
2527 return &Engine{orderTable: orderTable, maxFeeRate: maxFeeRate, nodeProgram: nodeProgram}
2628 }
2729
30+// HasMatchedTx check does the input trade pair can generate a match deal
2831 func (e *Engine) HasMatchedTx(tradePairs ...*common.TradePair) bool {
2932 if err := validateTradePairs(tradePairs); err != nil {
3033 return false
@@ -55,141 +58,51 @@ func (e *Engine) NextMatchedTx(tradePairs ...*common.TradePair) (*types.Tx, erro
5558 e.orderTable.PopOrder(tradePair)
5659 }
5760
58- if err := addPartialTradeOrder(tx, e.orderTable); err != nil {
61+ if err := e.addPartialTradeOrder(tx); err != nil {
5962 return nil, err
6063 }
6164 return tx, nil
6265 }
6366
64-func (e *Engine) peekOrders(tradePairs []*common.TradePair) []*common.Order {
65- var orders []*common.Order
66- for _, tradePair := range tradePairs {
67- order := e.orderTable.PeekOrder(tradePair)
68- if order == nil {
69- return nil
70- }
71-
72- orders = append(orders, order)
73- }
74- return orders
75-}
76-
77-func validateTradePairs(tradePairs []*common.TradePair) error {
78- if len(tradePairs) < 2 {
79- return errors.New("size of trade pairs at least 2")
80- }
81-
82- for i, tradePair := range tradePairs {
83- oppositeTradePair := tradePairs[getOppositeIndex(len(tradePairs), i)]
84- if *tradePair.ToAssetID != *oppositeTradePair.FromAssetID {
85- return errors.New("specified trade pairs is invalid")
86- }
87- }
88- return nil
89-}
90-
91-func isMatched(orders []*common.Order) bool {
92- for i, order := range orders {
93- opposisteOrder := orders[getOppositeIndex(len(orders), i)]
94- if 1 / order.Rate < opposisteOrder.Rate {
95- return false
96- }
97- }
98- return true
99-}
100-
101-func (e *Engine) buildMatchTx(orders []*common.Order) (*types.Tx, error) {
102- txData := &types.TxData{Version: 1}
103- for i, order := range orders {
104- input := types.NewSpendInput(nil, *order.Utxo.SourceID, *order.FromAssetID, order.Utxo.Amount, order.Utxo.SourcePos, order.Utxo.ControlProgram)
105- txData.Inputs = append(txData.Inputs, input)
106-
107- oppositeOrder := orders[getOppositeIndex(len(orders), i)]
108- if err := addMatchTxOutput(txData, input, order, oppositeOrder.Utxo.Amount); err != nil {
109- return nil, err
110- }
111- }
112-
113- if err := e.addMatchTxFeeOutput(txData); err != nil {
114- return nil, err
115- }
116-
117- byteData, err := txData.MarshalText()
118- if err != nil {
119- return nil, err
120- }
121-
122- txData.SerializedSize = uint64(len(byteData))
123- return types.NewTx(*txData), nil
124-}
125-
126-func addMatchTxOutput(txData *types.TxData, txInput *types.TxInput, order *common.Order, oppositeAmount uint64) error {
127- contractArgs, err := segwit.DecodeP2WMCProgram(order.Utxo.ControlProgram)
128- if err != nil {
129- return err
130- }
131-
132- requestAmount := calcRequestAmount(order.Utxo.Amount, contractArgs)
133- receiveAmount := vprMath.MinUint64(requestAmount, oppositeAmount)
134- shouldPayAmount := CalcShouldPayAmount(receiveAmount, contractArgs)
135- isPartialTrade := requestAmount > receiveAmount
136-
137- setMatchTxArguments(txInput, isPartialTrade, len(txData.Outputs), receiveAmount)
138- txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, receiveAmount, contractArgs.SellerProgram))
139- if isPartialTrade {
140- txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.FromAssetID, order.Utxo.Amount-shouldPayAmount, order.Utxo.ControlProgram))
141- }
142- return nil
143-}
144-
14567 func (e *Engine) addMatchTxFeeOutput(txData *types.TxData) error {
14668 txFee, err := CalcMatchedTxFee(txData, e.maxFeeRate)
14769 if err != nil {
14870 return err
14971 }
15072
151- for feeAssetID, amount := range txFee {
152- var reminder int64 = 0
153- feeAmount := amount.FeeAmount
154- if amount.FeeAmount > amount.MaxFeeAmount {
155- feeAmount = amount.MaxFeeAmount
156- reminder = amount.FeeAmount - amount.MaxFeeAmount
73+ for assetID, matchTxFee := range txFee {
74+ feeAmount, reminder := matchTxFee.FeeAmount, int64(0)
75+ if matchTxFee.FeeAmount > matchTxFee.MaxFeeAmount {
76+ feeAmount = matchTxFee.MaxFeeAmount
77+ reminder = matchTxFee.FeeAmount - matchTxFee.MaxFeeAmount
15778 }
158- txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(feeAssetID, uint64(feeAmount), e.nodeProgram))
79+ txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(assetID, uint64(feeAmount), e.nodeProgram))
15980
16081 // There is the remaining amount after paying the handling fee, assign it evenly to participants in the transaction
16182 averageAmount := reminder / int64(len(txData.Inputs))
16283 if averageAmount == 0 {
16384 averageAmount = 1
16485 }
86+
16587 for i := 0; i < len(txData.Inputs) && reminder > 0; i++ {
16688 contractArgs, err := segwit.DecodeP2WMCProgram(txData.Inputs[i].ControlProgram())
16789 if err != nil {
16890 return err
16991 }
17092
171- if i == len(txData.Inputs)-1 {
172- txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(feeAssetID, uint64(reminder), contractArgs.SellerProgram))
173- } else {
174- txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(feeAssetID, uint64(averageAmount), contractArgs.SellerProgram))
93+ if reminder < 2*averageAmount {
94+ txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(assetID, uint64(reminder), contractArgs.SellerProgram))
95+ break
17596 }
97+
98+ txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(assetID, uint64(averageAmount), contractArgs.SellerProgram))
17699 reminder -= averageAmount
177100 }
178101 }
179102 return nil
180103 }
181104
182-func setMatchTxArguments(txInput *types.TxInput, isPartialTrade bool, position int, receiveAmounts uint64) {
183- var arguments [][]byte
184- if isPartialTrade {
185- arguments = [][]byte{vm.Int64Bytes(int64(receiveAmounts)), vm.Int64Bytes(int64(position)), vm.Int64Bytes(contract.PartialTradeClauseSelector)}
186- } else {
187- arguments = [][]byte{vm.Int64Bytes(int64(position)), vm.Int64Bytes(contract.FullTradeClauseSelector)}
188- }
189- txInput.SetArguments(arguments)
190-}
191-
192-func addPartialTradeOrder(tx *types.Tx, orderTable *OrderTable) error {
105+func (e *Engine) addPartialTradeOrder(tx *types.Tx) error {
193106 for i, output := range tx.Outputs {
194107 if !segwit.IsP2WMCScript(output.ControlProgram()) {
195108 continue
@@ -200,41 +113,70 @@ func addPartialTradeOrder(tx *types.Tx, orderTable *OrderTable) error {
200113 return err
201114 }
202115
203- if err := orderTable.AddOrder(order); err != nil {
116+ if err := e.orderTable.AddOrder(order); err != nil {
204117 return err
205118 }
206119 }
207120 return nil
208121 }
209122
210-func getOppositeIndex(size int, selfIdx int) int {
211- oppositeIdx := selfIdx + 1
212- if selfIdx >= size-1 {
213- oppositeIdx = 0
123+func (e *Engine) buildMatchTx(orders []*common.Order) (*types.Tx, error) {
124+ txData := &types.TxData{Version: 1}
125+ for i, order := range orders {
126+ input := types.NewSpendInput(nil, *order.Utxo.SourceID, *order.FromAssetID, order.Utxo.Amount, order.Utxo.SourcePos, order.Utxo.ControlProgram)
127+ txData.Inputs = append(txData.Inputs, input)
128+
129+ oppositeOrder := orders[calcOppositeIndex(len(orders), i)]
130+ if err := addMatchTxOutput(txData, input, order, oppositeOrder.Utxo.Amount); err != nil {
131+ return nil, err
132+ }
133+ }
134+
135+ if err := e.addMatchTxFeeOutput(txData); err != nil {
136+ return nil, err
137+ }
138+
139+ byteData, err := txData.MarshalText()
140+ if err != nil {
141+ return nil, err
142+ }
143+
144+ txData.SerializedSize = uint64(len(byteData))
145+ return types.NewTx(*txData), nil
146+}
147+
148+func (e *Engine) peekOrders(tradePairs []*common.TradePair) []*common.Order {
149+ var orders []*common.Order
150+ for _, tradePair := range tradePairs {
151+ order := e.orderTable.PeekOrder(tradePair)
152+ if order == nil {
153+ return nil
154+ }
155+
156+ orders = append(orders, order)
214157 }
215- return oppositeIdx
158+ return orders
216159 }
217160
161+// MatchedTxFee is object to record the mov tx's fee information
218162 type MatchedTxFee struct {
219163 MaxFeeAmount int64
220164 FeeAmount int64
221165 }
222166
167+// CalcMatchedTxFee is used to calculate tx's MatchedTxFees
223168 func CalcMatchedTxFee(txData *types.TxData, maxFeeRate float64) (map[bc.AssetID]*MatchedTxFee, error) {
224169 assetFeeMap := make(map[bc.AssetID]*MatchedTxFee)
225- sellerProgramMap := make(map[string]bool)
226- assetInputMap := make(map[bc.AssetID]uint64)
170+ dealProgMaps := make(map[string]bool)
227171
228172 for _, input := range txData.Inputs {
229- assetFeeMap[input.AssetID()] = &MatchedTxFee{}
230- assetFeeMap[input.AssetID()].FeeAmount += int64(input.AssetAmount().Amount)
173+ assetFeeMap[input.AssetID()] = &MatchedTxFee{FeeAmount: int64(input.AssetAmount().Amount)}
231174 contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram())
232175 if err != nil {
233176 return nil, err
234177 }
235178
236- sellerProgramMap[hex.EncodeToString(contractArgs.SellerProgram)] = true
237- assetInputMap[input.AssetID()] = input.Amount()
179+ dealProgMaps[hex.EncodeToString(contractArgs.SellerProgram)] = true
238180 }
239181
240182 for _, input := range txData.Inputs {
@@ -243,40 +185,87 @@ func CalcMatchedTxFee(txData *types.TxData, maxFeeRate float64) (map[bc.AssetID]
243185 return nil, err
244186 }
245187
246- oppositeAmount := assetInputMap[contractArgs.RequestedAsset]
188+ oppositeAmount := uint64(assetFeeMap[contractArgs.RequestedAsset].FeeAmount)
247189 receiveAmount := vprMath.MinUint64(calcRequestAmount(input.Amount(), contractArgs), oppositeAmount)
248- assetFeeMap[input.AssetID()].MaxFeeAmount = CalcMaxFeeAmount(CalcShouldPayAmount(receiveAmount, contractArgs), maxFeeRate)
190+ assetFeeMap[input.AssetID()].MaxFeeAmount = calcMaxFeeAmount(calcShouldPayAmount(receiveAmount, contractArgs), maxFeeRate)
249191 }
250192
251193 for _, output := range txData.Outputs {
252- // minus the amount of the re-order
253- if segwit.IsP2WMCScript(output.ControlProgram()) {
254- assetFeeMap[*output.AssetAmount().AssetId].FeeAmount -= int64(output.AssetAmount().Amount)
255- }
256- // minus the amount of seller's receiving output
257- if _, ok := sellerProgramMap[hex.EncodeToString(output.ControlProgram())]; ok {
258- assetID := *output.AssetAmount().AssetId
259- fee, ok := assetFeeMap[assetID]
260- if !ok {
261- continue
262- }
263- fee.FeeAmount -= int64(output.AssetAmount().Amount)
264- if fee.FeeAmount == 0 {
265- delete(assetFeeMap, assetID)
194+ assetAmount := output.AssetAmount()
195+ if _, ok := dealProgMaps[hex.EncodeToString(output.ControlProgram())]; ok || segwit.IsP2WMCScript(output.ControlProgram()) {
196+ assetFeeMap[*assetAmount.AssetId].FeeAmount -= int64(assetAmount.Amount)
197+ if assetFeeMap[*assetAmount.AssetId].FeeAmount <= 0 {
198+ delete(assetFeeMap, *assetAmount.AssetId)
266199 }
267200 }
268201 }
269202 return assetFeeMap, nil
270203 }
271204
205+func addMatchTxOutput(txData *types.TxData, txInput *types.TxInput, order *common.Order, oppositeAmount uint64) error {
206+ contractArgs, err := segwit.DecodeP2WMCProgram(order.Utxo.ControlProgram)
207+ if err != nil {
208+ return err
209+ }
210+
211+ requestAmount := calcRequestAmount(order.Utxo.Amount, contractArgs)
212+ receiveAmount := vprMath.MinUint64(requestAmount, oppositeAmount)
213+ shouldPayAmount := calcShouldPayAmount(receiveAmount, contractArgs)
214+ isPartialTrade := requestAmount > receiveAmount
215+
216+ setMatchTxArguments(txInput, isPartialTrade, len(txData.Outputs), receiveAmount)
217+ txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, receiveAmount, contractArgs.SellerProgram))
218+ if isPartialTrade {
219+ txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.FromAssetID, order.Utxo.Amount-shouldPayAmount, order.Utxo.ControlProgram))
220+ }
221+ return nil
222+}
223+
272224 func calcRequestAmount(fromAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 {
273225 return uint64(int64(fromAmount) * contractArg.RatioNumerator / contractArg.RatioDenominator)
274226 }
275227
276-func CalcShouldPayAmount(receiveAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 {
228+func calcShouldPayAmount(receiveAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 {
277229 return uint64(math.Floor(float64(receiveAmount) * float64(contractArg.RatioDenominator) / float64(contractArg.RatioNumerator)))
278230 }
279231
280-func CalcMaxFeeAmount(shouldPayAmount uint64, maxFeeRate float64) int64 {
232+func calcMaxFeeAmount(shouldPayAmount uint64, maxFeeRate float64) int64 {
281233 return int64(math.Ceil(float64(shouldPayAmount) * maxFeeRate))
282234 }
235+
236+func calcOppositeIndex(size int, selfIdx int) int {
237+ return (selfIdx + 1) % size
238+}
239+
240+func isMatched(orders []*common.Order) bool {
241+ for i, order := range orders {
242+ if opposisteOrder := orders[calcOppositeIndex(len(orders), i)]; 1/order.Rate < opposisteOrder.Rate {
243+ return false
244+ }
245+ }
246+ return true
247+}
248+
249+func setMatchTxArguments(txInput *types.TxInput, isPartialTrade bool, position int, receiveAmounts uint64) {
250+ var arguments [][]byte
251+ if isPartialTrade {
252+ arguments = [][]byte{vm.Int64Bytes(int64(receiveAmounts)), vm.Int64Bytes(int64(position)), vm.Int64Bytes(contract.PartialTradeClauseSelector)}
253+ } else {
254+ arguments = [][]byte{vm.Int64Bytes(int64(position)), vm.Int64Bytes(contract.FullTradeClauseSelector)}
255+ }
256+ txInput.SetArguments(arguments)
257+}
258+
259+func validateTradePairs(tradePairs []*common.TradePair) error {
260+ if len(tradePairs) < 2 {
261+ return errors.New("size of trade pairs at least 2")
262+ }
263+
264+ for i, tradePair := range tradePairs {
265+ oppositeTradePair := tradePairs[calcOppositeIndex(len(tradePairs), i)]
266+ if *tradePair.ToAssetID != *oppositeTradePair.FromAssetID {
267+ return errors.New("specified trade pairs is invalid")
268+ }
269+ }
270+ return nil
271+}
--- a/application/mov/match/match_test.go
+++ b/application/mov/match/match_test.go
@@ -11,6 +11,9 @@ import (
1111 "github.com/vapor/protocol/bc/types"
1212 )
1313
14+/*
15+ Test: validateTradePairs vaild and invaild case for 2, 3 trade pairs
16+*/
1417 func TestGenerateMatchedTxs(t *testing.T) {
1518 btc2eth := &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH}
1619 eth2btc := &common.TradePair{FromAssetID: &mock.ETH, ToAssetID: &mock.BTC}
@@ -108,22 +111,22 @@ func TestCalcMatchedTxFee(t *testing.T) {
108111 wantMatchedTxFee map[bc.AssetID]*MatchedTxFee
109112 }{
110113 {
111- desc: "fee less than max fee",
112- maxFeeRate: 0.05,
114+ desc: "fee less than max fee",
115+ maxFeeRate: 0.05,
113116 wantMatchedTxFee: map[bc.AssetID]*MatchedTxFee{mock.ETH: {FeeAmount: 10, MaxFeeAmount: 26}},
114- tx: &mock.MatchedTxs[1].TxData,
117+ tx: &mock.MatchedTxs[1].TxData,
115118 },
116119 {
117- desc: "fee refund in tx",
118- maxFeeRate: 0.05,
120+ desc: "fee refund in tx",
121+ maxFeeRate: 0.05,
119122 wantMatchedTxFee: map[bc.AssetID]*MatchedTxFee{mock.ETH: {FeeAmount: 27, MaxFeeAmount: 27}},
120- tx: &mock.MatchedTxs[2].TxData,
123+ tx: &mock.MatchedTxs[2].TxData,
121124 },
122125 {
123- desc: "fee is zero",
124- maxFeeRate: 0.05,
126+ desc: "fee is zero",
127+ maxFeeRate: 0.05,
125128 wantMatchedTxFee: map[bc.AssetID]*MatchedTxFee{},
126- tx: &mock.MatchedTxs[0].TxData,
129+ tx: &mock.MatchedTxs[0].TxData,
127130 },
128131 }
129132
--- a/application/mov/match/order_table.go
+++ b/application/mov/match/order_table.go
@@ -8,8 +8,9 @@ import (
88 "github.com/vapor/errors"
99 )
1010
11+// OrderTable is used to handle the mov orders in memory like stack
1112 type OrderTable struct {
12- movStore database.MovStore
13+ movStore database.MovStore
1314 // key of tradePair -> []order
1415 dbOrders map[string][]*common.Order
1516 // key of tradePair -> iterator
@@ -21,6 +22,7 @@ type OrderTable struct {
2122 arrivalDelOrders map[string]*common.Order
2223 }
2324
25+// NewOrderTable create a new OrderTable object
2426 func NewOrderTable(movStore database.MovStore, arrivalAddOrders, arrivalDelOrders []*common.Order) *OrderTable {
2527 return &OrderTable{
2628 movStore: movStore,
@@ -32,21 +34,32 @@ func NewOrderTable(movStore database.MovStore, arrivalAddOrders, arrivalDelOrder
3234 }
3335 }
3436
37+// AddOrder add the in memory temp order to order table
38+func (o *OrderTable) AddOrder(order *common.Order) error {
39+ tradePairKey := order.TradePair().Key()
40+ orders := o.arrivalAddOrders[tradePairKey]
41+ if len(orders) > 0 && order.Rate > orders[len(orders)-1].Rate {
42+ return errors.New("rate of order must less than the min order in order table")
43+ }
44+
45+ o.arrivalAddOrders[tradePairKey] = append(orders, order)
46+ return nil
47+}
48+
49+// PeekOrder return the next lowest order of given trade pair
3550 func (o *OrderTable) PeekOrder(tradePair *common.TradePair) *common.Order {
3651 if len(o.dbOrders[tradePair.Key()]) == 0 {
3752 o.extendDBOrders(tradePair)
3853 }
3954
4055 var nextOrder *common.Order
41-
4256 orders := o.dbOrders[tradePair.Key()]
4357 if len(orders) != 0 {
44- nextOrder = orders[len(orders) - 1]
58+ nextOrder = orders[len(orders)-1]
4559 }
4660
4761 if nextOrder != nil && o.arrivalDelOrders[nextOrder.Key()] != nil {
4862 o.dbOrders[tradePair.Key()] = orders[0 : len(orders)-1]
49- delete(o.arrivalDelOrders, nextOrder.Key())
5063 return o.PeekOrder(tradePair)
5164 }
5265
@@ -57,6 +70,7 @@ func (o *OrderTable) PeekOrder(tradePair *common.TradePair) *common.Order {
5770 return nextOrder
5871 }
5972
73+// PopOrder delete the next lowest order of given trade pair
6074 func (o *OrderTable) PopOrder(tradePair *common.TradePair) {
6175 order := o.PeekOrder(tradePair)
6276 if order == nil {
@@ -64,25 +78,34 @@ func (o *OrderTable) PopOrder(tradePair *common.TradePair) {
6478 }
6579
6680 orders := o.dbOrders[tradePair.Key()]
67- if len(orders) != 0 && orders[len(orders) - 1].Key() == order.Key() {
81+ if len(orders) != 0 && orders[len(orders)-1].Key() == order.Key() {
6882 o.dbOrders[tradePair.Key()] = orders[0 : len(orders)-1]
6983 }
7084
7185 arrivalOrders := o.arrivalAddOrders[tradePair.Key()]
72- if len(arrivalOrders) != 0 && arrivalOrders[len(arrivalOrders) - 1].Key() == order.Key() {
86+ if len(arrivalOrders) != 0 && arrivalOrders[len(arrivalOrders)-1].Key() == order.Key() {
7387 o.arrivalAddOrders[tradePair.Key()] = arrivalOrders[0 : len(arrivalOrders)-1]
7488 }
7589 }
7690
77-func (o *OrderTable) AddOrder(order *common.Order) error {
78- tradePair := order.TradePair()
79- orders := o.dbOrders[tradePair.Key()]
80- if len(orders) > 0 && order.Rate > orders[len(orders)-1].Rate {
81- return errors.New("rate of order must less than the min order in order table")
91+func arrangeArrivalAddOrders(orders []*common.Order) map[string][]*common.Order {
92+ arrivalAddOrderMap := make(map[string][]*common.Order)
93+ for _, order := range orders {
94+ arrivalAddOrderMap[order.TradePair().Key()] = append(arrivalAddOrderMap[order.TradePair().Key()], order)
8295 }
8396
84- o.dbOrders[tradePair.Key()] = append(orders, order)
85- return nil
97+ for _, orders := range arrivalAddOrderMap {
98+ sort.Sort(sort.Reverse(common.OrderSlice(orders)))
99+ }
100+ return arrivalAddOrderMap
101+}
102+
103+func arrangeArrivalDelOrders(orders []*common.Order) map[string]*common.Order {
104+ arrivalDelOrderMap := make(map[string]*common.Order)
105+ for _, order := range orders {
106+ arrivalDelOrderMap[order.Key()] = order
107+ }
108+ return arrivalDelOrderMap
86109 }
87110
88111 func (o *OrderTable) extendDBOrders(tradePair *common.TradePair) {
@@ -99,29 +122,8 @@ func (o *OrderTable) extendDBOrders(tradePair *common.TradePair) {
99122 }
100123
101124 func (o *OrderTable) peekArrivalOrder(tradePair *common.TradePair) *common.Order {
102- arrivalAddOrders := o.arrivalAddOrders[tradePair.Key()]
103- if len(arrivalAddOrders) > 0 {
104- return arrivalAddOrders[len(arrivalAddOrders) -1]
125+ if arrivalAddOrders := o.arrivalAddOrders[tradePair.Key()]; len(arrivalAddOrders) > 0 {
126+ return arrivalAddOrders[len(arrivalAddOrders)-1]
105127 }
106128 return nil
107129 }
108-
109-func arrangeArrivalAddOrders(orders []*common.Order) map[string][]*common.Order {
110- arrivalAddOrderMap := make(map[string][]*common.Order)
111- for _, order := range orders {
112- arrivalAddOrderMap[order.TradePair().Key()] = append(arrivalAddOrderMap[order.TradePair().Key()], order)
113- }
114-
115- for _, orders := range arrivalAddOrderMap {
116- sort.Sort(sort.Reverse(common.OrderSlice(orders)))
117- }
118- return arrivalAddOrderMap
119-}
120-
121-func arrangeArrivalDelOrders(orders []*common.Order) map[string]*common.Order {
122- arrivalDelOrderMap := make(map[string]*common.Order)
123- for _, order := range orders {
124- arrivalDelOrderMap[order.Key()] = order
125- }
126- return arrivalDelOrderMap
127-}
--- a/application/mov/match/order_table_test.go
+++ b/application/mov/match/order_table_test.go
@@ -90,7 +90,7 @@ func TestOrderTable(t *testing.T) {
9090 mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
9191 }),
9292 initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
93- popOrders: []*common.TradePair{btc2eth},
93+ popOrders: []*common.TradePair{btc2eth},
9494 wantPeekedOrders: map[common.TradePair]*common.Order{
9595 *btc2eth: mock.Btc2EthOrders[0],
9696 },
@@ -129,7 +129,7 @@ func TestOrderTable(t *testing.T) {
129129 }),
130130 initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[2]},
131131 initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3]},
132- popOrders: []*common.TradePair{btc2eth},
132+ popOrders: []*common.TradePair{btc2eth},
133133 wantPeekedOrders: map[common.TradePair]*common.Order{
134134 *btc2eth: mock.Btc2EthOrders[2],
135135 },
@@ -154,6 +154,19 @@ func TestOrderTable(t *testing.T) {
154154 *btc2eth: nil,
155155 },
156156 },
157+ {
158+ desc: "has arrival delete orders, no add order, no pop order, need recursive to peek one order",
159+ initMovStore: mock.NewMovStore(
160+ []*common.TradePair{btc2eth},
161+ []*common.Order{
162+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
163+ }),
164+ initArrivalAddOrders: []*common.Order{},
165+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3], mock.Btc2EthOrders[0], mock.Btc2EthOrders[2]},
166+ wantPeekedOrders: map[common.TradePair]*common.Order{
167+ *btc2eth: mock.Btc2EthOrders[1],
168+ },
169+ },
157170 }
158171
159172 for i, c := range cases {
Show on old repository browser