OSDN Git Service

add maker taker (#537) master
authorPoseidon <shenao.78@163.com>
Thu, 22 Oct 2020 06:52:31 +0000 (14:52 +0800)
committerGitHub <noreply@github.com>
Thu, 22 Oct 2020 06:52:31 +0000 (14:52 +0800)
* add maker taker

* add trader type to input artumetns

* remove block height from engine

* update fee rate

* fix ci

* remove maker taker type

* remove maker taker type

* opt_determine_maker_taker

* price diffs no need reorder

* fix taker fee

* fix validate fee

* recover code

* maker taker equals fee

* opt code

* fix validate fee

* fix ci

* opt validate tx fee

* opt code

* update fork block height

application/mov/common/type.go
application/mov/database/mov_store.go
application/mov/database/mov_store_test.go
application/mov/match/engine.go
application/mov/match/engine_test.go
application/mov/match/fee_strategy.go
application/mov/mock/mock.go
application/mov/mov_core.go
application/mov/mov_core_test.go
proposal/proposal.go
protocol/protocol.go

index 11d89bb..749eaff 100644 (file)
@@ -26,6 +26,9 @@ type Order struct {
        Utxo             *MovUtxo
        RatioNumerator   int64
        RatioDenominator int64
+       SellerProgram    []byte
+       BlockHeight      uint64
+       TxIndex          uint64
 }
 
 // Rate return the exchange represented by float64
@@ -67,7 +70,7 @@ func (o OrderSlice) Less(i, j int) bool {
 }
 
 // NewOrderFromOutput convert txinput to order
-func NewOrderFromOutput(tx *types.Tx, outputIndex int) (*Order, error) {
+func NewOrderFromOutput(tx *types.Tx, outputIndex int, blockHeight, txIndex uint64) (*Order, error) {
        outputID := tx.OutputID(outputIndex)
        output, err := tx.IntraChainOutput(*outputID)
        if err != nil {
@@ -85,6 +88,9 @@ func NewOrderFromOutput(tx *types.Tx, outputIndex int) (*Order, error) {
                ToAssetID:        &contractArgs.RequestedAsset,
                RatioNumerator:   contractArgs.RatioNumerator,
                RatioDenominator: contractArgs.RatioDenominator,
+               SellerProgram:    contractArgs.SellerProgram,
+               BlockHeight:      blockHeight,
+               TxIndex:          txIndex,
                Utxo: &MovUtxo{
                        SourceID:       output.Source.Ref,
                        Amount:         assetAmount.Amount,
@@ -111,6 +117,7 @@ func NewOrderFromInput(tx *types.Tx, inputIndex int) (*Order, error) {
                ToAssetID:        &contractArgs.RequestedAsset,
                RatioNumerator:   contractArgs.RatioNumerator,
                RatioDenominator: contractArgs.RatioDenominator,
+               SellerProgram:    contractArgs.SellerProgram,
                Utxo: &MovUtxo{
                        SourceID:       &input.SourceID,
                        Amount:         input.Amount,
index c45b46f..0ed5eca 100644 (file)
@@ -7,6 +7,7 @@ import (
        "math"
 
        "github.com/bytom/vapor/application/mov/common"
+       "github.com/bytom/vapor/consensus/segwit"
        dbm "github.com/bytom/vapor/database/leveldb"
        "github.com/bytom/vapor/protocol/bc"
        "github.com/bytom/vapor/protocol/bc/types"
@@ -50,6 +51,8 @@ type orderData struct {
        Utxo             *common.MovUtxo
        RatioNumerator   int64
        RatioDenominator int64
+       BlockHeight      uint64
+       TxIndex          uint64
 }
 
 func calcOrderKey(fromAssetID, toAssetID *bc.AssetID, utxoHash *bc.Hash, rate float64) []byte {
@@ -152,12 +155,20 @@ func (m *LevelDBMovStore) ListOrders(orderAfter *common.Order) ([]*common.Order,
                        return nil, err
                }
 
+               contractArgs, err := segwit.DecodeP2WMCProgram(orderData.Utxo.ControlProgram)
+               if err != nil {
+                       return nil, err
+               }
+
                orders = append(orders, &common.Order{
                        FromAssetID:      orderAfter.FromAssetID,
                        ToAssetID:        orderAfter.ToAssetID,
                        Utxo:             orderData.Utxo,
                        RatioNumerator:   orderData.RatioNumerator,
                        RatioDenominator: orderData.RatioDenominator,
+                       BlockHeight:      orderData.BlockHeight,
+                       TxIndex:          orderData.TxIndex,
+                       SellerProgram:    contractArgs.SellerProgram,
                })
        }
        return orders, nil
@@ -225,6 +236,8 @@ func (m *LevelDBMovStore) addOrders(batch dbm.Batch, orders []*common.Order, tra
                        Utxo:             order.Utxo,
                        RatioNumerator:   order.RatioNumerator,
                        RatioDenominator: order.RatioDenominator,
+                       BlockHeight:      order.BlockHeight,
+                       TxIndex:          order.TxIndex,
                }
                data, err := json.Marshal(orderData)
                if err != nil {
index aeb9c3f..022051f 100644 (file)
@@ -26,6 +26,9 @@ var (
        assetID6 = &bc.AssetID{V0: 6}
        assetID7 = &bc.AssetID{V0: 7}
        assetID8 = &bc.AssetID{V0: 8}
+       
+       orderProgram = testutil.MustDecodeHexString("0020184e1cc4ee4845023888810a79eed7a42c02c544cf2c61ceac05e176d575bd4603ed4e0e0210272200204775b9e167e2c1ffe57ae3e5088af69e518be010529b5fdadf4be97656084eec20a3e21b55f44403884457166ad5847fdb5489512ba9611eee466efb9f94319143")
+       sellerProgram = testutil.MustDecodeHexString("00204775b9e167e2c1ffe57ae3e5088af69e518be010529b5fdadf4be97656084eec")
 
        mockOrders = []*common.Order{
                &common.Order{
@@ -37,8 +40,9 @@ var (
                                SourceID:       &bc.Hash{V0: 21},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID1,
@@ -49,8 +53,9 @@ var (
                                SourceID:       &bc.Hash{V0: 22},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID1,
@@ -61,8 +66,9 @@ var (
                                SourceID:       &bc.Hash{V0: 23},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID1,
@@ -73,8 +79,9 @@ var (
                                SourceID:       &bc.Hash{V0: 13},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID1,
@@ -85,8 +92,9 @@ var (
                                SourceID:       &bc.Hash{V0: 24},
                                Amount:         10,
                                SourcePos:      1,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID1,
@@ -97,8 +105,9 @@ var (
                                SourceID:       &bc.Hash{V0: 24},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID1,
@@ -109,8 +118,9 @@ var (
                                SourceID:       &bc.Hash{V0: 25},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID1,
@@ -121,8 +131,9 @@ var (
                                SourceID:       &bc.Hash{V0: 26},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID1,
@@ -133,8 +144,9 @@ var (
                                SourceID:       &bc.Hash{V0: 1},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID1,
@@ -145,8 +157,9 @@ var (
                                SourceID:       &bc.Hash{V0: 2},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID3,
@@ -157,8 +170,9 @@ var (
                                SourceID:       &bc.Hash{V0: 33},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID4,
@@ -169,8 +183,9 @@ var (
                                SourceID:       &bc.Hash{V0: 34},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID4,
@@ -181,8 +196,9 @@ var (
                                SourceID:       &bc.Hash{V0: 36},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID5,
@@ -193,8 +209,9 @@ var (
                                SourceID:       &bc.Hash{V0: 37},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
                &common.Order{
                        FromAssetID:      assetID6,
@@ -205,8 +222,9 @@ var (
                                SourceID:       &bc.Hash{V0: 38},
                                Amount:         1,
                                SourcePos:      0,
-                               ControlProgram: []byte("aa"),
+                               ControlProgram: orderProgram,
                        },
+                       SellerProgram: sellerProgram,
                },
        }
 )
@@ -624,8 +642,8 @@ func TestMovStore(t *testing.T) {
                                mockOrders[7],
                                mockOrders[6],
                                mockOrders[2],
-                               mockOrders[3],
                                mockOrders[4],
+                               mockOrders[3],
                                mockOrders[5],
                                mockOrders[0],
                        },
@@ -663,8 +681,8 @@ func TestMovStore(t *testing.T) {
                        wantOrders: []*common.Order{
                                mockOrders[7],
                                mockOrders[6],
-                               mockOrders[3],
                                mockOrders[4],
+                               mockOrders[3],
                                mockOrders[5],
                        },
                        wantTradePairs: []*common.TradePair{
@@ -943,8 +961,8 @@ func TestListOrders(t *testing.T) {
                                mockOrders[7],
                                mockOrders[6],
                                mockOrders[2],
-                               mockOrders[3],
                                mockOrders[4],
+                               mockOrders[3],
                                mockOrders[5],
                                mockOrders[0],
                        },
@@ -963,7 +981,6 @@ func TestListOrders(t *testing.T) {
                        },
                        query: mockOrders[3],
                        wantOrders: []*common.Order{
-                               mockOrders[4],
                                mockOrders[5],
                                mockOrders[0],
                        },
@@ -1030,8 +1047,8 @@ func TestAddOrders(t *testing.T) {
                                mockOrders[7],
                                mockOrders[6],
                                mockOrders[2],
-                               mockOrders[3],
                                mockOrders[4],
+                               mockOrders[3],
                                mockOrders[5],
                                mockOrders[0],
                        },
@@ -1055,8 +1072,8 @@ func TestAddOrders(t *testing.T) {
                                mockOrders[7],
                                mockOrders[6],
                                mockOrders[2],
-                               mockOrders[3],
                                mockOrders[4],
+                               mockOrders[3],
                                mockOrders[5],
                                mockOrders[0],
                        },
index 126810d..7e027c0 100644 (file)
@@ -20,9 +20,14 @@ type Engine struct {
        rewardProgram []byte
 }
 
+type orderPosition struct {
+       blockHeight uint64
+       txIndex     uint64
+}
+
 // NewEngine return a new Engine
-func NewEngine(orderBook *OrderBook, feeStrategy FeeStrategy, rewardProgram []byte) *Engine {
-       return &Engine{orderBook: orderBook, feeStrategy: feeStrategy, rewardProgram: rewardProgram}
+func NewEngine(orderBook *OrderBook, rewardProgram []byte) *Engine {
+       return &Engine{orderBook: orderBook, feeStrategy: NewDefaultFeeStrategy(), rewardProgram: rewardProgram}
 }
 
 // HasMatchedTx check does the input trade pair can generate a match deal
@@ -47,7 +52,7 @@ func (e *Engine) NextMatchedTx(tradePairs ...*common.TradePair) (*types.Tx, erro
                return nil, errors.New("the specified trade pairs can not be matched")
        }
 
-       tx, err := e.buildMatchTx(sortOrders(e.orderBook.PeekOrders(tradePairs)))
+       tx, partialOrderPositions, err := e.buildMatchTx(sortOrders(e.orderBook.PeekOrders(tradePairs)))
        if err != nil {
                return nil, err
        }
@@ -56,118 +61,102 @@ func (e *Engine) NextMatchedTx(tradePairs ...*common.TradePair) (*types.Tx, erro
                e.orderBook.PopOrder(tradePair)
        }
 
-       if err := e.addPartialTradeOrder(tx); err != nil {
+       if err := e.addReOrder(tx, partialOrderPositions); err != nil {
                return nil, err
        }
        return tx, nil
 }
 
-func (e *Engine) addMatchTxFeeOutput(txData *types.TxData, fees []*bc.AssetAmount) error {
+func addMatchTxFeeOutput(txData *types.TxData, fees []*bc.AssetAmount, rewardProgram []byte) {
        for _, feeAmount := range fees {
-               txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*feeAmount.AssetId, feeAmount.Amount, e.rewardProgram))
-       }
-
-       refoundAmount := map[bc.AssetID]uint64{}
-       assetIDs := []bc.AssetID{}
-       refoundScript := [][]byte{}
-       for _, input := range txData.Inputs {
-               refoundAmount[input.AssetID()] += input.Amount()
-               contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram())
-               if err != nil {
-                       return err
-               }
-
-               assetIDs = append(assetIDs, input.AssetID())
-               refoundScript = append(refoundScript, contractArgs.SellerProgram)
+               txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*feeAmount.AssetId, feeAmount.Amount, rewardProgram))
        }
-
-       for _, output := range txData.Outputs {
-               assetAmount := output.AssetAmount()
-               refoundAmount[*assetAmount.AssetId] -= assetAmount.Amount
-       }
-
-       refoundCount := len(refoundScript)
-       for _, assetID := range assetIDs {
-               amount := refoundAmount[assetID]
-               averageAmount := amount / uint64(refoundCount)
-               if averageAmount == 0 {
-                       averageAmount = 1
-               }
-
-               for i := 0; i < refoundCount && amount > 0; i++ {
-                       if i == refoundCount-1 {
-                               averageAmount = amount
-                       }
-                       txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(assetID, averageAmount, refoundScript[i]))
-                       amount -= averageAmount
-               }
-       }
-       return nil
 }
 
-func (e *Engine) addPartialTradeOrder(tx *types.Tx) error {
+func (e *Engine) addReOrder(tx *types.Tx, partialOrderPositions []*orderPosition) error {
+       index := 0
        for i, output := range tx.Outputs {
                if !segwit.IsP2WMCScript(output.ControlProgram()) || output.AssetAmount().Amount == 0 {
                        continue
                }
 
-               order, err := common.NewOrderFromOutput(tx, i)
+               partialOrderPos := partialOrderPositions[index]
+               order, err := common.NewOrderFromOutput(tx, i, partialOrderPos.blockHeight, partialOrderPos.txIndex)
                if err != nil {
                        return err
                }
 
+               index++
                e.orderBook.AddOrder(order)
        }
        return nil
 }
 
-func (e *Engine) buildMatchTx(orders []*common.Order) (*types.Tx, error) {
+func addRefundOutput(txData *types.TxData, takerProgram []byte) {
+       refundAmount := map[bc.AssetID]uint64{}
+       for _, input := range txData.Inputs {
+               refundAmount[input.AssetID()] += input.Amount()
+       }
+
+       for _, output := range txData.Outputs {
+               assetAmount := output.AssetAmount()
+               refundAmount[*assetAmount.AssetId] -= assetAmount.Amount
+       }
+
+       for assetID, amount := range refundAmount {
+               if amount != 0 {
+                       txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(assetID, amount, takerProgram))
+               }
+       }
+}
+
+func (e *Engine) buildMatchTx(orders []*common.Order) (*types.Tx, []*orderPosition, error) {
        txData := &types.TxData{Version: 1}
        for _, order := range orders {
                input := types.NewSpendInput(nil, *order.Utxo.SourceID, *order.FromAssetID, order.Utxo.Amount, order.Utxo.SourcePos, order.Utxo.ControlProgram)
                txData.Inputs = append(txData.Inputs, input)
        }
 
+       takerPos := takerPos(orders)
        receivedAmounts, priceDiffs := CalcReceivedAmount(orders)
-       allocatedAssets := e.feeStrategy.Allocate(receivedAmounts, priceDiffs)
-       if err := addMatchTxOutput(txData, orders, receivedAmounts, allocatedAssets); err != nil {
-               return nil, err
-       }
+       allocatedAssets := e.feeStrategy.Allocate(receivedAmounts, priceDiffs, takerPos)
 
-       if err := e.addMatchTxFeeOutput(txData, allocatedAssets.Fees); err != nil {
-               return nil, err
+       partialOrderPositions, err := addMatchTxOutput(txData, orders, receivedAmounts, allocatedAssets)
+       if err != nil {
+               return nil, nil, err
        }
 
+       addMatchTxFeeOutput(txData, allocatedAssets.Fees, e.rewardProgram)
+       addRefundOutput(txData, orders[takerPos].SellerProgram)
+
        byteData, err := txData.MarshalText()
        if err != nil {
-               return nil, err
+               return nil, nil, err
        }
 
        txData.SerializedSize = uint64(len(byteData))
-       return types.NewTx(*txData), nil
+       return types.NewTx(*txData), partialOrderPositions, nil
 }
 
-func addMatchTxOutput(txData *types.TxData, orders []*common.Order, receivedAmounts []*bc.AssetAmount, allocatedAssets *AllocatedAssets) error {
+func addMatchTxOutput(txData *types.TxData, orders []*common.Order, receivedAmounts []*bc.AssetAmount, allocatedAssets *AllocatedAssets) ([]*orderPosition, error) {
+       var partialOrderPositions []*orderPosition
        for i, order := range orders {
-               contractArgs, err := segwit.DecodeP2WMCProgram(order.Utxo.ControlProgram)
-               if err != nil {
-                       return err
-               }
-
                receivedAmount := receivedAmounts[i].Amount
-               shouldPayAmount := calcShouldPayAmount(receivedAmount, contractArgs.RatioNumerator, contractArgs.RatioDenominator)
+               shouldPayAmount := calcShouldPayAmount(receivedAmount, order.RatioNumerator, order.RatioDenominator)
 
                requestAmount := CalcRequestAmount(order.Utxo.Amount, order.RatioNumerator, order.RatioDenominator)
                exchangeAmount := order.Utxo.Amount - shouldPayAmount
-               isPartialTrade := requestAmount > receivedAmount && CalcRequestAmount(exchangeAmount, contractArgs.RatioNumerator, contractArgs.RatioDenominator) >= 1
+               isPartialTrade := requestAmount > receivedAmount && CalcRequestAmount(exchangeAmount, order.RatioNumerator, order.RatioDenominator) >= 1
 
                setMatchTxArguments(txData.Inputs[i], isPartialTrade, len(txData.Outputs), receivedAmount)
-               txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, allocatedAssets.Receives[i].Amount, contractArgs.SellerProgram))
+
+               txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, allocatedAssets.Receives[i].Amount, order.SellerProgram))
                if isPartialTrade {
                        txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.FromAssetID, exchangeAmount, order.Utxo.ControlProgram))
+                       partialOrderPositions = append(partialOrderPositions, &orderPosition{blockHeight: order.BlockHeight, txIndex: order.TxIndex})
                }
        }
-       return nil
+       return partialOrderPositions, nil
 }
 
 func calcOppositeIndex(size int, selfIdx int) int {
@@ -230,6 +219,27 @@ func IsMatched(orders []*common.Order) bool {
        return product.Cmp(one) <= 0
 }
 
+func takerPos(orders []*common.Order) int {
+       for i, order := range orders {
+               if !isMaker(order, orders[calcOppositeIndex(len(orders), i)]) {
+                       return i
+               }
+       }
+       return 0
+}
+
+func isMaker(order, oppositeOrder *common.Order) bool {
+       if order.BlockHeight != oppositeOrder.BlockHeight {
+               return order.BlockHeight < oppositeOrder.BlockHeight
+       }
+
+       if order.TxIndex != oppositeOrder.TxIndex {
+               return order.TxIndex < oppositeOrder.TxIndex
+       }
+
+       return order.UTXOHash().String() < oppositeOrder.UTXOHash().String()
+}
+
 func setMatchTxArguments(txInput *types.TxInput, isPartialTrade bool, position int, receiveAmounts uint64) {
        var arguments [][]byte
        if isPartialTrade {
index 1ac993a..6ff2165 100644 (file)
@@ -89,7 +89,7 @@ func TestGenerateMatchedTxs(t *testing.T) {
 
        for i, c := range cases {
                movStore := mock.NewMovStore([]*common.TradePair{btc2eth, eth2btc}, c.initStoreOrders)
-               matchEngine := NewEngine(NewOrderBook(movStore, nil, nil), NewDefaultFeeStrategy(), mock.RewardProgram)
+               matchEngine := NewEngine(NewOrderBook(movStore, nil, nil), mock.RewardProgram)
                var gotMatchedTxs []*types.Tx
                for matchEngine.HasMatchedTx(c.tradePairs...) {
                        matchedTx, err := matchEngine.NextMatchedTx(c.tradePairs...)
index 1d307a0..3d0de99 100644 (file)
@@ -8,10 +8,12 @@ import (
 )
 
 var (
-       // ErrAmountOfFeeOutOfRange represent The fee charged is out of range
-       ErrAmountOfFeeOutOfRange = errors.New("amount of fee is out of range")
+       // ErrInvalidAmountOfFee represent The fee charged is invalid
+       ErrInvalidAmountOfFee = errors.New("amount of fee is invalid")
 )
 
+const forkBlockHeightAt20201028 = 78968400
+
 // AllocatedAssets represent reallocated assets after calculating fees
 type AllocatedAssets struct {
        Receives []*bc.AssetAmount
@@ -22,12 +24,11 @@ type AllocatedAssets struct {
 type FeeStrategy interface {
        // Allocate will allocate the price differential in matching transaction to the participants and the fee
        // @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, priceDiffs []*bc.AssetAmount) *AllocatedAssets
+       Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount, takerPos int) *AllocatedAssets
 
        // Validate verify that the fee charged for a matching transaction is correct
-       Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error
+       Validate(receiveAmounts, priceDiffs []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64, blockHeight uint64) error
 }
 
 // DefaultFeeStrategy represent the default fee charge strategy
@@ -39,40 +40,84 @@ func NewDefaultFeeStrategy() *DefaultFeeStrategy {
 }
 
 // Allocate will allocate the price differential in matching transaction to the participants and the fee
-func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets {
+func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount, takerPos int) *AllocatedAssets {
        receives := make([]*bc.AssetAmount, len(receiveAmounts))
        fees := make([]*bc.AssetAmount, len(receiveAmounts))
 
        for i, receiveAmount := range receiveAmounts {
-               standFee := d.calcMinFeeAmount(receiveAmount.Amount)
-               fee := standFee + priceDiffs[i].Amount
-               if maxFeeAmount := d.calcMaxFeeAmount(receiveAmount.Amount); fee > maxFeeAmount {
-                       fee = maxFeeAmount
-               }
-
-               receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: receiveAmount.Amount - standFee}
+               fee := calcMinFeeAmount(receiveAmount.Amount)
+               receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: receiveAmount.Amount - fee}
                fees[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: fee}
+
+               if i == takerPos {
+                       for _, priceDiff := range priceDiffs {
+                               if *priceDiff.AssetId == *receiveAmount.AssetId {
+                                       fee = calcMinFeeAmount(priceDiff.Amount)
+                                       priceDiff.Amount -= fee
+                                       fees[i].Amount += fee
+                               }
+                       }
+               }
        }
        return &AllocatedAssets{Receives: receives, Fees: fees}
 }
 
 // Validate verify that the fee charged for a matching transaction is correct
-func (d *DefaultFeeStrategy) Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
+func (d *DefaultFeeStrategy) Validate(receiveAmounts, priceDiffs []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64, blockHeight uint64) error {
+       if blockHeight < forkBlockHeightAt20201028 {
+               return legendValidateFee(receiveAmounts, feeAmounts)
+       }
+       return validateFee(receiveAmounts, priceDiffs, feeAmounts)
+}
+
+func validateFee(receiveAmounts, priceDiffs []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
+       existTaker := false
+       for _, receiveAmount := range receiveAmounts {
+               feeAmount := calcMinFeeAmount(receiveAmount.Amount)
+               realFeeAmount := feeAmounts[*receiveAmount.AssetId]
+               if equalsFeeAmount(realFeeAmount, feeAmount) {
+                       continue
+               }
+
+               if existTaker {
+                       return ErrInvalidAmountOfFee
+               }
+
+               for _, priceDiff := range priceDiffs {
+                       if *priceDiff.AssetId == *receiveAmount.AssetId {
+                               feeAmount += calcMinFeeAmount(priceDiff.Amount)
+                       }
+               }
+
+               if !equalsFeeAmount(realFeeAmount, feeAmount) {
+                       return ErrInvalidAmountOfFee
+               }
+               existTaker = true
+       }
+       return nil
+}
+
+func equalsFeeAmount(realFeeAmount, feeAmount uint64) bool {
+       var tolerance float64 = 5
+       return math.Abs(float64(realFeeAmount)-float64(feeAmount)) < tolerance
+}
+
+func legendValidateFee(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
        for _, receiveAmount := range receiveAmounts {
-               feeAmount := feeAmounts[*receiveAmount.AssetId]
-               maxFeeAmount := d.calcMaxFeeAmount(receiveAmount.Amount)
-               minFeeAmount := d.calcMinFeeAmount(receiveAmount.Amount)
-               if feeAmount < minFeeAmount || feeAmount > maxFeeAmount {
-                       return ErrAmountOfFeeOutOfRange
+               realFeeAmount := feeAmounts[*receiveAmount.AssetId]
+               minFeeAmount := calcMinFeeAmount(receiveAmount.Amount)
+               maxFeeAmount := calcMaxFeeAmount(receiveAmount.Amount)
+               if realFeeAmount < minFeeAmount || realFeeAmount > maxFeeAmount {
+                       return ErrInvalidAmountOfFee
                }
        }
        return nil
 }
 
-func (d *DefaultFeeStrategy) calcMinFeeAmount(amount uint64) uint64 {
+func calcMinFeeAmount(amount uint64) uint64 {
        return uint64(math.Ceil(float64(amount) / 1000))
 }
 
-func (d *DefaultFeeStrategy) calcMaxFeeAmount(amount uint64) uint64 {
+func calcMaxFeeAmount(amount uint64) uint64 {
        return uint64(math.Ceil(float64(amount) * 0.05))
 }
index 0b5da6c..12daba6 100644 (file)
@@ -28,6 +28,9 @@ var (
                                Amount:         10,
                                ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"), 50, 1),
                        },
+                       BlockHeight:   100,
+                       TxIndex:       1,
+                       SellerProgram: testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"),
                },
                {
                        FromAssetID:      &BTC,
@@ -40,6 +43,9 @@ var (
                                Amount:         20,
                                ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252"), 53, 1),
                        },
+                       BlockHeight:   100,
+                       TxIndex:       2,
+                       SellerProgram: testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252"),
                },
                {
                        FromAssetID:      &BTC,
@@ -52,6 +58,7 @@ var (
                                Amount:         15,
                                ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252"), 53, 1),
                        },
+                       SellerProgram: testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252"),
                },
                {
                        FromAssetID:      &BTC,
@@ -64,6 +71,7 @@ var (
                                Amount:         17,
                                ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252"), 53, 1),
                        },
+                       SellerProgram: testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252"),
                },
        }
 
@@ -79,6 +87,7 @@ var (
                                Amount:         510,
                                ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253"), 1, 51.0),
                        },
+                       SellerProgram: testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253"),
                },
                {
                        FromAssetID:      &ETH,
@@ -91,6 +100,9 @@ var (
                                Amount:         416,
                                ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254"), 1, 52.0),
                        },
+                       BlockHeight:   100,
+                       TxIndex:       2,
+                       SellerProgram: testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254"),
                },
                {
                        FromAssetID:      &ETH,
@@ -103,6 +115,9 @@ var (
                                Amount:         810,
                                ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255"), 1, 54.0),
                        },
+                       BlockHeight:   101,
+                       TxIndex:       1,
+                       SellerProgram: testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255"),
                },
                {
                        FromAssetID:      &ETH,
@@ -115,6 +130,9 @@ var (
                                Amount:         600,
                                ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256"), 1, 150.0),
                        },
+                       BlockHeight:   101,
+                       TxIndex:       3,
+                       SellerProgram: testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256"),
                },
        }
 
@@ -130,6 +148,7 @@ var (
                                Amount:         100,
                                ControlProgram: MustCreateP2WMCProgram(ETC, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255"), 1, 2.0),
                        },
+                       SellerProgram: testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255"),
                },
        }
 
@@ -145,6 +164,7 @@ var (
                                Amount:         50,
                                ControlProgram: MustCreateP2WMCProgram(EOS, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3"), 2, 1.0),
                        },
+                       SellerProgram: testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3"),
                },
        }
 
@@ -160,6 +180,7 @@ var (
                                Amount:         500,
                                ControlProgram: MustCreateP2WMCProgram(EOS, testutil.MustDecodeHexString("0014e3178c0f294a9a8f4b304236406507913091df86"), 2, 1.0),
                        },
+                       SellerProgram: testutil.MustDecodeHexString("0014e3178c0f294a9a8f4b304236406507913091df86"),
                },
        }
 
@@ -175,6 +196,7 @@ var (
                                Amount:         1000,
                                ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("00144d0dfc8a0c5ce41d31d4f61d99aff70588bff8bc"), 1, 100.0),
                        },
+                       SellerProgram: testutil.MustDecodeHexString("00144d0dfc8a0c5ce41d31d4f61d99aff70588bff8bc"),
                },
        }
 
@@ -261,7 +283,9 @@ var (
                // partial matched transaction from Btc2EthOrders[0], Eth2BtcOrders[1]
                types.NewTx(types.TxData{
                        Inputs: []*types.TxInput{
+                               // maker
                                types.NewSpendInput([][]byte{vm.Int64Bytes(416), vm.Int64Bytes(0), vm.Int64Bytes(0)}, *Btc2EthOrders[0].Utxo.SourceID, *Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, Btc2EthOrders[0].Utxo.SourcePos, Btc2EthOrders[0].Utxo.ControlProgram),
+                               // taker
                                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{
@@ -285,15 +309,19 @@ var (
                                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(*Btc2EthOrders[0].ToAssetID, 2, RewardProgram),
                                types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, RewardProgram),
+                               // refund to taker
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
                        },
                }),
 
                // partial matched transaction from Btc2EthOrders[0], Eth2BtcOrders[2]
                types.NewTx(types.TxData{
                        Inputs: []*types.TxInput{
+                               // maker
                                types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *Btc2EthOrders[0].Utxo.SourceID, *Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, Btc2EthOrders[0].Utxo.SourcePos, Btc2EthOrders[0].Utxo.ControlProgram),
+                               // taker
                                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{
@@ -302,16 +330,17 @@ var (
                                // re-order
                                types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 270, Eth2BtcOrders[2].Utxo.ControlProgram),
                                // fee
-                               types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 25, RewardProgram),
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram),
                                types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 1, RewardProgram),
-                               // refund
-                               types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
-                               types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+                               // re-order
+                               types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 40, Eth2BtcOrders[2].SellerProgram),
                        },
                }),
                types.NewTx(types.TxData{
                        Inputs: []*types.TxInput{
+                               // maker
                                types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(0)}, *Btc2EthOrders[1].Utxo.SourceID, *Btc2EthOrders[1].FromAssetID, Btc2EthOrders[1].Utxo.Amount, Btc2EthOrders[1].Utxo.SourcePos, Btc2EthOrders[1].Utxo.ControlProgram),
+                               // taker
                                types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, testutil.MustDecodeHash("39bdb7058a0c31fb740af8e3c382bf608efff1b041cd4dd461332722ad24552a"), *Eth2BtcOrders[2].FromAssetID, 270, 2, Eth2BtcOrders[2].Utxo.ControlProgram),
                        },
                        Outputs: []*types.TxOutput{
@@ -365,9 +394,9 @@ 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, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
-                               types.NewIntraChainOutput(EOS, 999, testutil.MustDecodeHexString("0014e3178c0f294a9a8f4b304236406507913091df86")),
-                               types.NewIntraChainOutput(BTC, 9, testutil.MustDecodeHexString("00144d0dfc8a0c5ce41d31d4f61d99aff70588bff8bc")),
+                               types.NewIntraChainOutput(ETH, 499, Btc2EthOrders[0].SellerProgram),
+                               types.NewIntraChainOutput(EOS, 999, Eth2EosOrders[0].SellerProgram),
+                               types.NewIntraChainOutput(BTC, 9, Eos2BtcOrders[0].SellerProgram),
                                // fee
                                types.NewIntraChainOutput(ETH, 1, RewardProgram),
                                types.NewIntraChainOutput(EOS, 1, RewardProgram),
@@ -443,42 +472,46 @@ var (
                // full matched transaction from Btc2EthOrders[0] Eth2BtcOrders[3]
                types.NewTx(types.TxData{
                        Inputs: []*types.TxInput{
+                               // maker
                                types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *Btc2EthOrders[0].Utxo.SourceID, *Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, Btc2EthOrders[0].Utxo.SourcePos, Btc2EthOrders[0].Utxo.ControlProgram),
+                               // taker
                                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, 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),
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 2, RewardProgram),
                                // refund
-                               types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 3, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
-                               types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 3, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
-                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 38, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
-                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 38, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 5, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
                        },
                }),
        }
 )
 
 func MustCreateP2WMCProgram(requestAsset bc.AssetID, sellerProgram []byte, ratioNumerator, ratioDenominator int64) []byte {
-       contractArgs := vmutil.MagneticContractArgs{
+       contractArgs := createContractArgs(requestAsset, sellerProgram, ratioNumerator, ratioDenominator)
+       program, err := vmutil.P2WMCProgram(*contractArgs)
+       if err != nil {
+               panic(err)
+       }
+       return program
+}
+
+func createContractArgs(requestAsset bc.AssetID, sellerProgram []byte, ratioNumerator, ratioDenominator int64) *vmutil.MagneticContractArgs {
+       return &vmutil.MagneticContractArgs{
                RequestedAsset:   requestAsset,
                RatioNumerator:   ratioNumerator,
                RatioDenominator: ratioDenominator,
                SellerProgram:    sellerProgram,
                SellerKey:        testutil.MustDecodeHexString("ad79ec6bd3a6d6dbe4d0ee902afc99a12b9702fb63edce5f651db3081d868b75"),
        }
-       program, err := vmutil.P2WMCProgram(contractArgs)
-       if err != nil {
-               panic(err)
-       }
-       return program
 }
 
-func MustNewOrderFromOutput(tx *types.Tx, outputIndex int) *common.Order {
-       order, err := common.NewOrderFromOutput(tx, outputIndex)
+func MustNewOrderFromOutputV2(tx *types.Tx, outputIndex int, blockHeight, txIndex uint64) *common.Order {
+       order, err := common.NewOrderFromOutput(tx, outputIndex, blockHeight, txIndex)
        if err != nil {
                panic(err)
        }
@@ -486,6 +519,10 @@ func MustNewOrderFromOutput(tx *types.Tx, outputIndex int) *common.Order {
        return order
 }
 
+func MustNewOrderFromOutput(tx *types.Tx, outputIndex int) *common.Order {
+       return MustNewOrderFromOutputV2(tx, outputIndex, 0, 0)
+}
+
 func hashPtr(hash bc.Hash) *bc.Hash {
        return &hash
 }
index 4a08cf5..2980553 100644 (file)
@@ -62,11 +62,11 @@ func (m *Core) ApplyBlock(block *types.Block) error {
                return m.InitChainStatus(&blockHash)
        }
 
-       if err := m.validateMatchedTxSequence(block.Transactions); err != nil {
+       if err := m.validateMatchedTxSequence(movTxs(block)); err != nil {
                return err
        }
 
-       addOrders, deleteOrders, err := decodeTxsOrders(block.Transactions)
+       addOrders, deleteOrders, err := decodeTxsOrders(movTxs(block))
        if err != nil {
                return err
        }
@@ -74,24 +74,36 @@ func (m *Core) ApplyBlock(block *types.Block) error {
        return m.movStore.ProcessOrders(addOrders, deleteOrders, &block.BlockHeader)
 }
 
+// Tx contains raw transaction and the sequence of tx in block
+type Tx struct {
+       rawTx       *types.Tx
+       blockHeight uint64
+       txIndex     uint64
+}
+
+// NewTx create a new Tx instance
+func NewTx(tx *types.Tx, blockHeight, txIndex uint64) *Tx {
+       return &Tx{rawTx: tx, blockHeight: blockHeight, txIndex: txIndex}
+}
+
 // BeforeProposalBlock return all transactions than can be matched, and the number of transactions cannot exceed the given capacity.
-func (m *Core) BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error) {
-       if blockHeight <= m.startBlockHeight {
+func (m *Core) BeforeProposalBlock(block *types.Block, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error) {
+       if block.Height <= m.startBlockHeight {
                return nil, nil
        }
 
-       orderBook, err := buildOrderBook(m.movStore, txs)
+       orderBook, err := buildOrderBook(m.movStore, movTxs(block))
        if err != nil {
                return nil, err
        }
 
-       program, _ := getRewardProgram(blockHeight)
+       program, _ := getRewardProgram(block.Height)
        rewardProgram, err := hex.DecodeString(program)
        if err != nil {
                return nil, errNotConfiguredRewardProgram
        }
 
-       matchEngine := match.NewEngine(orderBook, match.NewDefaultFeeStrategy(), rewardProgram)
+       matchEngine := match.NewEngine(orderBook, rewardProgram)
        tradePairIterator := database.NewTradePairIterator(m.movStore)
        matchCollector := newMatchTxCollector(matchEngine, tradePairIterator, gasLeft, isTimeout)
        return matchCollector.result()
@@ -123,7 +135,7 @@ func (m *Core) DetachBlock(block *types.Block) error {
                return nil
        }
 
-       deleteOrders, addOrders, err := decodeTxsOrders(block.Transactions)
+       deleteOrders, addOrders, err := decodeTxsOrders(movTxs(block))
        if err != nil {
                return err
        }
@@ -294,7 +306,7 @@ func validateMatchedTx(tx *types.Tx, blockHeight uint64) error {
                toAssetIDMap[order.ToAssetID.String()] = true
        }
 
-       if len(fromAssetIDMap) != len(tx.Inputs) || len(toAssetIDMap) != len(tx.Inputs) {
+       if inputSize := len(tx.Inputs); len(fromAssetIDMap) != inputSize || len(toAssetIDMap) != inputSize {
                return errAssetIDMustUniqueInMatchedTx
        }
 
@@ -313,38 +325,38 @@ func validateMatchedTxFee(tx *types.Tx, blockHeight uint64) error {
                }
        }
 
-       orders, err := getDeleteOrdersFromTx(tx)
+       orders, err := parseDeleteOrdersFromTx(tx)
        if err != nil {
                return err
        }
 
-       receivedAmount, _ := match.CalcReceivedAmount(orders)
+       receivedAmount, priceDiffs := match.CalcReceivedAmount(orders)
        feeAmounts := make(map[bc.AssetID]uint64)
        for assetID, fee := range matchedTxFees {
                feeAmounts[assetID] = fee.amount
        }
 
        feeStrategy := match.NewDefaultFeeStrategy()
-       return feeStrategy.Validate(receivedAmount, feeAmounts)
+       return feeStrategy.Validate(receivedAmount, priceDiffs, feeAmounts, blockHeight)
 }
 
-func (m *Core) validateMatchedTxSequence(txs []*types.Tx) error {
+func (m *Core) validateMatchedTxSequence(txs []*Tx) error {
        orderBook := match.NewOrderBook(m.movStore, nil, nil)
        for _, tx := range txs {
-               if common.IsMatchedTx(tx) {
-                       tradePairs, err := getTradePairsFromMatchedTx(tx)
+               if common.IsMatchedTx(tx.rawTx) {
+                       tradePairs, err := parseTradePairsFromMatchedTx(tx.rawTx)
                        if err != nil {
                                return err
                        }
 
                        orders := orderBook.PeekOrders(tradePairs)
-                       if err := validateSpendOrders(tx, orders); err != nil {
+                       if err := validateSpendOrders(tx.rawTx, orders); err != nil {
                                return err
                        }
 
                        orderBook.PopOrders(tradePairs)
-               } else if common.IsCancelOrderTx(tx) {
-                       orders, err := getDeleteOrdersFromTx(tx)
+               } else if common.IsCancelOrderTx(tx.rawTx) {
+                       orders, err := parseDeleteOrdersFromTx(tx.rawTx)
                        if err != nil {
                                return err
                        }
@@ -354,7 +366,7 @@ func (m *Core) validateMatchedTxSequence(txs []*types.Tx) error {
                        }
                }
 
-               addOrders, err := getAddOrdersFromTx(tx)
+               addOrders, err := parseAddOrdersFromTx(tx)
                if err != nil {
                        return err
                }
@@ -390,11 +402,11 @@ func validateSpendOrders(tx *types.Tx, orders []*common.Order) error {
        return nil
 }
 
-func decodeTxsOrders(txs []*types.Tx) ([]*common.Order, []*common.Order, error) {
+func decodeTxsOrders(txs []*Tx) ([]*common.Order, []*common.Order, error) {
        deleteOrderMap := make(map[string]*common.Order)
        addOrderMap := make(map[string]*common.Order)
        for _, tx := range txs {
-               addOrders, err := getAddOrdersFromTx(tx)
+               addOrders, err := parseAddOrdersFromTx(tx)
                if err != nil {
                        return nil, nil, err
                }
@@ -403,7 +415,7 @@ func decodeTxsOrders(txs []*types.Tx) ([]*common.Order, []*common.Order, error)
                        addOrderMap[order.Key()] = order
                }
 
-               deleteOrders, err := getDeleteOrdersFromTx(tx)
+               deleteOrders, err := parseDeleteOrdersFromTx(tx.rawTx)
                if err != nil {
                        return nil, nil, err
                }
@@ -417,15 +429,15 @@ func decodeTxsOrders(txs []*types.Tx) ([]*common.Order, []*common.Order, error)
        return addOrders, deleteOrders, nil
 }
 
-func buildOrderBook(store database.MovStore, txs []*types.Tx) (*match.OrderBook, error) {
+func buildOrderBook(store database.MovStore, txs []*Tx) (*match.OrderBook, error) {
        var arrivalAddOrders, arrivalDelOrders []*common.Order
        for _, tx := range txs {
-               addOrders, err := getAddOrdersFromTx(tx)
+               addOrders, err := parseAddOrdersFromTx(tx)
                if err != nil {
                        return nil, err
                }
 
-               delOrders, err := getDeleteOrdersFromTx(tx)
+               delOrders, err := parseDeleteOrdersFromTx(tx.rawTx)
                if err != nil {
                        return nil, err
                }
@@ -437,9 +449,9 @@ func buildOrderBook(store database.MovStore, txs []*types.Tx) (*match.OrderBook,
        return match.NewOrderBook(store, arrivalAddOrders, arrivalDelOrders), nil
 }
 
-func getAddOrdersFromTx(tx *types.Tx) ([]*common.Order, error) {
+func parseAddOrdersFromTx(tx *Tx) ([]*common.Order, error) {
        var orders []*common.Order
-       for i, output := range tx.Outputs {
+       for i, output := range tx.rawTx.Outputs {
                if output.OutputType() != types.IntraChainOutputType || !segwit.IsP2WMCScript(output.ControlProgram()) {
                        continue
                }
@@ -448,7 +460,7 @@ func getAddOrdersFromTx(tx *types.Tx) ([]*common.Order, error) {
                        continue
                }
 
-               order, err := common.NewOrderFromOutput(tx, i)
+               order, err := common.NewOrderFromOutput(tx.rawTx, i, tx.blockHeight, tx.txIndex)
                if err != nil {
                        return nil, err
                }
@@ -458,7 +470,7 @@ func getAddOrdersFromTx(tx *types.Tx) ([]*common.Order, error) {
        return orders, nil
 }
 
-func getDeleteOrdersFromTx(tx *types.Tx) ([]*common.Order, error) {
+func parseDeleteOrdersFromTx(tx *types.Tx) ([]*common.Order, error) {
        var orders []*common.Order
        for i, input := range tx.Inputs {
                if input.InputType() != types.SpendInputType || !segwit.IsP2WMCScript(input.ControlProgram()) {
@@ -475,7 +487,7 @@ func getDeleteOrdersFromTx(tx *types.Tx) ([]*common.Order, error) {
        return orders, nil
 }
 
-func getTradePairsFromMatchedTx(tx *types.Tx) ([]*common.TradePair, error) {
+func parseTradePairsFromMatchedTx(tx *types.Tx) ([]*common.TradePair, error) {
        var tradePairs []*common.TradePair
        for _, tx := range tx.Inputs {
                contractArgs, err := segwit.DecodeP2WMCProgram(tx.ControlProgram())
@@ -504,6 +516,14 @@ func mergeOrders(addOrderMap, deleteOrderMap map[string]*common.Order) ([]*commo
        return addOrders, deleteOrders
 }
 
+func movTxs(block *types.Block) []*Tx {
+       var movTxs []*Tx
+       for i, tx := range block.Transactions {
+               movTxs = append(movTxs, NewTx(tx, block.Height, uint64(i)))
+       }
+       return movTxs
+}
+
 // 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
index 9b4e037..7df7c2a 100644 (file)
@@ -39,7 +39,7 @@ func TestApplyBlock(t *testing.T) {
                                },
                        },
                        blockFunc:   applyBlock,
-                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.Btc2EthMakerTxs[0], 0), mock.MustNewOrderFromOutput(mock.Eth2BtcMakerTxs[0], 0)},
+                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutputV2(mock.Btc2EthMakerTxs[0], 0, 2, 0), mock.MustNewOrderFromOutputV2(mock.Eth2BtcMakerTxs[0], 0, 2, 1)},
                        wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
                },
                {
@@ -55,10 +55,10 @@ func TestApplyBlock(t *testing.T) {
                        },
                        blockFunc: applyBlock,
                        wantOrders: []*common.Order{
-                               mock.MustNewOrderFromOutput(mock.Btc2EthMakerTxs[0], 0),
-                               mock.MustNewOrderFromOutput(mock.Eth2BtcMakerTxs[0], 0),
-                               mock.MustNewOrderFromOutput(mock.Eos2EtcMakerTxs[0], 0),
-                               mock.MustNewOrderFromOutput(mock.Eth2EosMakerTxs[0], 0),
+                               mock.MustNewOrderFromOutputV2(mock.Btc2EthMakerTxs[0], 0, 2, 0),
+                               mock.MustNewOrderFromOutputV2(mock.Eth2BtcMakerTxs[0], 0, 2, 1),
+                               mock.MustNewOrderFromOutputV2(mock.Eos2EtcMakerTxs[0], 0, 2, 2),
+                               mock.MustNewOrderFromOutputV2(mock.Eth2EosMakerTxs[0], 0, 2, 3),
                        },
                        wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
                },
@@ -85,7 +85,7 @@ func TestApplyBlock(t *testing.T) {
                        },
                        blockFunc:   applyBlock,
                        initOrders:  []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[1]},
-                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[0], 1)},
+                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutputV2(mock.MatchedTxs[0], 1, 2, 0)},
                        wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
                },
                {
@@ -98,7 +98,7 @@ func TestApplyBlock(t *testing.T) {
                        },
                        blockFunc:   applyBlock,
                        initOrders:  []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
-                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[3], 1)},
+                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutputV2(mock.MatchedTxs[3], 1, 2, 1)},
                        wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
                },
                {
@@ -113,7 +113,7 @@ func TestApplyBlock(t *testing.T) {
                        },
                        blockFunc:   applyBlock,
                        initOrders:  []*common.Order{},
-                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[4], 1)},
+                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutputV2(mock.MatchedTxs[4], 1, 2, 2)},
                        wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
                },
                {
@@ -133,8 +133,8 @@ func TestApplyBlock(t *testing.T) {
                        blockFunc:  applyBlock,
                        initOrders: []*common.Order{},
                        wantOrders: []*common.Order{
-                               mock.MustNewOrderFromOutput(mock.MatchedTxs[4], 1),
-                               mock.MustNewOrderFromOutput(mock.Eth2EosMakerTxs[0], 0),
+                               mock.MustNewOrderFromOutputV2(mock.MatchedTxs[4], 1, 2, 3),
+                               mock.MustNewOrderFromOutputV2(mock.Eth2EosMakerTxs[0], 0, 2, 4),
                        },
                        wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
                },
@@ -152,7 +152,7 @@ func TestApplyBlock(t *testing.T) {
                        },
                        blockFunc:   applyBlock,
                        initOrders:  []*common.Order{},
-                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[7], 2)},
+                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutputV2(mock.MatchedTxs[7], 2, 2, 4)},
                        wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
                },
                {
@@ -197,9 +197,13 @@ func TestApplyBlock(t *testing.T) {
                                        mock.MatchedTxs[1],
                                },
                        },
-                       blockFunc:   detachBlock,
-                       initOrders:  []*common.Order{mock.Btc2EthOrders[1]},
-                       wantOrders:  []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[0]},
+                       blockFunc:  detachBlock,
+                       initOrders: []*common.Order{mock.Btc2EthOrders[1]},
+                       wantOrders: []*common.Order{
+                               orderWithHeightAndTxIndex(mock.Btc2EthOrders[0], 0, 0),
+                               mock.Btc2EthOrders[1],
+                               orderWithHeightAndTxIndex(mock.Eth2BtcOrders[0], 0, 0),
+                       },
                        wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
                },
                {
@@ -210,9 +214,12 @@ func TestApplyBlock(t *testing.T) {
                                        mock.MatchedTxs[0],
                                },
                        },
-                       blockFunc:   detachBlock,
-                       initOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[0], 1)},
-                       wantOrders:  []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[1]},
+                       blockFunc:  detachBlock,
+                       initOrders: []*common.Order{mock.MustNewOrderFromOutputV2(mock.MatchedTxs[0], 1, 1, 0)},
+                       wantOrders: []*common.Order{
+                               orderWithHeightAndTxIndex(mock.Btc2EthOrders[0], 0, 0),
+                               orderWithHeightAndTxIndex(mock.Eth2BtcOrders[1], 0, 0),
+                       },
                        wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
                },
                {
@@ -223,9 +230,13 @@ func TestApplyBlock(t *testing.T) {
                                        mock.MatchedTxs[2], mock.MatchedTxs[3],
                                },
                        },
-                       blockFunc:   detachBlock,
-                       initOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[3], 1)},
-                       wantOrders:  []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
+                       blockFunc:  detachBlock,
+                       initOrders: []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[3], 1)},
+                       wantOrders: []*common.Order{
+                               orderWithHeightAndTxIndex(mock.Btc2EthOrders[0], 0, 0),
+                               orderWithHeightAndTxIndex(mock.Btc2EthOrders[1], 0, 0),
+                               orderWithHeightAndTxIndex(mock.Eth2BtcOrders[2], 0, 0),
+                       },
                        wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
                },
                {
@@ -266,12 +277,12 @@ func TestApplyBlock(t *testing.T) {
 
                movCore := &Core{movStore: store}
                if err := c.blockFunc(movCore, c.block); err != c.wantError {
-                       t.Errorf("#%d(%s):apply block want error(%v), got error(%v)", i, c.desc, c.wantError, err)
+                       t.Errorf("#%d(%s):want error(%v), got error(%v)", i, c.desc, c.wantError, err)
                }
 
                gotOrders := queryAllOrders(store)
                if !ordersEquals(c.wantOrders, gotOrders) {
-                       t.Errorf("#%d(%s):apply block want orders(%v), got orders(%v)", i, c.desc, c.wantOrders, gotOrders)
+                       t.Errorf("#%d(%s):want orders(%v), got orders(%v)", i, c.desc, c.wantOrders, gotOrders)
                }
 
                dbState, err := store.GetMovDatabaseState()
@@ -280,7 +291,7 @@ func TestApplyBlock(t *testing.T) {
                }
 
                if !testutil.DeepEqual(c.wantDBState, dbState) {
-                       t.Errorf("#%d(%s):apply block want db state(%v), got db state(%v)", i, c.desc, c.wantDBState, dbState)
+                       t.Errorf("#%d(%s):want db state(%v), got db state(%v)", i, c.desc, c.wantDBState, dbState)
                }
 
                testDB.Close()
@@ -471,7 +482,7 @@ func TestValidateBlock(t *testing.T) {
                                },
                        },
                        verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
-                       wantError:     match.ErrAmountOfFeeOutOfRange,
+                       wantError:     match.ErrInvalidAmountOfFee,
                },
                {
                        desc: "ratio numerator is zero",
@@ -516,7 +527,7 @@ func TestValidateBlock(t *testing.T) {
 
        for i, c := range cases {
                movCore := &Core{}
-               c.block.Height = 3456786543
+               c.block.Height = 84000000
                if err := movCore.ValidateBlock(c.block, c.verifyResults); err != c.wantError {
                        t.Errorf("#%d(%s):validate block want error(%v), got error(%v)", i, c.desc, c.wantError, err)
                }
@@ -527,33 +538,29 @@ func TestCalcMatchedTxFee(t *testing.T) {
        cases := []struct {
                desc             string
                tx               types.TxData
-               maxFeeRate       float64
                wantMatchedTxFee map[bc.AssetID]*matchedTxFee
        }{
                {
-                       desc:       "fee less than max fee",
-                       maxFeeRate: 0.05,
+                       desc: "fee less than max fee",
                        wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{
-                               mock.ETH: {amount: 11, rewardProgram: mock.RewardProgram},
                                mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram},
+                               mock.ETH: {amount: 2, rewardProgram: mock.RewardProgram},
                        },
                        tx: mock.MatchedTxs[1].TxData,
                },
                {
-                       desc:       "fee refund in tx",
-                       maxFeeRate: 0.05,
+                       desc: "fee refund in tx",
                        wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{
-                               mock.ETH: {amount: 25, rewardProgram: mock.RewardProgram},
                                mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram},
+                               mock.ETH: {amount: 1, rewardProgram: mock.RewardProgram},
                        },
                        tx: mock.MatchedTxs[2].TxData,
                },
                {
-                       desc:       "no price diff",
-                       maxFeeRate: 0.05,
+                       desc: "no price diff",
                        wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{
-                               mock.ETH: {amount: 1, rewardProgram: mock.RewardProgram},
                                mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram},
+                               mock.ETH: {amount: 1, rewardProgram: mock.RewardProgram},
                        },
                        tx: mock.MatchedTxs[0].TxData,
                },
@@ -644,7 +651,7 @@ func TestBeforeProposalBlock(t *testing.T) {
                }
 
                movCore := &Core{movStore: store}
-               gotMatchedTxs, err := movCore.BeforeProposalBlock(nil, 2, c.gasLeft, func() bool { return false })
+               gotMatchedTxs, err := movCore.BeforeProposalBlock(&types.Block{BlockHeader: types.BlockHeader{Height: 2}}, c.gasLeft, func() bool { return false })
                if err != nil {
                        t.Fatal(err)
                }
@@ -672,43 +679,43 @@ func TestValidateMatchedTxSequence(t *testing.T) {
        cases := []struct {
                desc         string
                initOrders   []*common.Order
-               transactions []*types.Tx
+               transactions []*Tx
                wantError    error
        }{
                {
                        desc:         "both db orders and transactions is empty",
                        initOrders:   []*common.Order{},
-                       transactions: []*types.Tx{},
+                       transactions: []*Tx{},
                        wantError:    nil,
                },
                {
                        desc:         "existing matched orders in db, and transactions is empty",
                        initOrders:   []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
-                       transactions: []*types.Tx{},
+                       transactions: []*Tx{},
                        wantError:    nil,
                },
                {
                        desc:         "db orders is empty, but transactions has matched tx",
                        initOrders:   []*common.Order{},
-                       transactions: []*types.Tx{mock.MatchedTxs[1]},
+                       transactions: []*Tx{{rawTx: mock.MatchedTxs[1]}},
                        wantError:    errNotMatchedOrder,
                },
                {
                        desc:         "existing matched orders in db, and corresponding matched tx in transactions",
                        initOrders:   []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
-                       transactions: []*types.Tx{mock.MatchedTxs[1]},
+                       transactions: []*Tx{{rawTx: mock.MatchedTxs[1]}},
                        wantError:    nil,
                },
                {
                        desc:         "package matched tx, one order from db, and the another order from transactions",
                        initOrders:   []*common.Order{mock.Btc2EthOrders[0]},
-                       transactions: []*types.Tx{mock.Eth2BtcMakerTxs[0], mock.MatchedTxs[10]},
+                       transactions: []*Tx{{rawTx: mock.Eth2BtcMakerTxs[0]}, {rawTx: mock.MatchedTxs[10]}},
                        wantError:    nil,
                },
                {
                        desc:         "two matched txs use the same orders",
                        initOrders:   []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
-                       transactions: []*types.Tx{mock.MatchedTxs[1], mock.MatchedTxs[1]},
+                       transactions: []*Tx{{rawTx: mock.MatchedTxs[1]}, {rawTx: mock.MatchedTxs[1]}},
                        wantError:    errNotMatchedOrder,
                },
                {
@@ -717,7 +724,7 @@ func TestValidateMatchedTxSequence(t *testing.T) {
                                mock.Btc2EthOrders[3], mock.Eth2BtcOrders[2],
                                mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
                        },
-                       transactions: []*types.Tx{mock.MatchedTxs[8]},
+                       transactions: []*Tx{{rawTx: mock.MatchedTxs[8]}},
                        wantError:    nil,
                },
                {
@@ -726,31 +733,31 @@ func TestValidateMatchedTxSequence(t *testing.T) {
                                mock.Btc2EthOrders[3], mock.Eth2BtcOrders[2],
                                mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
                        },
-                       transactions: []*types.Tx{mock.MatchedTxs[1], mock.MatchedTxs[8]},
+                       transactions: []*Tx{{rawTx: mock.MatchedTxs[1]}, {rawTx: mock.MatchedTxs[8]}},
                        wantError:    errSpendOutputIDIsIncorrect,
                },
                {
                        desc:         "matched tx and orders from packaged transactions",
                        initOrders:   []*common.Order{},
-                       transactions: []*types.Tx{mock.Btc2EthMakerTxs[0], mock.Eth2BtcMakerTxs[1], mock.MatchedTxs[4]},
+                       transactions: []*Tx{{rawTx: mock.Btc2EthMakerTxs[0]}, {rawTx: mock.Eth2BtcMakerTxs[1]}, {rawTx: mock.MatchedTxs[4]}},
                        wantError:    nil,
                },
                {
                        desc:         "package the matched tx first, then package match orders",
                        initOrders:   []*common.Order{},
-                       transactions: []*types.Tx{mock.MatchedTxs[4], mock.Btc2EthMakerTxs[0], mock.Eth2BtcMakerTxs[1]},
+                       transactions: []*Tx{{rawTx: mock.MatchedTxs[4]}, {rawTx: mock.Btc2EthMakerTxs[0]}, {rawTx: mock.Eth2BtcMakerTxs[1]}},
                        wantError:    errNotMatchedOrder,
                },
                {
                        desc:         "cancel order in transactions",
                        initOrders:   []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
-                       transactions: []*types.Tx{mock.Btc2EthCancelTxs[0], mock.MatchedTxs[1]},
+                       transactions: []*Tx{{rawTx: mock.Btc2EthCancelTxs[0]}, {rawTx: mock.MatchedTxs[1]}},
                        wantError:    errNotMatchedOrder,
                },
                {
                        desc:         "package cancel order after match tx",
                        initOrders:   []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0]},
-                       transactions: []*types.Tx{mock.MatchedTxs[1], mock.Btc2EthCancelTxs[0]},
+                       transactions: []*Tx{{rawTx: mock.MatchedTxs[1]}, {rawTx: mock.Btc2EthCancelTxs[0]}},
                        wantError:    nil,
                },
                {
@@ -759,7 +766,7 @@ func TestValidateMatchedTxSequence(t *testing.T) {
                                mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
                                mock.Eos2EtcOrders[0], mock.Etc2EosOrders[0],
                        },
-                       transactions: []*types.Tx{mock.MatchedTxs[1], mock.MatchedTxs[9]},
+                       transactions: []*Tx{{rawTx: mock.MatchedTxs[1]}, {rawTx: mock.MatchedTxs[9]}},
                        wantError:    nil,
                },
                {
@@ -768,19 +775,19 @@ func TestValidateMatchedTxSequence(t *testing.T) {
                                mock.Btc2EthOrders[0], mock.Eth2BtcOrders[0],
                                mock.Eos2EtcOrders[0], mock.Etc2EosOrders[0],
                        },
-                       transactions: []*types.Tx{mock.MatchedTxs[9], mock.MatchedTxs[1]},
+                       transactions: []*Tx{{rawTx: mock.MatchedTxs[9]}, {rawTx: mock.MatchedTxs[1]}},
                        wantError:    nil,
                },
                {
                        desc:         "package partial matched tx from db orders, and the re-pending order continue to match",
                        initOrders:   []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
-                       transactions: []*types.Tx{mock.MatchedTxs[2], mock.MatchedTxs[3]},
+                       transactions: []*Tx{{rawTx: mock.MatchedTxs[2]}, {rawTx: mock.MatchedTxs[3]}},
                        wantError:    nil,
                },
                {
                        desc:         "cancel the re-pending order",
                        initOrders:   []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
-                       transactions: []*types.Tx{mock.MatchedTxs[2], mock.Btc2EthCancelTxs[1], mock.MatchedTxs[3]},
+                       transactions: []*Tx{{rawTx: mock.MatchedTxs[2]}, {rawTx: mock.Btc2EthCancelTxs[1]}, {rawTx: mock.MatchedTxs[3]}},
                        wantError:    errNotMatchedOrder,
                },
        }
@@ -844,3 +851,9 @@ func ordersEquals(orders1 []*common.Order, orders2 []*common.Order) bool {
 func hashPtr(hash bc.Hash) *bc.Hash {
        return &hash
 }
+
+func orderWithHeightAndTxIndex(order *common.Order, blockHeight, txIndex uint64) *common.Order {
+       order.BlockHeight = blockHeight
+       order.TxIndex = txIndex
+       return order
+}
index 2186249..2832c9b 100644 (file)
@@ -148,7 +148,7 @@ func (b *blockBuilder) applyTransactionFromSubProtocol() error {
                        break
                }
 
-               subTxs, err := p.BeforeProposalBlock(b.block.Transactions, b.block.Height, b.gasLeft, isTimeout)
+               subTxs, err := p.BeforeProposalBlock(b.block, b.gasLeft, isTimeout)
                if err != nil {
                        log.WithFields(log.Fields{"module": logModule, "index": i, "error": err}).Error("failed on sub protocol txs package")
                        continue
index ea1b2b0..50e0d59 100644 (file)
@@ -26,7 +26,7 @@ var ErrNotInitSubProtocolChainStatus = errors.New("node state of sub protocol ha
 type SubProtocol interface {
        Name() string
        StartHeight() uint64
-       BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error)
+       BeforeProposalBlock(block *types.Block, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error)
 
        // ChainStatus return the the current block height and block hash of sub protocol.
        // it will return ErrNotInitSubProtocolChainStatus if not initialized.