OSDN Git Service

fix bug
[bytom/vapor.git] / application / mov / mov_core.go
1 package mov
2
3 import (
4         "github.com/vapor/application/mov/common"
5         "github.com/vapor/application/mov/contract"
6         "github.com/vapor/application/mov/database"
7         "github.com/vapor/application/mov/match"
8         "github.com/vapor/consensus/segwit"
9         dbm "github.com/vapor/database/leveldb"
10         "github.com/vapor/errors"
11         "github.com/vapor/math/checked"
12         "github.com/vapor/protocol/bc"
13         "github.com/vapor/protocol/bc/types"
14 )
15
16 const maxFeeRate = 0.05
17
18 var (
19         errInvalidTradePairs             = errors.New("The trade pairs in the tx input is invalid")
20         errStatusFailMustFalse           = errors.New("status fail of transaction does not allow to be true")
21         errInputProgramMustP2WMCScript   = errors.New("input program of matched tx must p2wmc script")
22         errExistCancelOrderInMatchedTx   = errors.New("can't exist cancel order in the matched transaction")
23         errExistTradeInCancelOrderTx     = errors.New("can't exist trade in the cancel order transaction")
24         errAmountOfFeeGreaterThanMaximum = errors.New("amount of fee greater than max fee amount")
25         errAssetIDMustUniqueInMatchedTx  = errors.New("asset id must unique in matched transaction")
26         errRatioOfTradeLessThanZero      = errors.New("ratio arguments must greater than zero")
27         errNumeratorOfRatioIsOverflow    = errors.New("ratio numerator of contract args product input amount is overflow")
28         errLengthOfInputIsIncorrect      = errors.New("length of matched tx input is not equals to actual matched tx input")
29         errSpendOutputIDIsIncorrect      = errors.New("spend output id of matched tx is not equals to actual matched tx")
30 )
31
32 // MovCore represent the core logic of the match module, which include generate match transactions before packing the block,
33 // verify the match transaction in block is correct, and update the order table according to the transaction.
34 type MovCore struct {
35         movStore         database.MovStore
36         startBlockHeight uint64
37 }
38
39 // NewMovCore return a instance of MovCore by path of mov db
40 func NewMovCore(dbBackend, dbDir string, startBlockHeight uint64) *MovCore {
41         movDB := dbm.NewDB("mov", dbBackend, dbDir)
42         return &MovCore{movStore: database.NewLevelDBMovStore(movDB), startBlockHeight: startBlockHeight}
43 }
44
45 // Name return the name of current module
46 func (m *MovCore) Name() string {
47         return "MOV"
48 }
49
50 // ChainStatus return the current block height and block hash in dex core
51 func (m *MovCore) ChainStatus() (uint64, *bc.Hash, error) {
52         state, err := m.movStore.GetMovDatabaseState()
53         if err != nil {
54                 return 0, nil, err
55         }
56
57         return state.Height, state.Hash, nil
58 }
59
60 // ValidateBlock no need to verify the block header, becaure the first module has been verified.
61 // just need to verify the transactions in the block.
62 func (m *MovCore) ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error {
63         return m.ValidateTxs(block.Transactions, verifyResults)
64 }
65
66 // ValidateTxs validate the trade transaction.
67 func (m *MovCore) ValidateTxs(txs []*types.Tx, verifyResults []*bc.TxVerifyResult) error {
68         for i, tx := range txs {
69                 if err := m.ValidateTx(tx, verifyResults[i]); err != nil {
70                         return err
71                 }
72         }
73         return nil
74 }
75
76 func (m *MovCore) ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error {
77         if common.IsMatchedTx(tx) {
78                 if err := validateMatchedTx(tx, verifyResult); err != nil {
79                         return err
80                 }
81         }
82
83         if common.IsCancelOrderTx(tx) {
84                 if err := validateCancelOrderTx(tx, verifyResult); err != nil {
85                         return err
86                 }
87         }
88
89         for _, output := range tx.Outputs {
90                 if !segwit.IsP2WMCScript(output.ControlProgram()) {
91                         continue
92                 }
93                 if verifyResult.StatusFail {
94                         return errStatusFailMustFalse
95                 }
96
97                 if err := validateMagneticContractArgs(output.AssetAmount().Amount, output.ControlProgram()); err != nil {
98                         return err
99                 }
100         }
101         return nil
102 }
103
104 func validateMatchedTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error {
105         if verifyResult.StatusFail {
106                 return errStatusFailMustFalse
107         }
108
109         fromAssetIDMap := make(map[string]bool)
110         toAssetIDMap := make(map[string]bool)
111         for i, input := range tx.Inputs {
112                 if !segwit.IsP2WMCScript(input.ControlProgram()) {
113                         return errInputProgramMustP2WMCScript
114                 }
115
116                 if contract.IsCancelClauseSelector(input) {
117                         return errExistCancelOrderInMatchedTx
118                 }
119
120                 order, err := common.NewOrderFromInput(tx, i)
121                 if err != nil {
122                         return err
123                 }
124
125                 fromAssetIDMap[order.FromAssetID.String()] = true
126                 toAssetIDMap[order.ToAssetID.String()] = true
127         }
128
129         if len(fromAssetIDMap) != len(tx.Inputs) || len(toAssetIDMap) != len(tx.Inputs) {
130                 return errAssetIDMustUniqueInMatchedTx
131         }
132
133         return validateMatchedTxFeeAmount(tx)
134 }
135
136 func validateCancelOrderTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error {
137         if verifyResult.StatusFail {
138                 return errStatusFailMustFalse
139         }
140
141         for _, input := range tx.Inputs {
142                 if !segwit.IsP2WMCScript(input.ControlProgram()) {
143                         return errInputProgramMustP2WMCScript
144                 }
145
146                 if contract.IsTradeClauseSelector(input) {
147                         return errExistTradeInCancelOrderTx
148                 }
149         }
150         return nil
151 }
152
153 func validateMatchedTxFeeAmount(tx *types.Tx) error {
154         txFee, err := match.CalcMatchedTxFee(&tx.TxData, maxFeeRate)
155         if err != nil {
156                 return err
157         }
158
159         for _, amount := range txFee {
160                 if amount.FeeAmount > amount.MaxFeeAmount {
161                         return errAmountOfFeeGreaterThanMaximum
162                 }
163         }
164         return nil
165 }
166
167 func validateMagneticContractArgs(inputAmount uint64, program []byte) error {
168         contractArgs, err := segwit.DecodeP2WMCProgram(program)
169         if err != nil {
170                 return err
171         }
172
173         if contractArgs.RatioNumerator <= 0 || contractArgs.RatioDenominator <= 0 {
174                 return errRatioOfTradeLessThanZero
175         }
176
177         if _, ok := checked.MulInt64(int64(inputAmount), contractArgs.RatioNumerator); !ok {
178                 return errNumeratorOfRatioIsOverflow
179         }
180         return nil
181 }
182
183 // ApplyBlock parse pending order and cancel from the the transactions of block
184 // and add pending order to the dex db, remove cancel order from dex db.
185 func (m *MovCore) ApplyBlock(block *types.Block) error {
186         if block.Height < m.startBlockHeight {
187                 return nil
188         }
189
190         if block.Height == m.startBlockHeight {
191                 blockHash := block.Hash()
192                 if err := m.movStore.InitDBState(block.Height, &blockHash); err != nil {
193                         return err
194                 }
195         }
196
197         if err := m.validateMatchedTxSequence(block.Transactions); err != nil {
198                 return err
199         }
200
201         addOrders, deleteOrders, err := applyTransactions(block.Transactions)
202         if err != nil {
203                 return err
204         }
205
206         return m.movStore.ProcessOrders(addOrders, deleteOrders, &block.BlockHeader)
207 }
208
209 func (m *MovCore) validateMatchedTxSequence(txs []*types.Tx) error {
210         matchEngine := match.NewEngine(m.movStore, maxFeeRate, nil)
211         for _, matchedTx := range txs {
212                 if !common.IsMatchedTx(matchedTx) {
213                         continue
214                 }
215
216                 tradePairs, err := getSortedTradePairsFromMatchedTx(matchedTx)
217                 if err != nil {
218                         return err
219                 }
220
221                 actualMatchedTx, err := matchEngine.NextMatchedTx(tradePairs...)
222                 if err != nil {
223                         return err
224                 }
225
226                 if len(matchedTx.Inputs) != len(actualMatchedTx.Inputs) {
227                         return errLengthOfInputIsIncorrect
228                 }
229
230                 spendOutputIDs := make(map[string]bool)
231                 for _, input := range matchedTx.Inputs {
232                         spendOutputID, err := input.SpentOutputID()
233                         if err != nil {
234                                 return err
235                         }
236
237                         spendOutputIDs[spendOutputID.String()] = true
238                 }
239
240                 for _, input := range actualMatchedTx.Inputs {
241                         spendOutputID, err := input.SpentOutputID()
242                         if err != nil {
243                                 return err
244                         }
245
246                         if _, ok := spendOutputIDs[spendOutputID.String()]; !ok {
247                                 return errSpendOutputIDIsIncorrect
248                         }
249                 }
250         }
251         return nil
252 }
253
254 func getSortedTradePairsFromMatchedTx(tx *types.Tx) ([]*common.TradePair, error) {
255         assetMap := make(map[bc.AssetID]bc.AssetID)
256         var firstTradePair *common.TradePair
257         for _, tx := range tx.Inputs {
258                 contractArgs, err := segwit.DecodeP2WMCProgram(tx.ControlProgram())
259                 if err != nil {
260                         return nil, err
261                 }
262
263                 assetMap[tx.AssetID()] = contractArgs.RequestedAsset
264                 if firstTradePair == nil {
265                         firstTradePair = &common.TradePair{FromAssetID: tx.AssetAmount().AssetId, ToAssetID: &contractArgs.RequestedAsset}
266                 }
267         }
268
269         tradePairs := []*common.TradePair{firstTradePair}
270         for tradePair := firstTradePair; *tradePair.ToAssetID != *firstTradePair.FromAssetID; {
271                 nextTradePairToAssetID, ok := assetMap[*tradePair.ToAssetID]
272                 if !ok {
273                         return nil, errInvalidTradePairs
274                 }
275
276                 tradePair = &common.TradePair{FromAssetID: tradePair.ToAssetID, ToAssetID: &nextTradePairToAssetID}
277                 tradePairs = append(tradePairs, tradePair)
278         }
279
280         if len(tradePairs) != len(tx.Inputs) {
281                 return nil, errInvalidTradePairs
282         }
283         return tradePairs, nil
284 }
285
286 // DetachBlock parse pending order and cancel from the the transactions of block
287 // and add cancel order to the dex db, remove pending order from dex db.
288 func (m *MovCore) DetachBlock(block *types.Block) error {
289         if block.Height <= m.startBlockHeight {
290                 return nil
291         }
292
293         deleteOrders, addOrders, err := applyTransactions(block.Transactions)
294         if err != nil {
295                 return err
296         }
297
298         return m.movStore.ProcessOrders(addOrders, deleteOrders, &block.BlockHeader)
299 }
300
301 // BeforeProposalBlock return all transactions than can be matched, and the number of transactions cannot exceed the given capacity.
302 func (m *MovCore) BeforeProposalBlock(nodeProgram []byte, gasLeft int64, calcGasUsed func(*types.Tx) (int64, error)) ([]*types.Tx, int64, error) {
303         matchEngine := match.NewEngine(m.movStore, maxFeeRate, nodeProgram)
304         tradePairMap := make(map[string]bool)
305         tradePairIterator := database.NewTradePairIterator(m.movStore)
306
307         var packagedTxs []*types.Tx
308         for gasLeft > 0 && tradePairIterator.HasNext() {
309                 tradePair := tradePairIterator.Next()
310                 if tradePairMap[tradePair.Key()] {
311                         continue
312                 }
313                 tradePairMap[tradePair.Key()] = true
314                 tradePairMap[tradePair.Reverse().Key()] = true
315
316                 for gasLeft > 0 && matchEngine.HasMatchedTx(tradePair, tradePair.Reverse()) {
317                         matchedTx, err := matchEngine.NextMatchedTx(tradePair, tradePair.Reverse())
318                         if err != nil {
319                                 return nil, 0, err
320                         }
321
322                         gasUsed, err := calcGasUsed(matchedTx)
323                         if err != nil {
324                                 return nil, 0, err
325                         }
326
327                         packagedTxs = append(packagedTxs, matchedTx)
328                         gasLeft -= gasUsed
329                 }
330         }
331         return packagedTxs, gasLeft, nil
332 }
333
334 // IsDust block the transaction that are not generated by the match engine 
335 func (m *MovCore) IsDust(tx *types.Tx) bool {
336         for _, input := range tx.Inputs {
337                 if segwit.IsP2WMCScript(input.ControlProgram()) && !contract.IsCancelClauseSelector(input) {
338                         return true
339                 }
340         }
341         return false
342 }
343
344 func applyTransactions(txs []*types.Tx) ([]*common.Order, []*common.Order, error) {
345         deleteOrderMap := make(map[string]*common.Order)
346         addOrderMap := make(map[string]*common.Order)
347         for _, tx := range txs {
348                 addOrders, err := getAddOrdersFromTx(tx)
349                 if err != nil {
350                         return nil, nil, err
351                 }
352
353                 for _, order := range addOrders {
354                         addOrderMap[order.Key()] = order
355                 }
356
357                 deleteOrders, err := getDeleteOrdersFromTx(tx)
358                 if err != nil {
359                         return nil, nil, err
360                 }
361
362                 for _, order := range deleteOrders {
363                         deleteOrderMap[order.Key()] = order
364                 }
365         }
366
367         addOrders, deleteOrders := mergeOrders(addOrderMap, deleteOrderMap)
368         return addOrders, deleteOrders, nil
369 }
370
371 func mergeOrders(addOrderMap, deleteOrderMap map[string]*common.Order) ([]*common.Order, []*common.Order) {
372         var deleteOrders, addOrders []*common.Order
373         for orderID, order := range addOrderMap {
374                 if _, ok := deleteOrderMap[orderID]; ok {
375                         delete(deleteOrderMap, orderID)
376                         continue
377                 }
378                 addOrders = append(addOrders, order)
379         }
380
381         for _, order := range deleteOrderMap {
382                 deleteOrders = append(deleteOrders, order)
383         }
384         return addOrders, deleteOrders
385 }
386
387 func getAddOrdersFromTx(tx *types.Tx) ([]*common.Order, error) {
388         var orders []*common.Order
389         for i, output := range tx.Outputs {
390                 if output.OutputType() != types.IntraChainOutputType || !segwit.IsP2WMCScript(output.ControlProgram()) {
391                         continue
392                 }
393
394                 order, err := common.NewOrderFromOutput(tx, i)
395                 if err != nil {
396                         return nil, err
397                 }
398
399                 orders = append(orders, order)
400         }
401         return orders, nil
402 }
403
404 func getDeleteOrdersFromTx(tx *types.Tx) ([]*common.Order, error) {
405         var orders []*common.Order
406         for i, input := range tx.Inputs {
407                 if input.InputType() != types.SpendInputType || !segwit.IsP2WMCScript(input.ControlProgram()) {
408                         continue
409                 }
410
411                 order, err := common.NewOrderFromInput(tx, i)
412                 if err != nil {
413                         return nil, err
414                 }
415
416                 orders = append(orders, order)
417         }
418         return orders, nil
419 }