OSDN Git Service

validate reward address (#505)
[bytom/vapor.git] / application / mov / mov_core.go
index 6a8855a..955a493 100644 (file)
@@ -18,16 +18,18 @@ import (
 const maxFeeRate = 0.05
 
 var (
-       errInvalidTradePairs             = errors.New("The trade pairs in the tx input is invalid")
-       errStatusFailMustFalse           = errors.New("status fail of transaction does not allow to be true")
-       errInputProgramMustP2WMCScript   = errors.New("input program of trade tx must p2wmc script")
-       errExistCancelOrderInMatchedTx   = errors.New("can't exist cancel order in the matched transaction")
-       errExistTradeInCancelOrderTx     = errors.New("can't exist trade in the cancel order transaction")
-       errAssetIDMustUniqueInMatchedTx  = errors.New("asset id must unique in matched transaction")
-       errRatioOfTradeLessThanZero      = errors.New("ratio arguments must greater than zero")
-       errSpendOutputIDIsIncorrect      = errors.New("spend output id of matched tx is not equals to actual matched tx")
-       errRequestAmountMath             = errors.New("request amount of order less than one or big than max of int64")
-       errNotMatchedOrder               = errors.New("order in matched tx is not matched")
+       errInvalidTradePairs            = errors.New("The trade pairs in the tx input is invalid")
+       errStatusFailMustFalse          = errors.New("status fail of transaction does not allow to be true")
+       errInputProgramMustP2WMCScript  = errors.New("input program of trade tx must p2wmc script")
+       errExistCancelOrderInMatchedTx  = errors.New("can't exist cancel order in the matched transaction")
+       errExistTradeInCancelOrderTx    = errors.New("can't exist trade in the cancel order transaction")
+       errAssetIDMustUniqueInMatchedTx = errors.New("asset id must unique in matched transaction")
+       errRatioOfTradeLessThanZero     = errors.New("ratio arguments must greater than zero")
+       errSpendOutputIDIsIncorrect     = errors.New("spend output id of matched tx is not equals to actual matched tx")
+       errRequestAmountMath            = errors.New("request amount of order less than one or big than max of int64")
+       errNotMatchedOrder              = errors.New("order in matched tx is not matched")
+       errNotConfiguredRewardProgram   = errors.New("reward program is not configured properly")
+       errRewardProgramIsWrong         = errors.New("the reward program is not correct")
 )
 
 // MovCore represent the core logic of the match module, which include generate match transactions before packing the block,
@@ -83,9 +85,10 @@ func (m *MovCore) BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLe
                return nil, err
        }
 
-       rewardProgram, err := hex.DecodeString(consensus.ActiveNetParams.MovRewardProgram)
+       program, _ := getRewardProgram(blockHeight)
+       rewardProgram, err := hex.DecodeString(program)
        if err != nil {
-               return nil, err
+               return nil, errNotConfiguredRewardProgram
        }
 
        matchEngine := match.NewEngine(orderBook, match.NewDefaultFeeStrategy(maxFeeRate), rewardProgram)
@@ -142,13 +145,8 @@ func (m *MovCore) StartHeight() uint64 {
 // ValidateBlock no need to verify the block header, because the first module has been verified.
 // just need to verify the transactions in the block.
 func (m *MovCore) ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error {
-       return m.ValidateTxs(block.Transactions, verifyResults)
-}
-
-// ValidateTxs validate the trade transaction.
-func (m *MovCore) ValidateTxs(txs []*types.Tx, verifyResults []*bc.TxVerifyResult) error {
-       for i, tx := range txs {
-               if err := m.ValidateTx(tx, verifyResults[i]); err != nil {
+       for i, tx := range block.Transactions {
+               if err := m.ValidateTx(tx, verifyResults[i], block.Height); err != nil {
                        return err
                }
        }
@@ -156,9 +154,9 @@ func (m *MovCore) ValidateTxs(txs []*types.Tx, verifyResults []*bc.TxVerifyResul
 }
 
 // ValidateTx validate one transaction.
-func (m *MovCore) ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error {
+func (m *MovCore) ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult, blockHeight uint64) error {
        if common.IsMatchedTx(tx) {
-               if err := validateMatchedTx(tx, verifyResult); err != nil {
+               if err := validateMatchedTx(tx, verifyResult, blockHeight); err != nil {
                        return err
                }
        } else if common.IsCancelOrderTx(tx) {
@@ -182,13 +180,19 @@ func (m *MovCore) ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) erro
        return nil
 }
 
+// matchedTxFee is object to record the mov tx's fee information
+type matchedTxFee struct {
+       rewardProgram []byte
+       amount        int64
+}
+
 // calcFeeAmount return the amount of fee in the matching transaction
-func calcFeeAmount(matchedTx *types.Tx) (map[bc.AssetID]int64, error) {
-       assetFeeMap := make(map[bc.AssetID]int64)
+func calcFeeAmount(matchedTx *types.Tx) (map[bc.AssetID]*matchedTxFee, error) {
+       assetFeeMap := make(map[bc.AssetID]*matchedTxFee)
        dealProgMaps := make(map[string]bool)
 
        for _, input := range matchedTx.Inputs {
-               assetFeeMap[input.AssetID()] = int64(input.AssetAmount().Amount)
+               assetFeeMap[input.AssetID()] = &matchedTxFee{amount: int64(input.AssetAmount().Amount)}
                contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram())
                if err != nil {
                        return nil, err
@@ -200,10 +204,12 @@ func calcFeeAmount(matchedTx *types.Tx) (map[bc.AssetID]int64, error) {
        for _, output := range matchedTx.Outputs {
                assetAmount := output.AssetAmount()
                if _, ok := dealProgMaps[hex.EncodeToString(output.ControlProgram())]; ok || segwit.IsP2WMCScript(output.ControlProgram()) {
-                       assetFeeMap[*assetAmount.AssetId] -= int64(assetAmount.Amount)
-                       if assetFeeMap[*assetAmount.AssetId] <= 0 {
+                       assetFeeMap[*assetAmount.AssetId].amount -= int64(assetAmount.Amount)
+                       if assetFeeMap[*assetAmount.AssetId].amount <= 0 {
                                delete(assetFeeMap, *assetAmount.AssetId)
                        }
+               } else {
+                       assetFeeMap[*assetAmount.AssetId].rewardProgram = output.ControlProgram()
                }
        }
        return assetFeeMap, nil
@@ -246,7 +252,7 @@ func validateMagneticContractArgs(fromAssetAmount bc.AssetAmount, program []byte
        return nil
 }
 
-func validateMatchedTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error {
+func validateMatchedTx(tx *types.Tx, verifyResult *bc.TxVerifyResult, blockHeight uint64) error {
        if verifyResult.StatusFail {
                return errStatusFailMustFalse
        }
@@ -275,21 +281,32 @@ func validateMatchedTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error {
                return errAssetIDMustUniqueInMatchedTx
        }
 
-       return validateMatchedTxFeeAmount(tx)
+       return validateMatchedTxFee(tx, blockHeight)
 }
 
-func validateMatchedTxFeeAmount(tx *types.Tx) error {
-       orders, err := getDeleteOrdersFromTx(tx)
+func validateMatchedTxFee(tx *types.Tx, blockHeight uint64) error {
+       matchedTxFees, err := calcFeeAmount(tx)
        if err != nil {
                return err
        }
 
-       receivedAmount, priceDiff := match.CalcReceivedAmount(orders)
-       feeAmounts, err := calcFeeAmount(tx)
+       for _, fee := range matchedTxFees {
+               if err := validateRewardProgram(blockHeight, hex.EncodeToString(fee.rewardProgram)); err != nil {
+                       return err
+               }
+       }
+
+       orders, err := getDeleteOrdersFromTx(tx)
        if err != nil {
                return err
        }
 
+       feeAmounts := make(map[bc.AssetID]int64)
+       for assetID, fee := range matchedTxFees {
+               feeAmounts[assetID] = fee.amount
+       }
+
+       receivedAmount, priceDiff := match.CalcReceivedAmount(orders)
        feeStrategy := match.NewDefaultFeeStrategy(maxFeeRate)
        return feeStrategy.Validate(receivedAmount, priceDiff, feeAmounts)
 }
@@ -472,3 +489,31 @@ func mergeOrders(addOrderMap, deleteOrderMap map[string]*common.Order) ([]*commo
        }
        return addOrders, deleteOrders
 }
+
+// getRewardProgram return the reward program by specified block height
+// if no reward program configured, then will return empty string
+// if reward program of 0-100 height is configured, but the specified height is 200, then will return  0-100's reward program
+// the second return value represent whether to find exactly
+func getRewardProgram(height uint64) (string, bool) {
+       rewardPrograms := consensus.ActiveNetParams.MovRewardPrograms
+       if len(rewardPrograms) == 0 {
+               return "", false
+       }
+
+       var program string
+       for _, rewardProgram := range rewardPrograms {
+               program = rewardProgram.Program
+               if height >= rewardProgram.BeginBlock && height <= rewardProgram.EndBlock {
+                       return program, true
+               }
+       }
+       return program, false
+}
+
+func validateRewardProgram(height uint64, program string) error {
+       rewardProgram, exact := getRewardProgram(height)
+       if exact && rewardProgram != program {
+               return errRewardProgramIsWrong
+       }
+       return nil
+}