OSDN Git Service

validate reward address (#505)
authorPoseidon <shenao.78@163.com>
Fri, 6 Mar 2020 08:09:40 +0000 (16:09 +0800)
committerGitHub <noreply@github.com>
Fri, 6 Mar 2020 08:09:40 +0000 (16:09 +0800)
* validate reward address

* fix ci

* opt code

* opt code

* opt code

application/mov/mov_core.go
application/mov/mov_core_test.go
consensus/general.go
proposal/proposal.go
protocol/protocol.go
protocol/tx.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
+}
index 9358f89..711dad8 100644 (file)
@@ -289,6 +289,14 @@ func TestApplyBlock(t *testing.T) {
 }
 
 func TestValidateBlock(t *testing.T) {
+       consensus.ActiveNetParams.MovRewardPrograms = []consensus.MovRewardProgram{
+               {
+                       BeginBlock: 0,
+                       EndBlock:   100,
+                       Program:    hex.EncodeToString(mock.RewardProgram),
+               },
+       }
+
        cases := []struct {
                desc          string
                block         *types.Block
@@ -410,7 +418,7 @@ func TestValidateBlock(t *testing.T) {
                                                        types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")),
                                                        types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("53")),
                                                        types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 10, []byte{0x51}),
-                                                       types.NewIntraChainOutput(*consensus.BTMAssetID, 100, []byte{0x51}),
+                                                       types.NewIntraChainOutput(*consensus.BTMAssetID, 100, mock.RewardProgram),
                                                },
                                        }),
                                },
@@ -429,7 +437,7 @@ func TestValidateBlock(t *testing.T) {
                                                },
                                                Outputs: []*types.TxOutput{
                                                        types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, 10, testutil.MustDecodeHexString("51")),
-                                                       types.NewIntraChainOutput(*consensus.BTMAssetID, 100, []byte{0x51}),
+                                                       types.NewIntraChainOutput(*consensus.BTMAssetID, 100, mock.RewardProgram),
                                                },
                                        }),
                                },
@@ -452,7 +460,7 @@ func TestValidateBlock(t *testing.T) {
                                                        // re-order
                                                        types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 270, mock.Eth2BtcOrders[2].Utxo.ControlProgram),
                                                        // fee
-                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 40, []byte{0x59}),
+                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 40, mock.RewardProgram),
                                                },
                                        }),
                                },
@@ -514,24 +522,24 @@ func TestCalcMatchedTxFee(t *testing.T) {
                desc             string
                tx               types.TxData
                maxFeeRate       float64
-               wantMatchedTxFee map[bc.AssetID]int64
+               wantMatchedTxFee map[bc.AssetID]*matchedTxFee
        }{
                {
                        desc:             "fee less than max fee",
                        maxFeeRate:       0.05,
-                       wantMatchedTxFee: map[bc.AssetID]int64{mock.ETH: 10},
+                       wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{mock.ETH: {amount: 10, rewardProgram: mock.RewardProgram}},
                        tx:               mock.MatchedTxs[1].TxData,
                },
                {
                        desc:             "fee refund in tx",
                        maxFeeRate:       0.05,
-                       wantMatchedTxFee: map[bc.AssetID]int64{mock.ETH: 25},
+                       wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{mock.ETH: {amount: 25, rewardProgram: mock.RewardProgram}},
                        tx:               mock.MatchedTxs[2].TxData,
                },
                {
                        desc:             "fee is zero",
                        maxFeeRate:       0.05,
-                       wantMatchedTxFee: map[bc.AssetID]int64{},
+                       wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{},
                        tx:               mock.MatchedTxs[0].TxData,
                },
        }
@@ -549,7 +557,13 @@ func TestCalcMatchedTxFee(t *testing.T) {
 }
 
 func TestBeforeProposalBlock(t *testing.T) {
-       consensus.ActiveNetParams.MovRewardProgram = hex.EncodeToString(mock.RewardProgram)
+       consensus.ActiveNetParams.MovRewardPrograms = []consensus.MovRewardProgram{
+               {
+                       BeginBlock: 0,
+                       EndBlock:   100,
+                       Program:    hex.EncodeToString(mock.RewardProgram),
+               },
+       }
 
        cases := []struct {
                desc           string
index 20392b1..fafda5a 100644 (file)
@@ -75,6 +75,13 @@ type ProducerSubsidy struct {
        Subsidy    uint64
 }
 
+// MovRewardProgram is a reward address corresponding to the range of the specified block height when matching transactions
+type MovRewardProgram struct {
+       BeginBlock uint64
+       EndBlock   uint64
+       Program    string
+}
+
 // Params store the config for different network
 type Params struct {
        // Name defines a human-readable identifier for the network.
@@ -108,7 +115,7 @@ type Params struct {
        MovStartHeight uint64
 
        // Used to receive rewards for matching transactions
-       MovRewardProgram string
+       MovRewardPrograms []MovRewardProgram
 }
 
 // ActiveNetParams is the active NetParams
index 7ab48ce..1a4d1c8 100644 (file)
@@ -102,7 +102,7 @@ func (b *blockBuilder) applyTransactions(txs []*types.Tx, timeoutStatus uint8) e
                        continue
                }
 
-               results, gasLeft := preValidateTxs(tempTxs, b.chain, b.utxoView, b.gasLeft)
+               results, gasLeft := b.preValidateTxs(tempTxs, b.chain, b.utxoView, b.gasLeft)
                for _, result := range results {
                        if result.err != nil && !result.gasOnly {
                                log.WithFields(log.Fields{"module": logModule, "error": result.err}).Error("mining block generation: skip tx due to")
@@ -289,7 +289,7 @@ type validateTxResult struct {
        err     error
 }
 
-func preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoViewpoint, gasLeft int64) ([]*validateTxResult, int64) {
+func (b *blockBuilder) preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoViewpoint, gasLeft int64) ([]*validateTxResult, int64) {
        var results []*validateTxResult
        bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: chain.BestBlockHeight() + 1}}
        bcTxs := make([]*bc.Tx, len(txs))
@@ -323,7 +323,7 @@ func preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoView
                        continue
                }
 
-               if err := validateBySubProtocols(txs[i], validateResults[i].GetError() != nil, chain.SubProtocols()); err != nil {
+               if err := b.validateBySubProtocols(txs[i], validateResults[i].GetError() != nil, chain.SubProtocols()); err != nil {
                        results = append(results, &validateTxResult{tx: txs[i], err: err})
                        continue
                }
@@ -334,10 +334,10 @@ func preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoView
        return results, gasLeft
 }
 
-func validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.Protocoler) error {
+func (b *blockBuilder) validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.Protocoler) error {
        for _, subProtocol := range subProtocols {
                verifyResult := &bc.TxVerifyResult{StatusFail: statusFail}
-               if err := subProtocol.ValidateTx(tx, verifyResult); err != nil {
+               if err := subProtocol.ValidateTx(tx, verifyResult, b.block.Height); err != nil {
                        return err
                }
        }
index 48b11b1..c067082 100644 (file)
@@ -25,8 +25,7 @@ type Protocoler interface {
        BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error)
        ChainStatus() (uint64, *bc.Hash, error)
        ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error
-       ValidateTxs(txs []*types.Tx, verifyResults []*bc.TxVerifyResult) error
-       ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error
+       ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult, blockHeight uint64) error
        ApplyBlock(block *types.Block) error
        DetachBlock(block *types.Block) error
 }
index 3c33737..1206916 100644 (file)
@@ -54,7 +54,7 @@ func (c *Chain) validateTx(tx *types.Tx, bh *types.BlockHeader) (bool, error) {
 
        txVerifyResult := &bc.TxVerifyResult{StatusFail: err != nil}
        for _, p := range c.subProtocols {
-               if err := p.ValidateTx(tx, txVerifyResult); err != nil {
+               if err := p.ValidateTx(tx, txVerifyResult, bh.Height); err != nil {
                        c.txPool.AddErrCache(&tx.ID, err)
                        return false, err
                }