OSDN Git Service

one_thousandth_fee (#508)
authorPoseidon <shenao.78@163.com>
Tue, 10 Mar 2020 11:23:41 +0000 (19:23 +0800)
committerGitHub <noreply@github.com>
Tue, 10 Mar 2020 11:23:41 +0000 (19:23 +0800)
* one_thousandth_fee

* opt code

* update fee

* bug fix

* fix all testcase

application/mov/match/match.go
application/mov/match/match_fee.go
application/mov/mock/mock.go
application/mov/mov_core.go
application/mov/mov_core_test.go

index b0eb861..da08869 100644 (file)
@@ -106,7 +106,7 @@ func (e *Engine) buildMatchTx(orders []*common.Order) (*types.Tx, error) {
 
        receivedAmounts, priceDiffs := CalcReceivedAmount(orders)
        allocatedAssets := e.feeStrategy.Allocate(receivedAmounts, priceDiffs)
-       if err := addMatchTxOutput(txData, orders, receivedAmounts, allocatedAssets.Received); err != nil {
+       if err := addMatchTxOutput(txData, orders, receivedAmounts, allocatedAssets.Receives); err != nil {
                return nil, err
        }
 
@@ -123,7 +123,7 @@ func (e *Engine) buildMatchTx(orders []*common.Order) (*types.Tx, error) {
        return types.NewTx(*txData), nil
 }
 
-func addMatchTxOutput(txData *types.TxData, orders []*common.Order, receivedAmounts, receivedAfterDeductFee []*bc.AssetAmount) error {
+func addMatchTxOutput(txData *types.TxData, orders []*common.Order, receivedAmounts, deductFeeReceives []*bc.AssetAmount) error {
        for i, order := range orders {
                contractArgs, err := segwit.DecodeP2WMCProgram(order.Utxo.ControlProgram)
                if err != nil {
@@ -135,8 +135,8 @@ func addMatchTxOutput(txData *types.TxData, orders []*common.Order, receivedAmou
                shouldPayAmount := calcShouldPayAmount(receivedAmount, contractArgs.RatioNumerator, contractArgs.RatioDenominator)
                isPartialTrade := requestAmount > receivedAmount
 
-               setMatchTxArguments(txData.Inputs[i], isPartialTrade, len(txData.Outputs), receivedAfterDeductFee[i].Amount)
-               txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, receivedAfterDeductFee[i].Amount, contractArgs.SellerProgram))
+               setMatchTxArguments(txData.Inputs[i], isPartialTrade, len(txData.Outputs), receivedAmounts[i].Amount)
+               txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, deductFeeReceives[i].Amount, contractArgs.SellerProgram))
                if isPartialTrade {
                        txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.FromAssetID, order.Utxo.Amount-shouldPayAmount, order.Utxo.ControlProgram))
                }
@@ -168,9 +168,8 @@ func calcShouldPayAmount(receiveAmount uint64, ratioNumerator, ratioDenominator
 }
 
 // CalcReceivedAmount return amount of assets received by each participant in the matching transaction and the price difference
-func CalcReceivedAmount(orders []*common.Order) ([]*bc.AssetAmount, map[bc.AssetID]int64) {
-       priceDiffs := make(map[bc.AssetID]int64)
-       var receivedAmounts, shouldPayAmounts []*bc.AssetAmount
+func CalcReceivedAmount(orders []*common.Order) ([]*bc.AssetAmount, []*bc.AssetAmount) {
+       var receivedAmounts, priceDiffs, shouldPayAmounts []*bc.AssetAmount
        for i, order := range orders {
                requestAmount := CalcRequestAmount(order.Utxo.Amount, order.RatioNumerator, order.RatioDenominator)
                oppositeOrder := orders[calcOppositeIndex(len(orders), i)]
@@ -185,7 +184,7 @@ func CalcReceivedAmount(orders []*common.Order) ([]*bc.AssetAmount, map[bc.Asset
                if oppositeShouldPayAmount.Amount > receivedAmount.Amount {
                        assetId := oppositeShouldPayAmount.AssetId
                        amount := oppositeShouldPayAmount.Amount - receivedAmount.Amount
-                       priceDiffs[*assetId] = int64(amount)
+                       priceDiffs = append(priceDiffs, &bc.AssetAmount{AssetId: assetId, Amount: amount})
                }
        }
        return receivedAmounts, priceDiffs
index b72260d..62e2211 100644 (file)
@@ -8,13 +8,13 @@ import (
 )
 
 var (
-       // ErrAmountOfFeeExceedMaximum represent The fee charged is exceeded the maximum
-       ErrAmountOfFeeExceedMaximum = errors.New("amount of fee greater than max fee amount")
+       // ErrAmountOfFeeOutOfRange represent The fee charged is out of range
+       ErrAmountOfFeeOutOfRange = errors.New("amount of fee is out of range")
 )
 
 // AllocatedAssets represent reallocated assets after calculating fees
 type AllocatedAssets struct {
-       Received []*bc.AssetAmount
+       Receives []*bc.AssetAmount
        Refunds  []RefundAssets
        Fees     []*bc.AssetAmount
 }
@@ -28,10 +28,10 @@ type FeeStrategy interface {
        // @param receiveAmounts the amount of assets that the participants in the matching transaction can received when no fee is considered
        // @param priceDiffs price differential of matching transaction
        // @return reallocated assets after calculating fees
-       Allocate(receiveAmounts []*bc.AssetAmount, priceDiffs map[bc.AssetID]int64) *AllocatedAssets
+       Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets
 
        // Validate verify that the fee charged for a matching transaction is correct
-       Validate(receiveAmounts []*bc.AssetAmount, priceDiffs, feeAmounts map[bc.AssetID]int64) error
+       Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error
 }
 
 // DefaultFeeStrategy represent the default fee charge strategy
@@ -45,59 +45,68 @@ func NewDefaultFeeStrategy(maxFeeRate float64) *DefaultFeeStrategy {
 }
 
 // Allocate will allocate the price differential in matching transaction to the participants and the fee
-func (d *DefaultFeeStrategy) Allocate(receiveAmounts []*bc.AssetAmount, priceDiffs map[bc.AssetID]int64) *AllocatedAssets {
-       var feeAmounts []*bc.AssetAmount
-       refundAmounts := make([]RefundAssets, len(receiveAmounts))
+func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets {
+       feeMap := make(map[bc.AssetID]uint64)
+       for _, priceDiff := range priceDiffs {
+               feeMap[*priceDiff.AssetId] = priceDiff.Amount
+       }
 
-       for _, receiveAmount := range receiveAmounts {
-               if _, ok := priceDiffs[*receiveAmount.AssetId]; !ok {
-                       continue
-               }
+       var fees []*bc.AssetAmount
+       refunds := make([]RefundAssets, len(receiveAmounts))
+       receives := make([]*bc.AssetAmount, len(receiveAmounts))
 
-               priceDiff := priceDiffs[*receiveAmount.AssetId]
-               maxFeeAmount := calcMaxFeeAmount(receiveAmount.Amount, d.maxFeeRate)
+       for i, receiveAmount := range receiveAmounts {
+               amount := receiveAmount.Amount
+               minFeeAmount := calcMinFeeAmount(amount)
+               receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: amount - minFeeAmount}
+               feeMap[*receiveAmount.AssetId] += minFeeAmount
 
-               feeAmount, reminder := priceDiff, int64(0)
-               if priceDiff > maxFeeAmount {
+               maxFeeAmount := calcMaxFeeAmount(amount, d.maxFeeRate)
+               feeAmount, reminder := feeMap[*receiveAmount.AssetId], uint64(0)
+               if feeAmount > maxFeeAmount {
+                       reminder = feeAmount - maxFeeAmount
                        feeAmount = maxFeeAmount
-                       reminder = priceDiff - maxFeeAmount
                }
 
-               feeAmounts = append(feeAmounts, &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: uint64(feeAmount)})
+               if feeAmount > 0 {
+                       fees = append(fees, &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: feeAmount})
+               }
 
                // There is the remaining amount after paying the handling fee, assign it evenly to participants in the transaction
-               averageAmount := reminder / int64(len(receiveAmounts))
+               averageAmount := reminder / uint64(len(receiveAmounts))
                if averageAmount == 0 {
                        averageAmount = 1
                }
 
-               for i := 0; i < len(receiveAmounts) && reminder > 0; i++ {
-                       amount := averageAmount
-                       if i == len(receiveAmounts)-1 {
-                               amount = reminder
+               for j := 0; j < len(receiveAmounts) && reminder > 0; j++ {
+                       refundAmount := averageAmount
+                       if j == len(receiveAmounts)-1 {
+                               refundAmount = reminder
                        }
-                       refundAmounts[i] = append(refundAmounts[i], &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: uint64(amount)})
+                       refunds[j] = append(refunds[j], &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: refundAmount})
                        reminder -= averageAmount
                }
        }
-
-       receivedAfterDeductFee := make([]*bc.AssetAmount, len(receiveAmounts))
-       copy(receivedAfterDeductFee, receiveAmounts)
-       return &AllocatedAssets{Received: receivedAfterDeductFee, Refunds: refundAmounts, Fees: feeAmounts}
+       return &AllocatedAssets{Receives: receives, Refunds: refunds, Fees: fees}
 }
 
 // Validate verify that the fee charged for a matching transaction is correct
-func (d *DefaultFeeStrategy) Validate(receiveAmounts []*bc.AssetAmount, feeAmounts, priceDiffs map[bc.AssetID]int64) error {
+func (d *DefaultFeeStrategy) Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
        for _, receiveAmount := range receiveAmounts {
-               if feeAmount, ok := feeAmounts[*receiveAmount.AssetId]; ok {
-                       if feeAmount > calcMaxFeeAmount(receiveAmount.Amount+uint64(priceDiffs[*receiveAmount.AssetId]), d.maxFeeRate) {
-                               return ErrAmountOfFeeExceedMaximum
-                       }
+               feeAmount := feeAmounts[*receiveAmount.AssetId]
+               maxFeeAmount := calcMaxFeeAmount(receiveAmount.Amount, d.maxFeeRate)
+               minFeeAmount := calcMinFeeAmount(receiveAmount.Amount)
+               if feeAmount < minFeeAmount || feeAmount > maxFeeAmount {
+                       return ErrAmountOfFeeOutOfRange
                }
        }
        return nil
 }
 
-func calcMaxFeeAmount(amount uint64, maxFeeRate float64) int64 {
-       return int64(math.Ceil(float64(amount) * maxFeeRate))
+func calcMinFeeAmount(amount uint64) uint64 {
+       return uint64(math.Ceil(float64(amount) / 1000))
+}
+
+func calcMaxFeeAmount(amount uint64, maxFeeRate float64) uint64 {
+       return uint64(math.Ceil(float64(amount) * maxFeeRate))
 }
index 594813b..46f0bcc 100644 (file)
@@ -265,10 +265,13 @@ var (
                                types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *Eth2BtcOrders[1].Utxo.SourceID, *Eth2BtcOrders[1].FromAssetID, Eth2BtcOrders[1].Utxo.Amount, Eth2BtcOrders[1].Utxo.SourcePos, Eth2BtcOrders[1].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
-                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 416, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 415, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
                                // re-order
                                types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 2, Btc2EthOrders[0].Utxo.ControlProgram),
-                               types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 7, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")),
+                               // fee
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 1, RewardProgram),
                        },
                }),
 
@@ -279,9 +282,11 @@ var (
                                types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *Eth2BtcOrders[0].Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, Eth2BtcOrders[0].Utxo.SourcePos, Eth2BtcOrders[0].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
-                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
-                               types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
-                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 10, RewardProgram),
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
+                               // fee
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 11, RewardProgram),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, RewardProgram),
                        },
                }),
 
@@ -292,14 +297,15 @@ var (
                                types.NewSpendInput([][]byte{vm.Int64Bytes(10), vm.Int64Bytes(1), vm.Int64Bytes(0)}, *Eth2BtcOrders[2].Utxo.SourceID, *Eth2BtcOrders[2].FromAssetID, Eth2BtcOrders[2].Utxo.Amount, Eth2BtcOrders[2].Utxo.SourcePos, Eth2BtcOrders[2].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
-                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
-                               types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
                                // re-order
                                types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 270, Eth2BtcOrders[2].Utxo.ControlProgram),
                                // fee
                                types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 25, RewardProgram),
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 1, RewardProgram),
                                // refund
-                               types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 7, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
                                types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
                        },
                }),
@@ -309,10 +315,13 @@ var (
                                types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, testutil.MustDecodeHash("39bdb7058a0c31fb740af8e3c382bf608efff1b041cd4dd461332722ad24552a"), *Eth2BtcOrders[2].FromAssetID, 270, 2, Eth2BtcOrders[2].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
-                               types.NewIntraChainOutput(*Btc2EthOrders[1].ToAssetID, 270, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")),
+                               types.NewIntraChainOutput(*Btc2EthOrders[1].ToAssetID, 269, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")),
                                // re-order
                                types.NewIntraChainOutput(*Btc2EthOrders[1].FromAssetID, 15, Btc2EthOrders[1].Utxo.ControlProgram),
-                               types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 5, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 4, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+                               // fee
+                               types.NewIntraChainOutput(*Btc2EthOrders[1].ToAssetID, 1, RewardProgram),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 1, RewardProgram),
                        },
                }),
 
@@ -323,10 +332,13 @@ var (
                                types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Eth2BtcMakerTxs[1], 0).Utxo.SourceID, *Eth2BtcOrders[1].FromAssetID, Eth2BtcOrders[1].Utxo.Amount, 0, Eth2BtcOrders[1].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
-                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 416, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 415, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
                                // re-order
                                types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 2, Btc2EthOrders[0].Utxo.ControlProgram),
-                               types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 7, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")),
+                               // fee
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 1, RewardProgram),
                        },
                }),
 
@@ -337,8 +349,11 @@ var (
                                types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Etc2EosMakerTxs[0], 0).Utxo.SourceID, *Etc2EosOrders[0].FromAssetID, Etc2EosOrders[0].Utxo.Amount, Etc2EosOrders[0].Utxo.SourcePos, Etc2EosOrders[0].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
-                               types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 50, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
-                               types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")),
+                               types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 49, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+                               types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 99, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")),
+                               // fee
+                               types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 1, RewardProgram),
+                               types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 1, RewardProgram),
                        },
                }),
 
@@ -350,9 +365,13 @@ var (
                                types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *Eos2BtcOrders[0].Utxo.SourceID, *Eos2BtcOrders[0].FromAssetID, Eos2BtcOrders[0].Utxo.Amount, Eos2BtcOrders[0].Utxo.SourcePos, Eos2BtcOrders[0].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
-                               types.NewIntraChainOutput(ETH, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
-                               types.NewIntraChainOutput(EOS, 1000, testutil.MustDecodeHexString("0014e3178c0f294a9a8f4b304236406507913091df86")),
-                               types.NewIntraChainOutput(BTC, 10, testutil.MustDecodeHexString("00144d0dfc8a0c5ce41d31d4f61d99aff70588bff8bc")),
+                               types.NewIntraChainOutput(ETH, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(EOS, 999, testutil.MustDecodeHexString("0014e3178c0f294a9a8f4b304236406507913091df86")),
+                               types.NewIntraChainOutput(BTC, 9, testutil.MustDecodeHexString("00144d0dfc8a0c5ce41d31d4f61d99aff70588bff8bc")),
+                               // fee
+                               types.NewIntraChainOutput(ETH, 1, RewardProgram),
+                               types.NewIntraChainOutput(EOS, 1, RewardProgram),
+                               types.NewIntraChainOutput(BTC, 1, RewardProgram),
                        },
                }),
 
@@ -363,10 +382,13 @@ var (
                                types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Eth2BtcMakerTxs[0], 0).Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, 0, Eth2BtcOrders[0].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
-                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
-                               types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 2, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 99, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
                                // re-order
                                types.NewIntraChainOutput(*Eth2BtcOrders[0].FromAssetID, 404, Eth2BtcOrders[0].Utxo.ControlProgram),
+                               // fee
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, RewardProgram),
                        },
                }),
 
@@ -377,12 +399,13 @@ var (
                                types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *Eth2BtcOrders[2].Utxo.SourceID, *Eth2BtcOrders[2].FromAssetID, Eth2BtcOrders[2].Utxo.Amount, Eth2BtcOrders[2].Utxo.SourcePos, Eth2BtcOrders[2].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
-                               types.NewIntraChainOutput(*Btc2EthOrders[3].ToAssetID, 810, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")),
+                               types.NewIntraChainOutput(*Btc2EthOrders[3].ToAssetID, 809, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")),
                                // re-order
                                types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, 1, Btc2EthOrders[3].Utxo.ControlProgram),
-                               types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 15, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 14, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
                                // fee
-                               types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, 1, RewardProgram),
+                               types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, 2, RewardProgram),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 1, RewardProgram),
                        },
                }),
 
@@ -393,8 +416,11 @@ var (
                                types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *Etc2EosOrders[0].Utxo.SourceID, *Etc2EosOrders[0].FromAssetID, Etc2EosOrders[0].Utxo.Amount, Etc2EosOrders[0].Utxo.SourcePos, Etc2EosOrders[0].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
-                               types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 50, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
-                               types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")),
+                               types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 49, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+                               types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 99, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")),
+                               // fee
+                               types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 1, RewardProgram),
+                               types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 1, RewardProgram),
                        },
                }),
 
@@ -405,9 +431,12 @@ var (
                                types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Eth2BtcMakerTxs[0], 0).Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, 0, Eth2BtcOrders[0].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
-                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
-                               types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
                                types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 10, RewardProgram),
+                               // fee
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, RewardProgram),
                        },
                }),
 
@@ -418,14 +447,14 @@ var (
                                types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *Eth2BtcOrders[3].Utxo.SourceID, *Eth2BtcOrders[3].FromAssetID, Eth2BtcOrders[3].Utxo.Amount, Eth2BtcOrders[3].Utxo.SourcePos, Eth2BtcOrders[3].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
-                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
-                               types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 4, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 3, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
                                // fee
                                types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 25, RewardProgram),
                                types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 1, RewardProgram),
                                // refund
-                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 37, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
-                               types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 2, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 38, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 3, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
                                types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 38, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
                                types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 3, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
                        },
index 04cc5e7..320a75f 100644 (file)
@@ -183,7 +183,7 @@ func (m *MovCore) ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult, bloc
 // matchedTxFee is object to record the mov tx's fee information
 type matchedTxFee struct {
        rewardProgram []byte
-       amount        int64
+       amount        uint64
 }
 
 // calcFeeAmount return the amount of fee in the matching transaction
@@ -192,7 +192,7 @@ func calcFeeAmount(matchedTx *types.Tx) (map[bc.AssetID]*matchedTxFee, error) {
        dealProgMaps := make(map[string]bool)
 
        for _, input := range matchedTx.Inputs {
-               assetFeeMap[input.AssetID()] = &matchedTxFee{amount: int64(input.AssetAmount().Amount)}
+               assetFeeMap[input.AssetID()] = &matchedTxFee{amount: input.AssetAmount().Amount}
                contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram())
                if err != nil {
                        return nil, err
@@ -204,7 +204,7 @@ func calcFeeAmount(matchedTx *types.Tx) (map[bc.AssetID]*matchedTxFee, error) {
        for _, output := range matchedTx.Outputs {
                assetAmount := output.AssetAmount()
                if _, ok := dealProgMaps[hex.EncodeToString(output.ControlProgram())]; ok || segwit.IsP2WMCScript(output.ControlProgram()) {
-                       assetFeeMap[*assetAmount.AssetId].amount -= int64(assetAmount.Amount)
+                       assetFeeMap[*assetAmount.AssetId].amount -= assetAmount.Amount
                        if assetFeeMap[*assetAmount.AssetId].amount <= 0 {
                                delete(assetFeeMap, *assetAmount.AssetId)
                        }
@@ -301,14 +301,14 @@ func validateMatchedTxFee(tx *types.Tx, blockHeight uint64) error {
                return err
        }
 
-       feeAmounts := make(map[bc.AssetID]int64)
+       feeAmounts := make(map[bc.AssetID]uint64)
        for assetID, fee := range matchedTxFees {
                feeAmounts[assetID] = fee.amount
        }
 
-       receivedAmount, priceDiffs := match.CalcReceivedAmount(orders)
+       receivedAmount, _ := match.CalcReceivedAmount(orders)
        feeStrategy := match.NewDefaultFeeStrategy(maxFeeRate)
-       return feeStrategy.Validate(receivedAmount, feeAmounts, priceDiffs)
+       return feeStrategy.Validate(receivedAmount, feeAmounts)
 }
 
 func (m *MovCore) validateMatchedTxSequence(txs []*types.Tx) error {
index 711dad8..a504539 100644 (file)
@@ -372,9 +372,11 @@ func TestValidateBlock(t *testing.T) {
                                                        types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *mock.Eth2BtcOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Eth2BtcOrders[0].Utxo.Amount, mock.Eth2BtcOrders[0].Utxo.SourcePos, mock.Eth2BtcOrders[0].Utxo.ControlProgram),
                                                },
                                                Outputs: []*types.TxOutput{
-                                                       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(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("51")),
+                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("53")),
+                                                       // fee
+                                                       types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 11, mock.RewardProgram),
+                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 1, mock.RewardProgram),
                                                },
                                        }),
                                },
@@ -393,9 +395,10 @@ func TestValidateBlock(t *testing.T) {
                                                        types.NewSpendInput(nil, testutil.MustDecodeHash("28b7b53d8dc90006bf97e0a4eaae2a72ec3d869873188698b694beaf20789f21"), *consensus.BTMAssetID, 100, 0, []byte{0x51}),
                                                },
                                                Outputs: []*types.TxOutput{
-                                                       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(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("51")),
+                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("53")),
+                                                       types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 11, mock.RewardProgram),
+                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 1, mock.RewardProgram),
                                                        types.NewIntraChainOutput(*consensus.BTMAssetID, 100, []byte{0x51}),
                                                },
                                        }),
@@ -415,10 +418,11 @@ func TestValidateBlock(t *testing.T) {
                                                        types.NewSpendInput([][]byte{{}, {}, vm.Int64Bytes(2)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram),
                                                },
                                                Outputs: []*types.TxOutput{
-                                                       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, mock.RewardProgram),
+                                                       types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("51")),
+                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("53")),
+                                                       types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 11, mock.RewardProgram),
+                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 1, mock.RewardProgram),
+                                                       types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, 10, testutil.MustDecodeHexString("51")),
                                                },
                                        }),
                                },
@@ -455,18 +459,19 @@ func TestValidateBlock(t *testing.T) {
                                                        types.NewSpendInput([][]byte{vm.Int64Bytes(10), vm.Int64Bytes(1), vm.Int64Bytes(0)}, *mock.Eth2BtcOrders[2].Utxo.SourceID, *mock.Eth2BtcOrders[2].FromAssetID, mock.Eth2BtcOrders[2].Utxo.Amount, mock.Eth2BtcOrders[2].Utxo.SourcePos, mock.Eth2BtcOrders[2].Utxo.ControlProgram),
                                                },
                                                Outputs: []*types.TxOutput{
-                                                       types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
-                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+                                                       types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
                                                        // re-order
                                                        types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 270, mock.Eth2BtcOrders[2].Utxo.ControlProgram),
                                                        // fee
-                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 40, mock.RewardProgram),
+                                                       types.NewIntraChainOutput(*mock.Btc2EthOrders[2].ToAssetID, 41, mock.RewardProgram),
+                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 1, mock.RewardProgram),
                                                },
                                        }),
                                },
                        },
                        verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
-                       wantError:     match.ErrAmountOfFeeExceedMaximum,
+                       wantError:     match.ErrAmountOfFeeOutOfRange,
                },
                {
                        desc: "ratio numerator is zero",
@@ -525,21 +530,30 @@ func TestCalcMatchedTxFee(t *testing.T) {
                wantMatchedTxFee map[bc.AssetID]*matchedTxFee
        }{
                {
-                       desc:             "fee less than max fee",
-                       maxFeeRate:       0.05,
-                       wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{mock.ETH: {amount: 10, rewardProgram: mock.RewardProgram}},
-                       tx:               mock.MatchedTxs[1].TxData,
+                       desc:       "fee less than max fee",
+                       maxFeeRate: 0.05,
+                       wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{
+                               mock.ETH: {amount: 11, rewardProgram: mock.RewardProgram},
+                               mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram},
+                       },
+                       tx: mock.MatchedTxs[1].TxData,
                },
                {
                        desc:             "fee refund in tx",
                        maxFeeRate:       0.05,
-                       wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{mock.ETH: {amount: 25, rewardProgram: mock.RewardProgram}},
+                       wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{
+                               mock.ETH: {amount: 25, rewardProgram: mock.RewardProgram},
+                               mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram},
+                       },
                        tx:               mock.MatchedTxs[2].TxData,
                },
                {
-                       desc:             "fee is zero",
+                       desc:             "no price diff",
                        maxFeeRate:       0.05,
-                       wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{},
+                       wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{
+                               mock.ETH: {amount: 1, rewardProgram: mock.RewardProgram},
+                               mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram},
+                       },
                        tx:               mock.MatchedTxs[0].TxData,
                },
        }