• R/O
  • HTTP
  • SSH
  • HTTPS

vapor: コミット

Golang implemented sidechain for Bytom


コミットメタ情報

リビジョン4826d3905a9091bbca84b970b5aaa311aa5807df (tree)
日時2019-10-23 15:12:02
作者shenao78 <shenao.78@163....>
コミッターshenao78

ログメッセージ

opt code

変更サマリ

差分

--- a/application/mov/match/match.go
+++ b/application/mov/match/match.go
@@ -1,12 +1,12 @@
11 package match
22
33 import (
4+ "encoding/hex"
45 "math"
56 "math/big"
67
78 "github.com/vapor/application/mov/common"
89 "github.com/vapor/application/mov/database"
9- "github.com/vapor/application/mov/util"
1010 "github.com/vapor/consensus/segwit"
1111 "github.com/vapor/errors"
1212 vprMath "github.com/vapor/math"
@@ -16,7 +16,7 @@ import (
1616 "github.com/vapor/protocol/vm/vmutil"
1717 )
1818
19-var maxFeeRate = 0.05
19+const maxFeeRate = 0.05
2020
2121 type Engine struct {
2222 orderTable *OrderTable
@@ -27,22 +27,46 @@ func NewEngine(movStore database.MovStore, nodeProgram []byte) *Engine {
2727 return &Engine{orderTable: NewOrderTable(movStore), nodeProgram: nodeProgram}
2828 }
2929
30+func (e *Engine) HasMatchedTx(tradePairs ...*common.TradePair) bool {
31+ if err := validateTradePairs(tradePairs); err != nil {
32+ return false
33+ }
34+
35+ orders := e.peekOrders(tradePairs)
36+ if len(orders) == 0 {
37+ return false
38+ }
39+
40+ var contractArgsList []*vmutil.MagneticContractArgs
41+ for _, order := range orders {
42+ contractArgs, err := segwit.DecodeP2WMCProgram(order.Utxo.ControlProgram)
43+ if err != nil {
44+ return false
45+ }
46+
47+ contractArgsList = append(contractArgsList, contractArgs)
48+ }
49+
50+ for i, contractArgs := range contractArgsList {
51+ oppositeContractArgs := contractArgsList[getOppositeIndex(len(contractArgsList), i)]
52+ if canNotBeMatched(contractArgs, oppositeContractArgs) {
53+ return false
54+ }
55+ }
56+ return true
57+}
58+
3059 // NextMatchedTx return the next matchable transaction by the specified trade pairs
31-// the size of trade pairs at least, and the sequence of trade pairs can form a loop
60+// the size of trade pairs at least 2, and the sequence of trade pairs can form a loop
3261 // for example, [assetA -> assetB, assetB -> assetC, assetC -> assetA]
33-func (e *Engine) NextMatchedTx(tradePairs ...*common.TradePair) (*types.Tx, error) {
62+func (e *Engine) NextMatchedTx(tradePairs ...*common.TradePair) (*types.Tx, error) {
3463 if err := validateTradePairs(tradePairs); err != nil {
3564 return nil, err
3665 }
3766
38- var orders []*common.Order
39- for _, tradePair := range tradePairs {
40- order := e.orderTable.PeekOrder(tradePair)
41- if order == nil {
42- return nil, nil
43- }
44-
45- orders = append(orders, order)
67+ orders := e.peekOrders(tradePairs)
68+ if len(orders) == 0 {
69+ return nil, errors.New("no order for the specified trade pair in the order table")
4670 }
4771
4872 tx, err := e.buildMatchTx(orders)
@@ -50,25 +74,35 @@ func (e *Engine) NextMatchedTx(tradePairs ...*common.TradePair) (*types.Tx, err
5074 return nil, err
5175 }
5276
53- if tx == nil {
54- return nil, nil
55- }
56-
5777 for _, tradePair := range tradePairs {
5878 e.orderTable.PopOrder(tradePair)
5979 }
80+
6081 if err := addPartialTradeOrder(tx, e.orderTable); err != nil {
6182 return nil, err
6283 }
6384 return tx, nil
6485 }
6586
87+func (e *Engine) peekOrders(tradePairs []*common.TradePair) []*common.Order {
88+ var orders []*common.Order
89+ for _, tradePair := range tradePairs {
90+ order := e.orderTable.PeekOrder(tradePair)
91+ if order == nil {
92+ return nil
93+ }
94+
95+ orders = append(orders, order)
96+ }
97+ return orders
98+}
99+
66100 func validateTradePairs(tradePairs []*common.TradePair) error {
67101 if len(tradePairs) < 2 {
68102 return errors.New("size of trade pairs at least 2")
69103 }
70104
71- for i, tradePair:= range tradePairs {
105+ for i, tradePair := range tradePairs {
72106 oppositeTradePair := tradePairs[getOppositeIndex(len(tradePairs), i)]
73107 if *tradePair.FromAssetID != *oppositeTradePair.ToAssetID || *tradePair.ToAssetID != *oppositeTradePair.FromAssetID {
74108 return errors.New("specified trade pairs is invalid")
@@ -77,34 +111,28 @@ func validateTradePairs(tradePairs []*common.TradePair) error {
77111 return nil
78112 }
79113
114+func canNotBeMatched(contractArgs, oppositeContractArgs *vmutil.MagneticContractArgs) bool {
115+ if contractArgs.RatioNumerator == 0 || oppositeContractArgs.RatioDenominator == 0 {
116+ return false
117+ }
118+
119+ buyRate := big.NewFloat(0).Quo(big.NewFloat(0).SetInt64(contractArgs.RatioDenominator), big.NewFloat(0).SetInt64(contractArgs.RatioNumerator))
120+ sellRate := big.NewFloat(0).Quo(big.NewFloat(0).SetInt64(oppositeContractArgs.RatioNumerator), big.NewFloat(0).SetInt64(oppositeContractArgs.RatioDenominator))
121+ return buyRate.Cmp(sellRate) < 0
122+}
123+
80124 func (e *Engine) buildMatchTx(orders []*common.Order) (*types.Tx, error) {
81125 txData := &types.TxData{Version: 1}
82- var partialTradeStatus []bool
83- var receiveAmounts []uint64
84-
85126 for i, order := range orders {
86- contractArgs, err := segwit.DecodeP2WMCProgram(order.Utxo.ControlProgram)
87- if err != nil {
88- return nil, err
89- }
127+ input := types.NewSpendInput(nil, *order.Utxo.SourceID, *order.FromAssetID, order.Utxo.Amount, order.Utxo.SourcePos, order.Utxo.ControlProgram)
128+ txData.Inputs = append(txData.Inputs, input)
90129
91130 oppositeOrder := orders[getOppositeIndex(len(orders), i)]
92- oppositeContractArgs, err := segwit.DecodeP2WMCProgram(oppositeOrder.Utxo.ControlProgram)
93- if err != nil {
131+ if err := addMatchTxOutput(txData, input, order, oppositeOrder.Utxo.Amount); err != nil {
94132 return nil, err
95133 }
96-
97- if canNotBeMatched(contractArgs, oppositeContractArgs) {
98- return nil, nil
99- }
100-
101- txData.Inputs = append(txData.Inputs, types.NewSpendInput(nil, *order.Utxo.SourceID, *order.FromAssetID, order.Utxo.Amount, order.Utxo.SourcePos, order.Utxo.ControlProgram))
102- isPartialTrade, receiveAmount := addMatchTxOutput(txData, order, contractArgs, oppositeOrder.Utxo.Amount)
103- partialTradeStatus = append(partialTradeStatus, isPartialTrade)
104- receiveAmounts = append(receiveAmounts, receiveAmount)
105134 }
106135
107- setMatchTxArguments(txData, partialTradeStatus, receiveAmounts)
108136 if err := e.addMatchTxFeeOutput(txData); err != nil {
109137 return nil, err
110138 }
@@ -119,28 +147,23 @@ func (e *Engine) buildMatchTx(orders []*common.Order) (*types.Tx, error) {
119147 return tx, nil
120148 }
121149
122-func canNotBeMatched(contractArgs, oppositeContractArgs *vmutil.MagneticContractArgs) bool {
123- if contractArgs.RatioNumerator == 0 || oppositeContractArgs.RatioDenominator == 0 {
124- return false
150+func addMatchTxOutput(txData *types.TxData, txInput *types.TxInput, order *common.Order, oppositeAmount uint64) error {
151+ contractArgs, err := segwit.DecodeP2WMCProgram(order.Utxo.ControlProgram)
152+ if err != nil {
153+ return err
125154 }
126155
127- buyRate := big.NewFloat(0).Quo(big.NewFloat(0).SetInt64(contractArgs.RatioDenominator), big.NewFloat(0).SetInt64(contractArgs.RatioNumerator))
128- sellRate := big.NewFloat(0).Quo(big.NewFloat(0).SetInt64(oppositeContractArgs.RatioNumerator), big.NewFloat(0).SetInt64(oppositeContractArgs.RatioDenominator))
129- return buyRate.Cmp(sellRate) < 0
130-}
131-
132-// addMatchTxOutput return whether partial matched
133-func addMatchTxOutput(txData *types.TxData, order *common.Order, contractArgs *vmutil.MagneticContractArgs, oppositeAmount uint64) (bool, uint64) {
134156 requestAmount := calcRequestAmount(order.Utxo.Amount, contractArgs)
135157 receiveAmount := vprMath.MinUint64(requestAmount, oppositeAmount)
136158 shouldPayAmount := CalcShouldPayAmount(receiveAmount, contractArgs)
159+ isPartialTrade := order.Utxo.Amount > shouldPayAmount
137160
161+ setMatchTxArguments(txInput, isPartialTrade, len(txData.Outputs), receiveAmount)
138162 txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, receiveAmount, contractArgs.SellerProgram))
139- if order.Utxo.Amount > shouldPayAmount {
163+ if isPartialTrade {
140164 txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.FromAssetID, order.Utxo.Amount-shouldPayAmount, order.Utxo.ControlProgram))
141- return true, receiveAmount
142165 }
143- return false, receiveAmount
166+ return nil
144167 }
145168
146169 func (e *Engine) addMatchTxFeeOutput(txData *types.TxData) error {
@@ -180,19 +203,14 @@ func (e *Engine) addMatchTxFeeOutput(txData *types.TxData) error {
180203 return nil
181204 }
182205
183-func setMatchTxArguments(txData *types.TxData, partialTradeStatus []bool, receiveAmounts []uint64) {
184- var position int64
185- for i, isPartial := range partialTradeStatus {
186- var arguments [][]byte
187- if isPartial {
188- arguments = [][]byte{vm.Int64Bytes(int64(receiveAmounts[i])), vm.Int64Bytes(position), vm.Int64Bytes(0)}
189- position += 2
190- } else {
191- arguments = [][]byte{vm.Int64Bytes(position), vm.Int64Bytes(1)}
192- position++
193- }
194- txData.Inputs[i].SetArguments(arguments)
206+func setMatchTxArguments(txInput *types.TxInput, isPartialTrade bool, position int, receiveAmounts uint64) {
207+ var arguments [][]byte
208+ if isPartialTrade {
209+ arguments = [][]byte{vm.Int64Bytes(int64(receiveAmounts)), vm.Int64Bytes(int64(position)), vm.Int64Bytes(0)}
210+ } else {
211+ arguments = [][]byte{vm.Int64Bytes(int64(position)), vm.Int64Bytes(1)}
195212 }
213+ txInput.SetArguments(arguments)
196214 }
197215
198216 func addPartialTradeOrder(tx *types.Tx, orderTable *OrderTable) error {
@@ -215,24 +233,12 @@ func addPartialTradeOrder(tx *types.Tx, orderTable *OrderTable) error {
215233
216234 func getOppositeIndex(size int, selfIdx int) int {
217235 oppositeIdx := selfIdx + 1
218- if selfIdx >= size - 1 {
236+ if selfIdx >= size-1 {
219237 oppositeIdx = 0
220238 }
221239 return oppositeIdx
222240 }
223241
224-func calcRequestAmount(fromAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 {
225- return uint64(int64(fromAmount) * contractArg.RatioNumerator / contractArg.RatioDenominator)
226-}
227-
228-func CalcShouldPayAmount(receiveAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 {
229- return uint64(math.Ceil(float64(receiveAmount) * float64(contractArg.RatioDenominator) / float64(contractArg.RatioNumerator)))
230-}
231-
232-func CalcMaxFeeAmount(shouldPayAmount uint64) uint64 {
233- return uint64(math.Ceil(float64(shouldPayAmount) * maxFeeRate))
234-}
235-
236242 type feeAmount struct {
237243 maxFeeAmount uint64
238244 payableFeeAmount uint64
@@ -244,28 +250,30 @@ func CalcFeeFromMatchedTx(txData *types.TxData) (map[bc.AssetID]*feeAmount, erro
244250 assetAmountMap[input.AssetID()] = &feeAmount{}
245251 }
246252
247- for _, input := range txData.Inputs {
248- assetAmountMap[input.AssetID()].payableFeeAmount += input.AssetAmount().Amount
249- outputPos, err := util.GetTradeReceivePosition(input)
250- if err != nil {
251- return nil, err
253+ receiveOutputMap := make(map[string]*types.TxOutput)
254+ for _, output := range txData.Outputs {
255+ // minus the amount of the re-order
256+ if segwit.IsP2WMCScript(output.ControlProgram()) {
257+ assetAmountMap[*output.AssetAmount().AssetId].payableFeeAmount -= output.AssetAmount().Amount
258+ } else {
259+ receiveOutputMap[hex.EncodeToString(output.ControlProgram())] = output
252260 }
261+ }
253262
254- receiveOutput := txData.Outputs[outputPos]
255- assetAmountMap[*receiveOutput.AssetAmount().AssetId].payableFeeAmount -= receiveOutput.AssetAmount().Amount
263+ for _, input := range txData.Inputs {
256264 contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram())
257265 if err != nil {
258266 return nil, err
259267 }
260268
261- assetAmountMap[input.AssetID()].maxFeeAmount = CalcMaxFeeAmount(CalcShouldPayAmount(receiveOutput.AssetAmount().Amount, contractArgs))
262- }
263-
264- for _, output := range txData.Outputs {
265- // minus the amount of the re-order
266- if segwit.IsP2WMCScript(output.ControlProgram()) {
267- assetAmountMap[*output.AssetAmount().AssetId].payableFeeAmount -= output.AssetAmount().Amount
269+ assetAmountMap[input.AssetID()].payableFeeAmount += input.AssetAmount().Amount
270+ receiveOutput, ok := receiveOutputMap[hex.EncodeToString(contractArgs.SellerProgram)]
271+ if !ok {
272+ return nil, errors.New("the input of matched tx has no receive output")
268273 }
274+
275+ assetAmountMap[*receiveOutput.AssetAmount().AssetId].payableFeeAmount -= receiveOutput.AssetAmount().Amount
276+ assetAmountMap[input.AssetID()].maxFeeAmount = CalcMaxFeeAmount(CalcShouldPayAmount(receiveOutput.AssetAmount().Amount, contractArgs))
269277 }
270278
271279 for assetID, amount := range assetAmountMap {
@@ -275,3 +283,15 @@ func CalcFeeFromMatchedTx(txData *types.TxData) (map[bc.AssetID]*feeAmount, erro
275283 }
276284 return assetAmountMap, nil
277285 }
286+
287+func calcRequestAmount(fromAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 {
288+ return uint64(int64(fromAmount) * contractArg.RatioNumerator / contractArg.RatioDenominator)
289+}
290+
291+func CalcShouldPayAmount(receiveAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 {
292+ return uint64(math.Ceil(float64(receiveAmount) * float64(contractArg.RatioDenominator) / float64(contractArg.RatioNumerator)))
293+}
294+
295+func CalcMaxFeeAmount(shouldPayAmount uint64) uint64 {
296+ return uint64(math.Ceil(float64(shouldPayAmount) * maxFeeRate))
297+}
--- a/application/mov/match/match_test.go
+++ b/application/mov/match/match_test.go
@@ -180,14 +180,12 @@ func TestGenerateMatchedTxs(t *testing.T) {
180180 movStore := &database.MockMovStore{OrderMap: c.storeOrderMap}
181181 matchEngine := NewEngine(movStore, []byte{0x51})
182182 var gotMatchedTxs []*types.Tx
183- for {
183+ for matchEngine.HasMatchedTx(c.tradePair, c.tradePair.Reverse()) {
184184 matchedTx, err := matchEngine.NextMatchedTx(c.tradePair, c.tradePair.Reverse())
185185 if err != nil {
186186 t.Fatal(err)
187187 }
188- if matchedTx == nil {
189- break
190- }
188+
191189 gotMatchedTxs = append(gotMatchedTxs, matchedTx)
192190 }
193191
--- a/application/mov/util/util.go
+++ b/application/mov/util/util.go
@@ -3,13 +3,22 @@ package util
33 import (
44 "encoding/hex"
55
6- "github.com/vapor/errors"
76 "github.com/vapor/protocol/bc/types"
87 "github.com/vapor/protocol/vm"
98 )
109
10+const (
11+ sizeOfCancelClauseArgs = 3
12+ sizeOfPartialTradeClauseArgs = 3
13+ sizeOfFullTradeClauseArgs = 2
14+
15+ partialTradeClauseSelector int64 = iota
16+ fullTradeClauseSelector
17+ cancelClauseSelector
18+)
19+
1120 func IsCancelClauseSelector(input *types.TxInput) bool {
12- return len(input.Arguments()) == 3 && hex.EncodeToString(input.Arguments()[2]) == hex.EncodeToString(vm.Int64Bytes(2))
21+ return len(input.Arguments()) == sizeOfCancelClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments()) - 1]) == hex.EncodeToString(vm.Int64Bytes(cancelClauseSelector))
1322 }
1423
1524 func IsTradeClauseSelector(input *types.TxInput) bool {
@@ -17,20 +26,9 @@ func IsTradeClauseSelector(input *types.TxInput) bool {
1726 }
1827
1928 func IsPartialTradeClauseSelector(input *types.TxInput) bool {
20- return len(input.Arguments()) == 3 && hex.EncodeToString(input.Arguments()[2]) == hex.EncodeToString(vm.Int64Bytes(0))
29+ return len(input.Arguments()) == sizeOfPartialTradeClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments()) - 1]) == hex.EncodeToString(vm.Int64Bytes(partialTradeClauseSelector))
2130 }
2231
2332 func IsFullTradeClauseSelector(input *types.TxInput) bool {
24- return len(input.Arguments()) == 2 && hex.EncodeToString(input.Arguments()[1]) == hex.EncodeToString(vm.Int64Bytes(1))
25-}
26-
27-func GetTradeReceivePosition(input *types.TxInput) (int64, error) {
28- if IsPartialTradeClauseSelector(input) {
29- return vm.AsInt64(input.Arguments()[1])
30- }
31-
32- if IsFullTradeClauseSelector(input) {
33- return vm.AsInt64(input.Arguments()[0])
34- }
35- return 0, errors.New("non trade transaction input")
33+ return len(input.Arguments()) == sizeOfFullTradeClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments()) - 1]) == hex.EncodeToString(vm.Int64Bytes(fullTradeClauseSelector))
3634 }
旧リポジトリブラウザで表示