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,
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)
// 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
}
}
}
// 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) {
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
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
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
}
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)
}
}
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
+}