OSDN Git Service

update repo
authorChengcheng Zhang <943420582@qq.com>
Fri, 6 Dec 2019 10:35:45 +0000 (18:35 +0800)
committerChengcheng Zhang <943420582@qq.com>
Fri, 6 Dec 2019 10:35:45 +0000 (18:35 +0800)
52 files changed:
1  2 
.travis.yml
application/mov/common/type.go
application/mov/common/type_test.go
application/mov/common/util.go
application/mov/contract/contract.go
application/mov/database/mov_iterator.go
application/mov/database/mov_iterator_test.go
application/mov/database/mov_store.go
application/mov/database/mov_store_test.go
application/mov/match/match.go
application/mov/match/match_test.go
application/mov/match/order_table.go
application/mov/match/order_table_test.go
application/mov/mock/mock.go
application/mov/mock/mock_mov_store.go
application/mov/mov_core.go
application/mov/mov_core_test.go
blockchain/txbuilder/actions.go
config/federation_test.go
consensus/general.go
consensus/segwit/segwit.go
docs/federation/sql_dump/federation_shema.sql
netsync/chainmgr/fast_sync.go
netsync/chainmgr/handle.go
netsync/chainmgr/tx_keeper_test.go
node/node.go
proposal/blockproposer/blockproposer.go
proposal/proposal.go
proposal/proposal_test.go
protocol/bbft.go
protocol/bc/types/map.go
protocol/block.go
protocol/protocol.go
protocol/tx.go
protocol/txpool.go
protocol/validation/tx.go
protocol/validation/tx_test.go
protocol/validation/vmcontext.go
protocol/vm/numeric.go
protocol/vm/numeric_test.go
protocol/vm/ops.go
protocol/vm/vmutil/script.go
test/accounts_test.go
test/bench_blockchain_test.go
test/mock/mempool.go
test/performance/mining_test.go
test/util.go
test/wallet_test.go
toolbar/federation/api/handler.go
toolbar/federation/database/orm/asset.go
toolbar/federation/synchron/mainchain_keeper.go
toolbar/federation/synchron/sidechain_keeper.go

diff --cc .travis.yml
Simple merge
@@@ -1,7 -1,16 +1,16 @@@
  package common
  
- import "github.com/bytom/vapor/protocol/bc"
+ import (
+       "encoding/hex"
+       "fmt"
  
 -      "github.com/vapor/consensus/segwit"
 -      "github.com/vapor/errors"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/bc/types"
++      "github.com/bytom/vapor/consensus/segwit"
++      "github.com/bytom/vapor/errors"
++      "github.com/bytom/vapor/protocol/bc"
++      "github.com/bytom/vapor/protocol/bc/types"
+ )
+ // MovUtxo store the utxo information for mov order
  type MovUtxo struct {
        SourceID       *bc.Hash
        SourcePos      uint64
index 0000000,fc98871..8fe9e81
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,27 +1,27 @@@
 -      "github.com/vapor/consensus"
 -      "github.com/vapor/testutil"
+ package common
+ import (
+       "testing"
++      "github.com/bytom/vapor/consensus"
++      "github.com/bytom/vapor/testutil"
+ )
+ func TestCalcUTXOHash(t *testing.T) {
+       wantHash := "d94acbac0304e054569b0a2c2ab546be293552eb83d2d84af7234a013986a906"
+       controlProgram := testutil.MustDecodeHexString("0014d6f0330717170c838e6ac4c643de61e4c035e9b7")
+       sourceID := testutil.MustDecodeHash("3cada915465af2f08c93911bce7a100498fddb5738e5400269c4d5c2b2f5b261")
+       order := Order{
+               FromAssetID: consensus.BTMAssetID,
+               Utxo: &MovUtxo{
+                       SourceID:       &sourceID,
+                       SourcePos:      1,
+                       Amount:         399551000,
+                       ControlProgram: controlProgram,
+               },
+       }
+       if hash := order.UTXOHash(); hash.String() != wantHash {
+               t.Fatal("The function is incorrect")
+       }
+ }
index 0000000,860511c..09413c9
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,30 +1,30 @@@
 -      "github.com/vapor/application/mov/contract"
 -      "github.com/vapor/consensus/segwit"
 -      "github.com/vapor/protocol/bc/types"
+ package common
+ import (
++      "github.com/bytom/vapor/application/mov/contract"
++      "github.com/bytom/vapor/consensus/segwit"
++      "github.com/bytom/vapor/protocol/bc/types"
+ )
+ // IsMatchedTx check if this transaction has trade mov order input
+ func IsMatchedTx(tx *types.Tx) bool {
+       if len(tx.Inputs) < 2 {
+               return false
+       }
+       for _, input := range tx.Inputs {
+               if input.InputType() == types.SpendInputType && contract.IsTradeClauseSelector(input) && segwit.IsP2WMCScript(input.ControlProgram()) {
+                       return true
+               }
+       }
+       return false
+ }
+ // IsCancelOrderTx check if this transaction has cancel mov order input
+ func IsCancelOrderTx(tx *types.Tx) bool {
+       for _, input := range tx.Inputs {
+               if input.InputType() == types.SpendInputType && contract.IsCancelClauseSelector(input) && segwit.IsP2WMCScript(input.ControlProgram()) {
+                       return true
+               }
+       }
+       return false
+ }
index 0000000,fbf870c..75cd00d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,41 +1,41 @@@
 -      "github.com/vapor/protocol/bc/types"
 -      "github.com/vapor/protocol/vm"
+ package contract
+ import (
+       "encoding/hex"
++      "github.com/bytom/vapor/protocol/bc/types"
++      "github.com/bytom/vapor/protocol/vm"
+ )
+ const (
+       sizeOfCancelClauseArgs       = 3
+       sizeOfPartialTradeClauseArgs = 3
+       sizeOfFullTradeClauseArgs    = 2
+ )
+ // smart contract clause select for differnet unlock method
+ const (
+       PartialTradeClauseSelector int64 = iota
+       FullTradeClauseSelector
+       CancelClauseSelector
+ )
+ // IsCancelClauseSelector check if input select cancel clause
+ func IsCancelClauseSelector(input *types.TxInput) bool {
+       return len(input.Arguments()) == sizeOfCancelClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments())-1]) == hex.EncodeToString(vm.Int64Bytes(CancelClauseSelector))
+ }
+ // IsTradeClauseSelector check if input select is partial trade clause or full trade clause
+ func IsTradeClauseSelector(input *types.TxInput) bool {
+       return IsPartialTradeClauseSelector(input) || IsFullTradeClauseSelector(input)
+ }
+ // IsPartialTradeClauseSelector check if input select partial trade clause
+ func IsPartialTradeClauseSelector(input *types.TxInput) bool {
+       return len(input.Arguments()) == sizeOfPartialTradeClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments())-1]) == hex.EncodeToString(vm.Int64Bytes(PartialTradeClauseSelector))
+ }
+ // IsFullTradeClauseSelector check if input select full trade clause
+ func IsFullTradeClauseSelector(input *types.TxInput) bool {
+       return len(input.Arguments()) == sizeOfFullTradeClauseArgs && hex.EncodeToString(input.Arguments()[len(input.Arguments())-1]) == hex.EncodeToString(vm.Int64Bytes(FullTradeClauseSelector))
+ }
index 0000000,d391ace..b06debf
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,104 +1,104 @@@
 -      "github.com/vapor/application/mov/common"
 -      "github.com/vapor/protocol/bc"
+ package database
+ import (
+       log "github.com/sirupsen/logrus"
++      "github.com/bytom/vapor/application/mov/common"
++      "github.com/bytom/vapor/protocol/bc"
+ )
+ // TradePairIterator wrap read trade pair from DB action
+ type TradePairIterator struct {
+       movStore       MovStore
+       tradePairs     []*common.TradePair
+       tradePairIndex int
+ }
+ // NewTradePairIterator create the new TradePairIterator object
+ func NewTradePairIterator(movStore MovStore) *TradePairIterator {
+       return &TradePairIterator{movStore: movStore}
+ }
+ // HasNext check if there are more trade pairs in memory or DB
+ func (t *TradePairIterator) HasNext() bool {
+       tradePairSize := len(t.tradePairs)
+       if t.tradePairIndex < tradePairSize {
+               return true
+       }
+       var fromAssetID, toAssetID *bc.AssetID
+       if len(t.tradePairs) > 0 {
+               lastTradePair := t.tradePairs[tradePairSize-1]
+               fromAssetID, toAssetID = lastTradePair.FromAssetID, lastTradePair.ToAssetID
+       }
+       tradePairs, err := t.movStore.ListTradePairsWithStart(fromAssetID, toAssetID)
+       if err != nil {
+               // If the error is returned, it's an error of program itself,
+               // and cannot be recovered, so panic directly.
+               log.WithField("err", err).Fatal("fail to list trade pairs")
+       }
+       if len(tradePairs) == 0 {
+               return false
+       }
+       t.tradePairs = tradePairs
+       t.tradePairIndex = 0
+       return true
+ }
+ // Next return the next available trade pair in memory or DB
+ func (t *TradePairIterator) Next() *common.TradePair {
+       if !t.HasNext() {
+               return nil
+       }
+       tradePair := t.tradePairs[t.tradePairIndex]
+       t.tradePairIndex++
+       return tradePair
+ }
+ // OrderIterator wrap read order from DB action
+ type OrderIterator struct {
+       movStore  MovStore
+       lastOrder *common.Order
+       orders    []*common.Order
+ }
+ // NewOrderIterator create the new OrderIterator object
+ func NewOrderIterator(movStore MovStore, tradePair *common.TradePair) *OrderIterator {
+       return &OrderIterator{
+               movStore:  movStore,
+               lastOrder: &common.Order{FromAssetID: tradePair.FromAssetID, ToAssetID: tradePair.ToAssetID},
+       }
+ }
+ // HasNext check if there are more orders in memory or DB
+ func (o *OrderIterator) HasNext() bool {
+       if len(o.orders) == 0 {
+               orders, err := o.movStore.ListOrders(o.lastOrder)
+               if err != nil {
+                       log.WithField("err", err).Fatal("fail to list orders")
+               }
+               if len(orders) == 0 {
+                       return false
+               }
+               o.orders = orders
+               o.lastOrder = o.orders[len(o.orders)-1]
+       }
+       return true
+ }
+ // NextBatch return the next batch of orders in memory or DB
+ func (o *OrderIterator) NextBatch() []*common.Order {
+       if !o.HasNext() {
+               return nil
+       }
+       orders := o.orders
+       o.orders = nil
+       return orders
+ }
index 0000000,be6c110..82f57dd
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,166 +1,166 @@@
 -      "github.com/vapor/application/mov/common"
 -      "github.com/vapor/application/mov/mock"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/testutil"
+ package database
+ import (
+       "testing"
++      "github.com/bytom/vapor/application/mov/common"
++      "github.com/bytom/vapor/application/mov/mock"
++      "github.com/bytom/vapor/protocol/bc"
++      "github.com/bytom/vapor/testutil"
+ )
+ var (
+       asset1 = bc.NewAssetID([32]byte{1})
+       asset2 = bc.NewAssetID([32]byte{2})
+       asset3 = bc.NewAssetID([32]byte{3})
+       asset4 = bc.NewAssetID([32]byte{4})
+       order1 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, Rate: 0.1}
+       order2 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, Rate: 0.2}
+       order3 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, Rate: 0.3}
+       order4 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, Rate: 0.4}
+       order5 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, Rate: 0.5}
+ )
+ func TestTradePairIterator(t *testing.T) {
+       cases := []struct {
+               desc            string
+               storeTradePairs []*common.TradePair
+               wantTradePairs  []*common.TradePair
+       }{
+               {
+                       desc: "normal case",
+                       storeTradePairs: []*common.TradePair{
+                               {
+                                       FromAssetID: &asset1,
+                                       ToAssetID:   &asset2,
+                               },
+                               {
+                                       FromAssetID: &asset1,
+                                       ToAssetID:   &asset3,
+                               },
+                               {
+                                       FromAssetID: &asset1,
+                                       ToAssetID:   &asset4,
+                               },
+                       },
+                       wantTradePairs: []*common.TradePair{
+                               {
+                                       FromAssetID: &asset1,
+                                       ToAssetID:   &asset2,
+                               },
+                               {
+                                       FromAssetID: &asset1,
+                                       ToAssetID:   &asset3,
+                               },
+                               {
+                                       FromAssetID: &asset1,
+                                       ToAssetID:   &asset4,
+                               },
+                       },
+               },
+               {
+                       desc: "num of trade pairs more than one return",
+                       storeTradePairs: []*common.TradePair{
+                               {
+                                       FromAssetID: &asset1,
+                                       ToAssetID:   &asset2,
+                               },
+                               {
+                                       FromAssetID: &asset1,
+                                       ToAssetID:   &asset3,
+                               },
+                               {
+                                       FromAssetID: &asset1,
+                                       ToAssetID:   &asset4,
+                               },
+                               {
+                                       FromAssetID: &asset2,
+                                       ToAssetID:   &asset1,
+                               },
+                       },
+                       wantTradePairs: []*common.TradePair{
+                               {
+                                       FromAssetID: &asset1,
+                                       ToAssetID:   &asset2,
+                               },
+                               {
+                                       FromAssetID: &asset1,
+                                       ToAssetID:   &asset3,
+                               },
+                               {
+                                       FromAssetID: &asset1,
+                                       ToAssetID:   &asset4,
+                               },
+                               {
+                                       FromAssetID: &asset2,
+                                       ToAssetID:   &asset1,
+                               },
+                       },
+               },
+               {
+                       desc:            "store is empty",
+                       storeTradePairs: []*common.TradePair{},
+                       wantTradePairs:  []*common.TradePair{},
+               },
+       }
+       for i, c := range cases {
+               store := mock.NewMovStore(c.storeTradePairs, nil)
+               var gotTradePairs []*common.TradePair
+               iterator := NewTradePairIterator(store)
+               for iterator.HasNext() {
+                       gotTradePairs = append(gotTradePairs, iterator.Next())
+               }
+               if !testutil.DeepEqual(c.wantTradePairs, gotTradePairs) {
+                       t.Errorf("#%d(%s):got trade pairs is not equals want trade pairs", i, c.desc)
+               }
+       }
+ }
+ func TestOrderIterator(t *testing.T) {
+       cases := []struct {
+               desc        string
+               tradePair   *common.TradePair
+               storeOrders []*common.Order
+               wantOrders  []*common.Order
+       }{
+               {
+                       desc:        "normal case",
+                       tradePair:   &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2},
+                       storeOrders: []*common.Order{order1, order2, order3},
+                       wantOrders:  []*common.Order{order1, order2, order3},
+               },
+               {
+                       desc:        "num of orders more than one return",
+                       tradePair:   &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2},
+                       storeOrders: []*common.Order{order1, order2, order3, order4, order5},
+                       wantOrders:  []*common.Order{order1, order2, order3, order4, order5},
+               },
+               {
+                       desc:        "only one order",
+                       tradePair:   &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2},
+                       storeOrders: []*common.Order{order1},
+                       wantOrders:  []*common.Order{order1},
+               },
+               {
+                       desc:        "store is empty",
+                       tradePair:   &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2},
+                       storeOrders: []*common.Order{},
+                       wantOrders:  []*common.Order{},
+               },
+       }
+       for i, c := range cases {
+               store := mock.NewMovStore(nil, c.storeOrders)
+               var gotOrders []*common.Order
+               iterator := NewOrderIterator(store, c.tradePair)
+               for iterator.HasNext() {
+                       gotOrders = append(gotOrders, iterator.NextBatch()...)
+               }
+               if !testutil.DeepEqual(c.wantOrders, gotOrders) {
+                       t.Errorf("#%d(%s):got orders it not equals want orders", i, c.desc)
+               }
+       }
+ }
@@@ -6,14 -6,23 +6,23 @@@ import 
        "errors"
        "math"
  
 -      "github.com/vapor/application/mov/common"
 -      dbm "github.com/vapor/database/leveldb"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/bc/types"
 +      "github.com/bytom/vapor/application/mov/common"
 +      dbm "github.com/bytom/vapor/database/leveldb"
 +      "github.com/bytom/vapor/protocol/bc"
 +      "github.com/bytom/vapor/protocol/bc/types"
  )
  
+ // MovStore is the interface for mov's persistent storage
+ type MovStore interface {
+       GetMovDatabaseState() (*common.MovDatabaseState, error)
+       InitDBState(height uint64, hash *bc.Hash) error
+       ListOrders(orderAfter *common.Order) ([]*common.Order, error)
+       ListTradePairsWithStart(fromAssetIDAfter, toAssetIDAfter *bc.AssetID) ([]*common.TradePair, error)
+       ProcessOrders(addOrders []*common.Order, delOrders []*common.Order, blockHeader *types.BlockHeader) error
+ }
  const (
-       order byte = iota
+       order byte = iota + 1
        tradePair
        matchStatus
  
@@@ -9,14 -9,12 +9,12 @@@ import 
  
        "github.com/stretchr/testify/require"
  
 -      "github.com/vapor/application/mov/common"
 -      "github.com/vapor/database/leveldb"
 -      dbm "github.com/vapor/database/leveldb"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/bc/types"
 -      "github.com/vapor/testutil"
 +      "github.com/bytom/vapor/application/mov/common"
-       "github.com/bytom/vapor/consensus"
 +      "github.com/bytom/vapor/database/leveldb"
 +      dbm "github.com/bytom/vapor/database/leveldb"
-       chainjson "github.com/bytom/vapor/encoding/json"
 +      "github.com/bytom/vapor/protocol/bc"
 +      "github.com/bytom/vapor/protocol/bc/types"
 +      "github.com/bytom/vapor/testutil"
  )
  
  var (
index 0000000,8feab6a..db5a2c5
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,281 +1,281 @@@
 -      "github.com/vapor/application/mov/common"
 -      "github.com/vapor/application/mov/contract"
 -      "github.com/vapor/consensus/segwit"
 -      "github.com/vapor/errors"
 -      vprMath "github.com/vapor/math"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/bc/types"
 -      "github.com/vapor/protocol/vm"
 -      "github.com/vapor/protocol/vm/vmutil"
+ package match
+ import (
+       "encoding/hex"
+       "math"
+       "math/big"
++      "github.com/bytom/vapor/application/mov/common"
++      "github.com/bytom/vapor/application/mov/contract"
++      "github.com/bytom/vapor/consensus/segwit"
++      "github.com/bytom/vapor/errors"
++      vprMath "github.com/bytom/vapor/math"
++      "github.com/bytom/vapor/protocol/bc"
++      "github.com/bytom/vapor/protocol/bc/types"
++      "github.com/bytom/vapor/protocol/vm"
++      "github.com/bytom/vapor/protocol/vm/vmutil"
+ )
+ // Engine is used to generate math transactions
+ type Engine struct {
+       orderTable  *OrderTable
+       maxFeeRate  float64
+       nodeProgram []byte
+ }
+ // NewEngine return a new Engine
+ func NewEngine(orderTable *OrderTable, maxFeeRate float64, nodeProgram []byte) *Engine {
+       return &Engine{orderTable: orderTable, maxFeeRate: maxFeeRate, nodeProgram: nodeProgram}
+ }
+ // HasMatchedTx check does the input trade pair can generate a match deal
+ func (e *Engine) HasMatchedTx(tradePairs ...*common.TradePair) bool {
+       if err := validateTradePairs(tradePairs); err != nil {
+               return false
+       }
+       orders := e.peekOrders(tradePairs)
+       if len(orders) == 0 {
+               return false
+       }
+       return isMatched(orders)
+ }
+ // NextMatchedTx return the next matchable transaction by the specified trade pairs
+ // the size of trade pairs at least 2, and the sequence of trade pairs can form a loop
+ // for example, [assetA -> assetB, assetB -> assetC, assetC -> assetA]
+ func (e *Engine) NextMatchedTx(tradePairs ...*common.TradePair) (*types.Tx, error) {
+       if !e.HasMatchedTx(tradePairs...) {
+               return nil, errors.New("the specified trade pairs can not be matched")
+       }
+       tx, err := e.buildMatchTx(e.peekOrders(tradePairs))
+       if err != nil {
+               return nil, err
+       }
+       for _, tradePair := range tradePairs {
+               e.orderTable.PopOrder(tradePair)
+       }
+       if err := e.addPartialTradeOrder(tx); err != nil {
+               return nil, err
+       }
+       return tx, nil
+ }
+ func (e *Engine) addMatchTxFeeOutput(txData *types.TxData) error {
+       txFee, err := CalcMatchedTxFee(txData, e.maxFeeRate)
+       if err != nil {
+               return err
+       }
+       for assetID, matchTxFee := range txFee {
+               feeAmount, reminder := matchTxFee.FeeAmount, int64(0)
+               if matchTxFee.FeeAmount > matchTxFee.MaxFeeAmount {
+                       feeAmount = matchTxFee.MaxFeeAmount
+                       reminder = matchTxFee.FeeAmount - matchTxFee.MaxFeeAmount
+               }
+               txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(assetID, uint64(feeAmount), e.nodeProgram))
+               // There is the remaining amount after paying the handling fee, assign it evenly to participants in the transaction
+               averageAmount := reminder / int64(len(txData.Inputs))
+               if averageAmount == 0 {
+                       averageAmount = 1
+               }
+               for i := 0; i < len(txData.Inputs) && reminder > 0; i++ {
+                       contractArgs, err := segwit.DecodeP2WMCProgram(txData.Inputs[i].ControlProgram())
+                       if err != nil {
+                               return err
+                       }
+                       if i == len(txData.Inputs)-1 {
+                               txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(assetID, uint64(reminder), contractArgs.SellerProgram))
+                       } else {
+                               txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(assetID, uint64(averageAmount), contractArgs.SellerProgram))
+                       }
+                       reminder -= averageAmount
+               }
+       }
+       return nil
+ }
+ func (e *Engine) addPartialTradeOrder(tx *types.Tx) error {
+       for i, output := range tx.Outputs {
+               if !segwit.IsP2WMCScript(output.ControlProgram()) {
+                       continue
+               }
+               order, err := common.NewOrderFromOutput(tx, i)
+               if err != nil {
+                       return err
+               }
+               if err := e.orderTable.AddOrder(order); err != nil {
+                       return err
+               }
+       }
+       return nil
+ }
+ func (e *Engine) buildMatchTx(orders []*common.Order) (*types.Tx, error) {
+       txData := &types.TxData{Version: 1}
+       for i, 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)
+               oppositeOrder := orders[calcOppositeIndex(len(orders), i)]
+               if err := addMatchTxOutput(txData, input, order, oppositeOrder.Utxo.Amount); err != nil {
+                       return nil, err
+               }
+       }
+       if err := e.addMatchTxFeeOutput(txData); err != nil {
+               return nil, err
+       }
+       byteData, err := txData.MarshalText()
+       if err != nil {
+               return nil, err
+       }
+       txData.SerializedSize = uint64(len(byteData))
+       return types.NewTx(*txData), nil
+ }
+ func (e *Engine) peekOrders(tradePairs []*common.TradePair) []*common.Order {
+       var orders []*common.Order
+       for _, tradePair := range tradePairs {
+               order := e.orderTable.PeekOrder(tradePair)
+               if order == nil {
+                       return nil
+               }
+               orders = append(orders, order)
+       }
+       return orders
+ }
+ // MatchedTxFee is object to record the mov tx's fee information
+ type MatchedTxFee struct {
+       MaxFeeAmount int64
+       FeeAmount    int64
+ }
+ // CalcMatchedTxFee is used to calculate tx's MatchedTxFees
+ func CalcMatchedTxFee(txData *types.TxData, maxFeeRate float64) (map[bc.AssetID]*MatchedTxFee, error) {
+       assetFeeMap := make(map[bc.AssetID]*MatchedTxFee)
+       dealProgMaps := make(map[string]bool)
+       for _, input := range txData.Inputs {
+               assetFeeMap[input.AssetID()] = &MatchedTxFee{FeeAmount: int64(input.AssetAmount().Amount)}
+               contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram())
+               if err != nil {
+                       return nil, err
+               }
+               dealProgMaps[hex.EncodeToString(contractArgs.SellerProgram)] = true
+       }
+       for _, input := range txData.Inputs {
+               contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram())
+               if err != nil {
+                       return nil, err
+               }
+               oppositeAmount := uint64(assetFeeMap[contractArgs.RequestedAsset].FeeAmount)
+               receiveAmount := vprMath.MinUint64(CalcRequestAmount(input.Amount(), contractArgs), oppositeAmount)
+               assetFeeMap[input.AssetID()].MaxFeeAmount = calcMaxFeeAmount(calcShouldPayAmount(receiveAmount, contractArgs), maxFeeRate)
+       }
+       for _, output := range txData.Outputs {
+               assetAmount := output.AssetAmount()
+               if _, ok := dealProgMaps[hex.EncodeToString(output.ControlProgram())]; ok || segwit.IsP2WMCScript(output.ControlProgram()) {
+                       assetFeeMap[*assetAmount.AssetId].FeeAmount -= int64(assetAmount.Amount)
+                       if assetFeeMap[*assetAmount.AssetId].FeeAmount <= 0 {
+                               delete(assetFeeMap, *assetAmount.AssetId)
+                       }
+               }
+       }
+       return assetFeeMap, nil
+ }
+ func addMatchTxOutput(txData *types.TxData, txInput *types.TxInput, order *common.Order, oppositeAmount uint64) error {
+       contractArgs, err := segwit.DecodeP2WMCProgram(order.Utxo.ControlProgram)
+       if err != nil {
+               return err
+       }
+       requestAmount := CalcRequestAmount(order.Utxo.Amount, contractArgs)
+       receiveAmount := vprMath.MinUint64(requestAmount, oppositeAmount)
+       shouldPayAmount := calcShouldPayAmount(receiveAmount, contractArgs)
+       isPartialTrade := requestAmount > receiveAmount
+       setMatchTxArguments(txInput, isPartialTrade, len(txData.Outputs), receiveAmount)
+       txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, receiveAmount, contractArgs.SellerProgram))
+       if isPartialTrade {
+               txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.FromAssetID, order.Utxo.Amount-shouldPayAmount, order.Utxo.ControlProgram))
+       }
+       return nil
+ }
+ func CalcRequestAmount(fromAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 {
+       res := big.NewInt(0).SetUint64(fromAmount)
+       res.Mul(res, big.NewInt(contractArg.RatioNumerator)).Quo(res, big.NewInt(contractArg.RatioDenominator))
+       if !res.IsUint64() {
+               return 0
+       }
+       return res.Uint64()
+ }
+ func calcShouldPayAmount(receiveAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 {
+       res := big.NewInt(0).SetUint64(receiveAmount)
+       res.Mul(res, big.NewInt(contractArg.RatioDenominator)).Quo(res, big.NewInt(contractArg.RatioNumerator))
+       if !res.IsUint64() {
+               return 0
+       }
+       return res.Uint64()
+ }
+ func calcMaxFeeAmount(shouldPayAmount uint64, maxFeeRate float64) int64 {
+       return int64(math.Ceil(float64(shouldPayAmount) * maxFeeRate))
+ }
+ func calcOppositeIndex(size int, selfIdx int) int {
+       return (selfIdx + 1) % size
+ }
+ func isMatched(orders []*common.Order) bool {
+       for i, order := range orders {
+               if oppositeOrder := orders[calcOppositeIndex(len(orders), i)]; 1/order.Rate < oppositeOrder.Rate {
+                       return false
+               }
+       }
+       return true
+ }
+ func setMatchTxArguments(txInput *types.TxInput, isPartialTrade bool, position int, receiveAmounts uint64) {
+       var arguments [][]byte
+       if isPartialTrade {
+               arguments = [][]byte{vm.Int64Bytes(int64(receiveAmounts)), vm.Int64Bytes(int64(position)), vm.Int64Bytes(contract.PartialTradeClauseSelector)}
+       } else {
+               arguments = [][]byte{vm.Int64Bytes(int64(position)), vm.Int64Bytes(contract.FullTradeClauseSelector)}
+       }
+       txInput.SetArguments(arguments)
+ }
+ func validateTradePairs(tradePairs []*common.TradePair) error {
+       if len(tradePairs) < 2 {
+               return errors.New("size of trade pairs at least 2")
+       }
+       for i, tradePair := range tradePairs {
+               oppositeTradePair := tradePairs[calcOppositeIndex(len(tradePairs), i)]
+               if *tradePair.ToAssetID != *oppositeTradePair.FromAssetID {
+                       return errors.New("specified trade pairs is invalid")
+               }
+       }
+       return nil
+ }
index 0000000,05e42fb..880ab4f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,143 +1,142 @@@
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/testutil"
 -
 -      "github.com/vapor/application/mov/common"
 -      "github.com/vapor/application/mov/mock"
 -      "github.com/vapor/protocol/bc/types"
+ package match
+ import (
+       "testing"
++      "github.com/bytom/vapor/protocol/bc"
++      "github.com/bytom/vapor/testutil"
++      "github.com/bytom/vapor/application/mov/common"
++      "github.com/bytom/vapor/application/mov/mock"
++      "github.com/bytom/vapor/protocol/bc/types"
+ )
+ /*
+       Test: validateTradePairs vaild and invaild case for 2, 3 trade pairs
+ */
+ func TestGenerateMatchedTxs(t *testing.T) {
+       btc2eth := &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH}
+       eth2btc := &common.TradePair{FromAssetID: &mock.ETH, ToAssetID: &mock.BTC}
+       cases := []struct {
+               desc            string
+               tradePair       *common.TradePair
+               initStoreOrders []*common.Order
+               wantMatchedTxs  []*types.Tx
+       }{
+               {
+                       desc:      "full matched",
+                       tradePair: &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH},
+                       initStoreOrders: []*common.Order{
+                               mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
+                               mock.Eth2BtcOrders[0],
+                       },
+                       wantMatchedTxs: []*types.Tx{
+                               mock.MatchedTxs[1],
+                       },
+               },
+               {
+                       desc:      "partial matched",
+                       tradePair: &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH},
+                       initStoreOrders: []*common.Order{
+                               mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
+                               mock.Eth2BtcOrders[1],
+                       },
+                       wantMatchedTxs: []*types.Tx{
+                               mock.MatchedTxs[0],
+                       },
+               },
+               {
+                       desc:      "partial matched and continue to match",
+                       tradePair: &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH},
+                       initStoreOrders: []*common.Order{
+                               mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
+                               mock.Eth2BtcOrders[2],
+                       },
+                       wantMatchedTxs: []*types.Tx{
+                               mock.MatchedTxs[2],
+                               mock.MatchedTxs[3],
+                       },
+               },
+               {
+                       desc:      "unable to match",
+                       tradePair: &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH},
+                       initStoreOrders: []*common.Order{
+                               mock.Btc2EthOrders[1],
+                               mock.Eth2BtcOrders[0],
+                       },
+                       wantMatchedTxs: []*types.Tx{},
+               },
+       }
+       for i, c := range cases {
+               movStore := mock.NewMovStore([]*common.TradePair{btc2eth, eth2btc}, c.initStoreOrders)
+               matchEngine := NewEngine(NewOrderTable(movStore, nil, nil), 0.05, mock.NodeProgram)
+               var gotMatchedTxs []*types.Tx
+               for matchEngine.HasMatchedTx(c.tradePair, c.tradePair.Reverse()) {
+                       matchedTx, err := matchEngine.NextMatchedTx(c.tradePair, c.tradePair.Reverse())
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       gotMatchedTxs = append(gotMatchedTxs, matchedTx)
+               }
+               if len(c.wantMatchedTxs) != len(gotMatchedTxs) {
+                       t.Errorf("#%d(%s) the length of got matched tx is not equals want matched tx", i, c.desc)
+                       continue
+               }
+               for i, gotMatchedTx := range gotMatchedTxs {
+                       c.wantMatchedTxs[i].Version = 1
+                       byteData, err := c.wantMatchedTxs[i].MarshalText()
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       c.wantMatchedTxs[i].SerializedSize = uint64(len(byteData))
+                       wantMatchedTx := types.NewTx(c.wantMatchedTxs[i].TxData)
+                       if gotMatchedTx.ID != wantMatchedTx.ID {
+                               t.Errorf("#%d(%s) the tx hash of got matched tx: %s is not equals want matched tx: %s", i, c.desc, gotMatchedTx.ID.String(), wantMatchedTx.ID.String())
+                       }
+               }
+       }
+ }
+ 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,
+                       wantMatchedTxFee: map[bc.AssetID]*MatchedTxFee{mock.ETH: {FeeAmount: 10, MaxFeeAmount: 26}},
+                       tx:               &mock.MatchedTxs[1].TxData,
+               },
+               {
+                       desc:             "fee refund in tx",
+                       maxFeeRate:       0.05,
+                       wantMatchedTxFee: map[bc.AssetID]*MatchedTxFee{mock.ETH: {FeeAmount: 27, MaxFeeAmount: 27}},
+                       tx:               &mock.MatchedTxs[2].TxData,
+               },
+               {
+                       desc:             "fee is zero",
+                       maxFeeRate:       0.05,
+                       wantMatchedTxFee: map[bc.AssetID]*MatchedTxFee{},
+                       tx:               &mock.MatchedTxs[0].TxData,
+               },
+       }
+       for i, c := range cases {
+               gotMatchedTxFee, err := CalcMatchedTxFee(c.tx, c.maxFeeRate)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if !testutil.DeepEqual(gotMatchedTxFee, c.wantMatchedTxFee) {
+                       t.Errorf("#%d(%s):fail to caculate matched tx fee, got (%v), want (%v)", i, c.desc, gotMatchedTxFee, c.wantMatchedTxFee)
+               }
+       }
+ }
index 0000000,7e6043f..30b6056
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,129 +1,129 @@@
 -      "github.com/vapor/application/mov/common"
 -      "github.com/vapor/application/mov/database"
 -      "github.com/vapor/errors"
+ package match
+ import (
+       "sort"
++      "github.com/bytom/vapor/application/mov/common"
++      "github.com/bytom/vapor/application/mov/database"
++      "github.com/bytom/vapor/errors"
+ )
+ // OrderTable is used to handle the mov orders in memory like stack
+ type OrderTable struct {
+       movStore database.MovStore
+       // key of tradePair -> []order
+       dbOrders map[string][]*common.Order
+       // key of tradePair -> iterator
+       orderIterators map[string]*database.OrderIterator
+       // key of tradePair -> []order
+       arrivalAddOrders map[string][]*common.Order
+       // key of order -> order
+       arrivalDelOrders map[string]*common.Order
+ }
+ // NewOrderTable create a new OrderTable object
+ func NewOrderTable(movStore database.MovStore, arrivalAddOrders, arrivalDelOrders []*common.Order) *OrderTable {
+       return &OrderTable{
+               movStore:       movStore,
+               dbOrders:       make(map[string][]*common.Order),
+               orderIterators: make(map[string]*database.OrderIterator),
+               arrivalAddOrders: arrangeArrivalAddOrders(arrivalAddOrders),
+               arrivalDelOrders: arrangeArrivalDelOrders(arrivalDelOrders),
+       }
+ }
+ // AddOrder add the in memory temp order to order table
+ func (o *OrderTable) AddOrder(order *common.Order) error {
+       tradePairKey := order.TradePair().Key()
+       orders := o.arrivalAddOrders[tradePairKey]
+       if len(orders) > 0 && order.Rate > orders[len(orders)-1].Rate {
+               return errors.New("rate of order must less than the min order in order table")
+       }
+       o.arrivalAddOrders[tradePairKey] = append(orders, order)
+       return nil
+ }
+ // PeekOrder return the next lowest order of given trade pair
+ func (o *OrderTable) PeekOrder(tradePair *common.TradePair) *common.Order {
+       if len(o.dbOrders[tradePair.Key()]) == 0 {
+               o.extendDBOrders(tradePair)
+       }
+       var nextOrder *common.Order
+       orders := o.dbOrders[tradePair.Key()]
+       if len(orders) != 0 {
+               nextOrder = orders[len(orders)-1]
+       }
+       if nextOrder != nil && o.arrivalDelOrders[nextOrder.Key()] != nil {
+               o.dbOrders[tradePair.Key()] = orders[0 : len(orders)-1]
+               return o.PeekOrder(tradePair)
+       }
+       arrivalOrder := o.peekArrivalOrder(tradePair)
+       if nextOrder == nil || (arrivalOrder != nil && arrivalOrder.Rate < nextOrder.Rate) {
+               nextOrder = arrivalOrder
+       }
+       return nextOrder
+ }
+ // PopOrder delete the next lowest order of given trade pair
+ func (o *OrderTable) PopOrder(tradePair *common.TradePair) {
+       order := o.PeekOrder(tradePair)
+       if order == nil {
+               return
+       }
+       orders := o.dbOrders[tradePair.Key()]
+       if len(orders) != 0 && orders[len(orders)-1].Key() == order.Key() {
+               o.dbOrders[tradePair.Key()] = orders[0 : len(orders)-1]
+       }
+       arrivalOrders := o.arrivalAddOrders[tradePair.Key()]
+       if len(arrivalOrders) != 0 && arrivalOrders[len(arrivalOrders)-1].Key() == order.Key() {
+               o.arrivalAddOrders[tradePair.Key()] = arrivalOrders[0 : len(arrivalOrders)-1]
+       }
+ }
+ func arrangeArrivalAddOrders(orders []*common.Order) map[string][]*common.Order {
+       arrivalAddOrderMap := make(map[string][]*common.Order)
+       for _, order := range orders {
+               arrivalAddOrderMap[order.TradePair().Key()] = append(arrivalAddOrderMap[order.TradePair().Key()], order)
+       }
+       for _, orders := range arrivalAddOrderMap {
+               sort.Sort(sort.Reverse(common.OrderSlice(orders)))
+       }
+       return arrivalAddOrderMap
+ }
+ func arrangeArrivalDelOrders(orders []*common.Order) map[string]*common.Order {
+       arrivalDelOrderMap := make(map[string]*common.Order)
+       for _, order := range orders {
+               arrivalDelOrderMap[order.Key()] = order
+       }
+       return arrivalDelOrderMap
+ }
+ func (o *OrderTable) extendDBOrders(tradePair *common.TradePair) {
+       iterator, ok := o.orderIterators[tradePair.Key()]
+       if !ok {
+               iterator = database.NewOrderIterator(o.movStore, tradePair)
+               o.orderIterators[tradePair.Key()] = iterator
+       }
+       nextOrders := iterator.NextBatch()
+       for i := len(nextOrders) - 1; i >= 0; i-- {
+               o.dbOrders[tradePair.Key()] = append(o.dbOrders[tradePair.Key()], nextOrders[i])
+       }
+ }
+ func (o *OrderTable) peekArrivalOrder(tradePair *common.TradePair) *common.Order {
+       if arrivalAddOrders := o.arrivalAddOrders[tradePair.Key()]; len(arrivalAddOrders) > 0 {
+               return arrivalAddOrders[len(arrivalAddOrders)-1]
+       }
+       return nil
+ }
index 0000000,9f9ca17..2f9582a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,195 +1,195 @@@
 -      "github.com/vapor/application/mov/common"
 -      "github.com/vapor/application/mov/database"
 -      "github.com/vapor/application/mov/mock"
+ package match
+ import (
+       "testing"
++      "github.com/bytom/vapor/application/mov/common"
++      "github.com/bytom/vapor/application/mov/database"
++      "github.com/bytom/vapor/application/mov/mock"
+ )
+ var (
+       btc2eth = &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH}
+ )
+ func TestOrderTable(t *testing.T) {
+       cases := []struct {
+               desc                 string
+               initMovStore         database.MovStore
+               initArrivalAddOrders []*common.Order
+               initArrivalDelOrders []*common.Order
+               addOrders            []*common.Order
+               popOrders            []*common.TradePair
+               wantPeekedOrders     map[common.TradePair]*common.Order
+       }{
+               {
+                       desc: "no arrival orders, no add order, no pop order",
+                       initMovStore: mock.NewMovStore(
+                               []*common.TradePair{btc2eth},
+                               []*common.Order{
+                                       mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+                               },
+                       ),
+                       wantPeekedOrders: map[common.TradePair]*common.Order{
+                               *btc2eth: mock.Btc2EthOrders[0],
+                       },
+               },
+               {
+                       desc: "no arrival orders, add lower price order, no pop order",
+                       initMovStore: mock.NewMovStore(
+                               []*common.TradePair{btc2eth},
+                               []*common.Order{
+                                       mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+                               }),
+                       addOrders: []*common.Order{mock.Btc2EthOrders[3]},
+                       wantPeekedOrders: map[common.TradePair]*common.Order{
+                               *btc2eth: mock.Btc2EthOrders[3],
+                       },
+               },
+               {
+                       desc: "no arrival orders, no add order, pop one order",
+                       initMovStore: mock.NewMovStore(
+                               []*common.TradePair{btc2eth},
+                               []*common.Order{
+                                       mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+                               }),
+                       popOrders: []*common.TradePair{btc2eth},
+                       wantPeekedOrders: map[common.TradePair]*common.Order{
+                               *btc2eth: mock.Btc2EthOrders[2],
+                       },
+               },
+               {
+                       desc: "has arrival add orders, no add order, no pop order, the arrival add order is lower price",
+                       initMovStore: mock.NewMovStore(
+                               []*common.TradePair{btc2eth},
+                               []*common.Order{
+                                       mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+                               }),
+                       initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[3]},
+                       wantPeekedOrders: map[common.TradePair]*common.Order{
+                               *btc2eth: mock.Btc2EthOrders[3],
+                       },
+               },
+               {
+                       desc: "has arrival add orders, no add order, no pop order, the db add order is lower price",
+                       initMovStore: mock.NewMovStore(
+                               []*common.TradePair{btc2eth},
+                               []*common.Order{
+                                       mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
+                               }),
+                       initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[2]},
+                       wantPeekedOrders: map[common.TradePair]*common.Order{
+                               *btc2eth: mock.Btc2EthOrders[0],
+                       },
+               },
+               {
+                       desc: "has arrival add orders, no add order, pop one order, after pop the arrival order is lower price",
+                       initMovStore: mock.NewMovStore(
+                               []*common.TradePair{btc2eth},
+                               []*common.Order{
+                                       mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+                               }),
+                       initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
+                       popOrders:            []*common.TradePair{btc2eth},
+                       wantPeekedOrders: map[common.TradePair]*common.Order{
+                               *btc2eth: mock.Btc2EthOrders[0],
+                       },
+               },
+               {
+                       desc: "has arrival delete orders, no add order, no pop order",
+                       initMovStore: mock.NewMovStore(
+                               []*common.TradePair{btc2eth},
+                               []*common.Order{
+                                       mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+                               }),
+                       initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3]},
+                       wantPeekedOrders: map[common.TradePair]*common.Order{
+                               *btc2eth: mock.Btc2EthOrders[2],
+                       },
+               },
+               {
+                       desc: "has arrival delete orders and arrival add orders, no add order, no pop order, the arrival order is lower price",
+                       initMovStore: mock.NewMovStore(
+                               []*common.TradePair{btc2eth},
+                               []*common.Order{
+                                       mock.Btc2EthOrders[3], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+                               }),
+                       initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
+                       initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3]},
+                       wantPeekedOrders: map[common.TradePair]*common.Order{
+                               *btc2eth: mock.Btc2EthOrders[0],
+                       },
+               },
+               {
+                       desc: "has arrival delete orders and arrival add orders, no add order, pop one order",
+                       initMovStore: mock.NewMovStore(
+                               []*common.TradePair{btc2eth},
+                               []*common.Order{
+                                       mock.Btc2EthOrders[3], mock.Btc2EthOrders[1],
+                               }),
+                       initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[2]},
+                       initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3]},
+                       popOrders:            []*common.TradePair{btc2eth},
+                       wantPeekedOrders: map[common.TradePair]*common.Order{
+                               *btc2eth: mock.Btc2EthOrders[2],
+                       },
+               },
+               {
+                       desc: "has arrival add orders, but db order is empty",
+                       initMovStore: mock.NewMovStore(
+                               []*common.TradePair{btc2eth},
+                               []*common.Order{}),
+                       initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[2]},
+                       wantPeekedOrders: map[common.TradePair]*common.Order{
+                               *btc2eth: mock.Btc2EthOrders[0],
+                       },
+               },
+               {
+                       desc: "no add orders, and db order is empty",
+                       initMovStore: mock.NewMovStore(
+                               []*common.TradePair{btc2eth},
+                               []*common.Order{}),
+                       initArrivalAddOrders: []*common.Order{},
+                       wantPeekedOrders: map[common.TradePair]*common.Order{
+                               *btc2eth: nil,
+                       },
+               },
+               {
+                       desc: "has arrival delete orders, no add order, no pop order, need recursive to peek one order",
+                       initMovStore: mock.NewMovStore(
+                               []*common.TradePair{btc2eth},
+                               []*common.Order{
+                                       mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+                               }),
+                       initArrivalAddOrders: []*common.Order{},
+                       initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3], mock.Btc2EthOrders[0], mock.Btc2EthOrders[2]},
+                       wantPeekedOrders: map[common.TradePair]*common.Order{
+                               *btc2eth: mock.Btc2EthOrders[1],
+                       },
+               },
+       }
+       for i, c := range cases {
+               orderTable := NewOrderTable(c.initMovStore, c.initArrivalAddOrders, c.initArrivalDelOrders)
+               for _, order := range c.addOrders {
+                       if err := orderTable.AddOrder(order); err != nil {
+                               t.Fatal(err)
+                       }
+               }
+               for _, tradePair := range c.popOrders {
+                       orderTable.PopOrder(tradePair)
+               }
+               for tradePair, wantOrder := range c.wantPeekedOrders {
+                       gotOrder := orderTable.PeekOrder(&tradePair)
+                       if wantOrder == gotOrder && wantOrder == nil {
+                               continue
+                       }
+                       if gotOrder.Key() != wantOrder.Key() {
+                               t.Errorf("#%d(%s):the key of got order(%v) is not equals key of want order(%v)", i, c.desc, gotOrder, wantOrder)
+                       }
+               }
+       }
+ }
index 0000000,33f41b8..8a918a8
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,242 +1,242 @@@
 -      "github.com/vapor/application/mov/common"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/bc/types"
 -      "github.com/vapor/protocol/vm"
 -      "github.com/vapor/protocol/vm/vmutil"
 -      "github.com/vapor/testutil"
+ package mock
+ import (
++      "github.com/bytom/vapor/application/mov/common"
++      "github.com/bytom/vapor/protocol/bc"
++      "github.com/bytom/vapor/protocol/bc/types"
++      "github.com/bytom/vapor/protocol/vm"
++      "github.com/bytom/vapor/protocol/vm/vmutil"
++      "github.com/bytom/vapor/testutil"
+ )
+ var (
+       BTC         = bc.NewAssetID([32]byte{1})
+       ETH         = bc.NewAssetID([32]byte{2})
+       NodeProgram = []byte{0x58}
+       Btc2EthOrders = []*common.Order{
+               {
+                       FromAssetID: &BTC,
+                       ToAssetID:   &ETH,
+                       Rate:        50,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("37b8edf656e45a7addf47f5626e114a8c394d918a36f61b5a2905675a09b40ae")),
+                               SourcePos:      0,
+                               Amount:         10,
+                               ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"), 50, 1),
+                       },
+               },
+               {
+                       FromAssetID: &BTC,
+                       ToAssetID:   &ETH,
+                       Rate:        53,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("3ec2bbfb499a8736d377b547eee5392bcddf7ec2b287e9ed20b5938c3d84e7cd")),
+                               SourcePos:      0,
+                               Amount:         20,
+                               ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252"), 53, 1),
+                       },
+               },
+               {
+                       FromAssetID: &BTC,
+                       ToAssetID:   &ETH,
+                       Rate:        52,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("1232bbfb499a8736d377b547eee5392bcddf7ec2b287e9ed20b5938c3d84e7cd")),
+                               SourcePos:      0,
+                               Amount:         15,
+                               ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252"), 53, 1),
+                       },
+               },
+               {
+                       FromAssetID: &BTC,
+                       ToAssetID:   &ETH,
+                       Rate:        49,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("7872bbfb499a8736d377b547eee5392bcddf7ec2b287e9ed20b5938c3d84e7cd")),
+                               SourcePos:      0,
+                               Amount:         17,
+                               ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252"), 53, 1),
+                       },
+               },
+       }
+       Eth2BtcOrders = []*common.Order{
+               {
+                       FromAssetID: &ETH,
+                       ToAssetID:   &BTC,
+                       Rate:        1 / 51.0,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("fba43ff5155209cb1769e2ec0e1d4a33accf899c740865edfc6d1de39b873b29")),
+                               SourcePos:      0,
+                               Amount:         510,
+                               ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253"), 1, 51.0),
+                       },
+               },
+               {
+                       FromAssetID: &ETH,
+                       ToAssetID:   &BTC,
+                       Rate:        1 / 52.0,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("05f24bb847db823075d81786aa270748e02602199cd009c0284f928503846a5a")),
+                               SourcePos:      0,
+                               Amount:         416,
+                               ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254"), 1, 52.0),
+                       },
+               },
+               {
+                       FromAssetID: &ETH,
+                       ToAssetID:   &BTC,
+                       Rate:        1 / 54.0,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("119a02980796dc352cf6475457463aef5666f66622088de3551fa73a65f0d201")),
+                               SourcePos:      0,
+                               Amount:         810,
+                               ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255"), 1, 54.0),
+                       },
+               },
+       }
+       Btc2EthMakerTxs = []*types.Tx{
+               // Btc2EthOrders[0]
+               types.NewTx(types.TxData{
+                       Inputs:  []*types.TxInput{types.NewSpendInput(nil, *Btc2EthOrders[0].Utxo.SourceID, *Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, Btc2EthOrders[0].Utxo.SourcePos, []byte{0x51})},
+                       Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, Btc2EthOrders[0].Utxo.ControlProgram)},
+               }),
+               // Btc2EthOrders[1]
+               types.NewTx(types.TxData{
+                       Inputs:  []*types.TxInput{types.NewSpendInput(nil, *Btc2EthOrders[1].Utxo.SourceID, *Btc2EthOrders[1].FromAssetID, Btc2EthOrders[1].Utxo.Amount, Btc2EthOrders[1].Utxo.SourcePos, []byte{0x51})},
+                       Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Btc2EthOrders[1].FromAssetID, Btc2EthOrders[1].Utxo.Amount, Btc2EthOrders[1].Utxo.ControlProgram)},
+               }),
+               // Btc2EthOrders[2]
+               types.NewTx(types.TxData{
+                       Inputs:  []*types.TxInput{types.NewSpendInput(nil, *Btc2EthOrders[2].Utxo.SourceID, *Btc2EthOrders[2].FromAssetID, Btc2EthOrders[2].Utxo.Amount, Btc2EthOrders[2].Utxo.SourcePos, []byte{0x51})},
+                       Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Btc2EthOrders[2].FromAssetID, Btc2EthOrders[2].Utxo.Amount, Btc2EthOrders[2].Utxo.ControlProgram)},
+               }),
+               // Btc2EthOrders[3]
+               types.NewTx(types.TxData{
+                       Inputs:  []*types.TxInput{types.NewSpendInput(nil, *Btc2EthOrders[3].Utxo.SourceID, *Btc2EthOrders[3].FromAssetID, Btc2EthOrders[3].Utxo.Amount, Btc2EthOrders[3].Utxo.SourcePos, []byte{0x51})},
+                       Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, Btc2EthOrders[3].Utxo.Amount, Btc2EthOrders[3].Utxo.ControlProgram)},
+               }),
+       }
+       Eth2BtcMakerTxs = []*types.Tx{
+               // Eth2Btc[0]
+               types.NewTx(types.TxData{
+                       Inputs:  []*types.TxInput{types.NewSpendInput(nil, *Eth2BtcOrders[0].Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, Eth2BtcOrders[0].Utxo.SourcePos, []byte{0x51})},
+                       Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, Eth2BtcOrders[0].Utxo.ControlProgram)},
+               }),
+               // Eth2Btc[1]
+               types.NewTx(types.TxData{
+                       Inputs:  []*types.TxInput{types.NewSpendInput(nil, *Eth2BtcOrders[1].Utxo.SourceID, *Eth2BtcOrders[1].FromAssetID, Eth2BtcOrders[1].Utxo.Amount, Eth2BtcOrders[1].Utxo.SourcePos, []byte{0x51})},
+                       Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Eth2BtcOrders[1].FromAssetID, Eth2BtcOrders[1].Utxo.Amount, Eth2BtcOrders[1].Utxo.ControlProgram)},
+               }),
+               // Eth2Btc[2]
+               types.NewTx(types.TxData{
+                       Inputs:  []*types.TxInput{types.NewSpendInput(nil, *Eth2BtcOrders[2].Utxo.SourceID, *Eth2BtcOrders[2].FromAssetID, Eth2BtcOrders[2].Utxo.Amount, Eth2BtcOrders[2].Utxo.SourcePos, []byte{0x51})},
+                       Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, Eth2BtcOrders[2].Utxo.Amount, Eth2BtcOrders[2].Utxo.ControlProgram)},
+               }),
+       }
+       MatchedTxs = []*types.Tx{
+               // partial matched transaction from Btc2EthOrders[0], Eth2BtcOrders[1]
+               types.NewTx(types.TxData{
+                       Inputs: []*types.TxInput{
+                               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),
+                               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")),
+                               // re-order
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 2, Btc2EthOrders[0].Utxo.ControlProgram),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")),
+                       },
+               }),
+               // full matched transaction from Btc2EthOrders[0], Eth2BtcOrders[0]
+               types.NewTx(types.TxData{
+                       Inputs: []*types.TxInput{
+                               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),
+                               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, NodeProgram),
+                       },
+               }),
+               // partial matched transaction from Btc2EthOrders[0], Eth2BtcOrders[2]
+               types.NewTx(types.TxData{
+                       Inputs: []*types.TxInput{
+                               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),
+                               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")),
+                               // re-order
+                               types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 270, Eth2BtcOrders[2].Utxo.ControlProgram),
+                               // fee
+                               types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 27, NodeProgram),
+                               // refund
+                               types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 6, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 7, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+                       },
+               }),
+               types.NewTx(types.TxData{
+                       Inputs: []*types.TxInput{
+                               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),
+                               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")),
+                               // re-order
+                               types.NewIntraChainOutput(*Btc2EthOrders[1].FromAssetID, 15, Btc2EthOrders[1].Utxo.ControlProgram),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 5, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+                       },
+               }),
+               // partial matched transaction from Btc2EthMakerTxs[0], Eth2BtcMakerTxs[1]
+               types.NewTx(types.TxData{
+                       Inputs: []*types.TxInput{
+                               types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Btc2EthMakerTxs[0], 0).Utxo.SourceID, *Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, 0, Btc2EthOrders[0].Utxo.ControlProgram),
+                               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")),
+                               // re-order
+                               types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 2, Btc2EthOrders[0].Utxo.ControlProgram),
+                               types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")),
+                       },
+               }),
+       }
+ )
+ func MustCreateP2WMCProgram(requestAsset bc.AssetID, sellerProgram []byte, ratioNumerator, ratioDenominator int64) []byte {
+       contractArgs := 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)
+       if err != nil {
+               panic(err)
+       }
+       return order
+ }
+ func hashPtr(hash bc.Hash) *bc.Hash {
+       return &hash
+ }
index 0000000,fc5c6db..a28fdf5
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,108 +1,108 @@@
 -      "github.com/vapor/application/mov/common"
 -      "github.com/vapor/errors"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/bc/types"
+ package mock
+ import (
+       "sort"
++      "github.com/bytom/vapor/application/mov/common"
++      "github.com/bytom/vapor/errors"
++      "github.com/bytom/vapor/protocol/bc"
++      "github.com/bytom/vapor/protocol/bc/types"
+ )
+ type MovStore struct {
+       tradePairs []*common.TradePair
+       orderMap   map[string][]*common.Order
+       dbState    *common.MovDatabaseState
+ }
+ func NewMovStore(tradePairs []*common.TradePair, orders []*common.Order) *MovStore {
+       orderMap := make(map[string][]*common.Order)
+       for _, order := range orders {
+               orderMap[order.TradePair().Key()] = append(orderMap[order.TradePair().Key()], order)
+       }
+       for _, orders := range orderMap {
+               sort.Sort(common.OrderSlice(orders))
+       }
+       return &MovStore{
+               tradePairs: tradePairs,
+               orderMap:   orderMap,
+       }
+ }
+ func (m *MovStore) GetMovDatabaseState() (*common.MovDatabaseState, error) {
+       return m.dbState, nil
+ }
+ func (m *MovStore) InitDBState(height uint64, hash *bc.Hash) error {
+       return nil
+ }
+ func (m *MovStore) ListOrders(orderAfter *common.Order) ([]*common.Order, error) {
+       tradePair := &common.TradePair{FromAssetID: orderAfter.FromAssetID, ToAssetID: orderAfter.ToAssetID}
+       orders := m.orderMap[tradePair.Key()]
+       begin := len(orders)
+       if orderAfter.Rate == 0 {
+               begin = 0
+       } else {
+               for i, order := range orders {
+                       if order.Rate == orderAfter.Rate {
+                               begin = i + 1
+                               break
+                       }
+               }
+       }
+       var result []*common.Order
+       for i := begin; i < len(orders) && len(result) < 3; i++ {
+               result = append(result, orders[i])
+       }
+       return result, nil
+ }
+ func (m *MovStore) ListTradePairsWithStart(fromAssetIDAfter, toAssetIDAfter *bc.AssetID) ([]*common.TradePair, error) {
+       begin := len(m.tradePairs)
+       if fromAssetIDAfter == nil || toAssetIDAfter == nil {
+               begin = 0
+       } else {
+               for i, tradePair := range m.tradePairs {
+                       if *tradePair.FromAssetID == *fromAssetIDAfter && *tradePair.ToAssetID == *toAssetIDAfter {
+                               begin = i + 1
+                               break
+                       }
+               }
+       }
+       var result []*common.TradePair
+       for i := begin; i < len(m.tradePairs) && len(result) < 3; i++ {
+               result = append(result, m.tradePairs[i])
+       }
+       return result, nil
+ }
+ func (m *MovStore) ProcessOrders(addOrders []*common.Order, delOrders []*common.Order, blockHeader *types.BlockHeader) error {
+       for _, order := range addOrders {
+               tradePair := &common.TradePair{FromAssetID: order.FromAssetID, ToAssetID: order.ToAssetID}
+               m.orderMap[tradePair.Key()] = append(m.orderMap[tradePair.Key()], order)
+       }
+       for _, delOrder := range delOrders {
+               tradePair := &common.TradePair{FromAssetID: delOrder.FromAssetID, ToAssetID: delOrder.ToAssetID}
+               orders := m.orderMap[tradePair.Key()]
+               for i, order := range orders {
+                       if delOrder.Key() == order.Key() {
+                               m.orderMap[tradePair.Key()] = append(orders[0:i], orders[i+1:]...)
+                       }
+               }
+       }
+       for _, orders := range m.orderMap {
+               sort.Sort(common.OrderSlice(orders))
+       }
+       if blockHeader.Height == m.dbState.Height {
+               m.dbState = &common.MovDatabaseState{Height: blockHeader.Height - 1, Hash: &blockHeader.PreviousBlockHash}
+       } else if blockHeader.Height == m.dbState.Height+1 {
+               blockHash := blockHeader.Hash()
+               m.dbState = &common.MovDatabaseState{Height: blockHeader.Height, Hash: &blockHash}
+       } else {
+               return errors.New("error block header")
+       }
+       return nil
+ }
index 0000000,335e8c6..0d50377
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,484 +1,484 @@@
 -      "github.com/vapor/application/mov/common"
 -      "github.com/vapor/application/mov/contract"
 -      "github.com/vapor/application/mov/database"
 -      "github.com/vapor/application/mov/match"
 -      "github.com/vapor/consensus/segwit"
 -      dbm "github.com/vapor/database/leveldb"
 -      "github.com/vapor/errors"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/bc/types"
+ package mov
+ import (
++      "github.com/bytom/vapor/application/mov/common"
++      "github.com/bytom/vapor/application/mov/contract"
++      "github.com/bytom/vapor/application/mov/database"
++      "github.com/bytom/vapor/application/mov/match"
++      "github.com/bytom/vapor/consensus/segwit"
++      dbm "github.com/bytom/vapor/database/leveldb"
++      "github.com/bytom/vapor/errors"
++      "github.com/bytom/vapor/protocol/bc"
++      "github.com/bytom/vapor/protocol/bc/types"
+ )
+ const maxFeeRate = 0.05
+ var (
+       errInvalidTradePairs             = errors.New("The trade pairs in the tx input is invalid")
+       errStatusFailMustFalse           = errors.New("status fail of transaction does not allow to be true")
+       errInputProgramMustP2WMCScript   = errors.New("input program of trade tx must p2wmc script")
+       errExistCancelOrderInMatchedTx   = errors.New("can't exist cancel order in the matched transaction")
+       errExistTradeInCancelOrderTx     = errors.New("can't exist trade in the cancel order transaction")
+       errAmountOfFeeGreaterThanMaximum = errors.New("amount of fee greater than max fee amount")
+       errAssetIDMustUniqueInMatchedTx  = errors.New("asset id must unique in matched transaction")
+       errRatioOfTradeLessThanZero      = errors.New("ratio arguments must greater than zero")
+       errLengthOfInputIsIncorrect      = errors.New("length of matched tx input is not equals to actual matched tx input")
+       errSpendOutputIDIsIncorrect      = errors.New("spend output id of matched tx is not equals to actual matched tx")
+       errRequestAmountMath             = errors.New("request amount of order less than one or big than max of int64")
+ )
+ // MovCore represent the core logic of the match module, which include generate match transactions before packing the block,
+ // verify the match transaction in block is correct, and update the order table according to the transaction.
+ type MovCore struct {
+       movStore         database.MovStore
+       startBlockHeight uint64
+ }
+ // NewMovCore return a instance of MovCore by path of mov db
+ func NewMovCore(dbBackend, dbDir string, startBlockHeight uint64) *MovCore {
+       movDB := dbm.NewDB("mov", dbBackend, dbDir)
+       return &MovCore{movStore: database.NewLevelDBMovStore(movDB), startBlockHeight: startBlockHeight}
+ }
+ // ApplyBlock parse pending order and cancel from the the transactions of block
+ // and add pending order to the dex db, remove cancel order from dex db.
+ func (m *MovCore) ApplyBlock(block *types.Block) error {
+       if block.Height < m.startBlockHeight {
+               return nil
+       }
+       if block.Height == m.startBlockHeight {
+               blockHash := block.Hash()
+               if err := m.movStore.InitDBState(block.Height, &blockHash); err != nil {
+                       return err
+               }
+               return nil
+       }
+       if err := m.validateMatchedTxSequence(block.Transactions); err != nil {
+               return err
+       }
+       addOrders, deleteOrders, err := applyTransactions(block.Transactions)
+       if err != nil {
+               return err
+       }
+       return m.movStore.ProcessOrders(addOrders, deleteOrders, &block.BlockHeader)
+ }
+ /*
+       @issue: I have two orders A and B, order A's seller program is order B and order B's seller program is order A.
+     Assume consensus node accept 0% fee and This two orders are the only two order of this trading pair, will this
+     become an infinite loop and DDoS attacks the whole network?
+ */
+ // BeforeProposalBlock return all transactions than can be matched, and the number of transactions cannot exceed the given capacity.
+ func (m *MovCore) BeforeProposalBlock(txs []*types.Tx, nodeProgram []byte, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error) {
+       if blockHeight <= m.startBlockHeight {
+               return nil, nil
+       }
+       orderTable, err := buildOrderTable(m.movStore, txs)
+       if err != nil {
+               return nil, err
+       }
+       matchEngine := match.NewEngine(orderTable, maxFeeRate, nodeProgram)
+       tradePairMap := make(map[string]bool)
+       tradePairIterator := database.NewTradePairIterator(m.movStore)
+       var packagedTxs []*types.Tx
+       for gasLeft > 0 && !isTimeout() && tradePairIterator.HasNext() {
+               tradePair := tradePairIterator.Next()
+               if tradePairMap[tradePair.Key()] {
+                       continue
+               }
+               tradePairMap[tradePair.Key()] = true
+               tradePairMap[tradePair.Reverse().Key()] = true
+               for gasLeft > 0 && !isTimeout() && matchEngine.HasMatchedTx(tradePair, tradePair.Reverse()) {
+                       matchedTx, err := matchEngine.NextMatchedTx(tradePair, tradePair.Reverse())
+                       if err != nil {
+                               return nil, err
+                       }
+                       gasUsed := calcMatchedTxGasUsed(matchedTx)
+                       if gasLeft-gasUsed >= 0 {
+                               packagedTxs = append(packagedTxs, matchedTx)
+                       }
+                       gasLeft -= gasUsed
+               }
+       }
+       return packagedTxs, nil
+ }
+ // ChainStatus return the current block height and block hash in dex core
+ func (m *MovCore) ChainStatus() (uint64, *bc.Hash, error) {
+       state, err := m.movStore.GetMovDatabaseState()
+       if err != nil {
+               return 0, nil, err
+       }
+       return state.Height, state.Hash, nil
+ }
+ // DetachBlock parse pending order and cancel from the the transactions of block
+ // and add cancel order to the dex db, remove pending order from dex db.
+ func (m *MovCore) DetachBlock(block *types.Block) error {
+       if block.Height <= m.startBlockHeight {
+               return nil
+       }
+       deleteOrders, addOrders, err := applyTransactions(block.Transactions)
+       if err != nil {
+               return err
+       }
+       return m.movStore.ProcessOrders(addOrders, deleteOrders, &block.BlockHeader)
+ }
+ // IsDust block the transaction that are not generated by the match engine
+ func (m *MovCore) IsDust(tx *types.Tx) bool {
+       for _, input := range tx.Inputs {
+               if segwit.IsP2WMCScript(input.ControlProgram()) && !contract.IsCancelClauseSelector(input) {
+                       return true
+               }
+       }
+       return false
+ }
+ // Name return the name of current module
+ func (m *MovCore) Name() string {
+       return "MOV"
+ }
+ // StartHeight return the start block height of current module
+ func (m *MovCore) StartHeight() uint64 {
+       return m.startBlockHeight
+ }
+ // ValidateBlock no need to verify the block header, because the first module has been verified.
+ // just need to verify the transactions in the block.
+ func (m *MovCore) ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error {
+       return m.ValidateTxs(block.Transactions, verifyResults)
+ }
+ // ValidateTxs validate the trade transaction.
+ func (m *MovCore) ValidateTxs(txs []*types.Tx, verifyResults []*bc.TxVerifyResult) error {
+       for i, tx := range txs {
+               if err := m.ValidateTx(tx, verifyResults[i]); err != nil {
+                       return err
+               }
+       }
+       return nil
+ }
+ // ValidateTxs validate one transaction.
+ func (m *MovCore) ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error {
+       if common.IsMatchedTx(tx) {
+               if err := validateMatchedTx(tx, verifyResult); err != nil {
+                       return err
+               }
+       }
+       if common.IsCancelOrderTx(tx) {
+               if err := validateCancelOrderTx(tx, verifyResult); err != nil {
+                       return err
+               }
+       }
+       for _, output := range tx.Outputs {
+               if !segwit.IsP2WMCScript(output.ControlProgram()) {
+                       continue
+               }
+               if verifyResult.StatusFail {
+                       return errStatusFailMustFalse
+               }
+               if err := validateMagneticContractArgs(output.AssetAmount(), output.ControlProgram()); err != nil {
+                       return err
+               }
+       }
+       return nil
+ }
+ func validateCancelOrderTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error {
+       if verifyResult.StatusFail {
+               return errStatusFailMustFalse
+       }
+       for _, input := range tx.Inputs {
+               if !segwit.IsP2WMCScript(input.ControlProgram()) {
+                       return errInputProgramMustP2WMCScript
+               }
+               if contract.IsTradeClauseSelector(input) {
+                       return errExistTradeInCancelOrderTx
+               }
+       }
+       return nil
+ }
+ func validateMagneticContractArgs(fromAssetAmount bc.AssetAmount, program []byte) error {
+       contractArgs, err := segwit.DecodeP2WMCProgram(program)
+       if err != nil {
+               return err
+       }
+       if *fromAssetAmount.AssetId == contractArgs.RequestedAsset {
+               return errInvalidTradePairs
+       }
+       if contractArgs.RatioNumerator <= 0 || contractArgs.RatioDenominator <= 0 {
+               return errRatioOfTradeLessThanZero
+       }
+       if match.CalcRequestAmount(fromAssetAmount.Amount, contractArgs) < 1 {
+               return errRequestAmountMath
+       }
+       return nil
+ }
+ func validateMatchedTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error {
+       if verifyResult.StatusFail {
+               return errStatusFailMustFalse
+       }
+       fromAssetIDMap := make(map[string]bool)
+       toAssetIDMap := make(map[string]bool)
+       for i, input := range tx.Inputs {
+               if !segwit.IsP2WMCScript(input.ControlProgram()) {
+                       return errInputProgramMustP2WMCScript
+               }
+               if contract.IsCancelClauseSelector(input) {
+                       return errExistCancelOrderInMatchedTx
+               }
+               order, err := common.NewOrderFromInput(tx, i)
+               if err != nil {
+                       return err
+               }
+               fromAssetIDMap[order.FromAssetID.String()] = true
+               toAssetIDMap[order.ToAssetID.String()] = true
+       }
+       if len(fromAssetIDMap) != len(tx.Inputs) || len(toAssetIDMap) != len(tx.Inputs) {
+               return errAssetIDMustUniqueInMatchedTx
+       }
+       return validateMatchedTxFeeAmount(tx)
+ }
+ func validateMatchedTxFeeAmount(tx *types.Tx) error {
+       txFee, err := match.CalcMatchedTxFee(&tx.TxData, maxFeeRate)
+       if err != nil {
+               return err
+       }
+       for _, amount := range txFee {
+               if amount.FeeAmount > amount.MaxFeeAmount {
+                       return errAmountOfFeeGreaterThanMaximum
+               }
+       }
+       return nil
+ }
+ /*
+       @issue: the match package didn't support circle yet
+ */
+ func (m *MovCore) validateMatchedTxSequence(txs []*types.Tx) error {
+       orderTable, err := buildOrderTable(m.movStore, txs)
+       if err != nil {
+               return err
+       }
+       matchEngine := match.NewEngine(orderTable, maxFeeRate, nil)
+       for _, matchedTx := range txs {
+               if !common.IsMatchedTx(matchedTx) {
+                       continue
+               }
+               tradePairs, err := getSortedTradePairsFromMatchedTx(matchedTx)
+               if err != nil {
+                       return err
+               }
+               actualMatchedTx, err := matchEngine.NextMatchedTx(tradePairs...)
+               if err != nil {
+                       return err
+               }
+               if len(matchedTx.Inputs) != len(actualMatchedTx.Inputs) {
+                       return errLengthOfInputIsIncorrect
+               }
+               spendOutputIDs := make(map[string]bool)
+               for _, input := range matchedTx.Inputs {
+                       spendOutputID, err := input.SpentOutputID()
+                       if err != nil {
+                               return err
+                       }
+                       spendOutputIDs[spendOutputID.String()] = true
+               }
+               for _, input := range actualMatchedTx.Inputs {
+                       spendOutputID, err := input.SpentOutputID()
+                       if err != nil {
+                               return err
+                       }
+                       if _, ok := spendOutputIDs[spendOutputID.String()]; !ok {
+                               return errSpendOutputIDIsIncorrect
+                       }
+               }
+       }
+       return nil
+ }
+ func applyTransactions(txs []*types.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)
+               if err != nil {
+                       return nil, nil, err
+               }
+               for _, order := range addOrders {
+                       addOrderMap[order.Key()] = order
+               }
+               deleteOrders, err := getDeleteOrdersFromTx(tx)
+               if err != nil {
+                       return nil, nil, err
+               }
+               for _, order := range deleteOrders {
+                       deleteOrderMap[order.Key()] = order
+               }
+       }
+       addOrders, deleteOrders := mergeOrders(addOrderMap, deleteOrderMap)
+       return addOrders, deleteOrders, nil
+ }
+ /*
+       @issue: if consensus node packed match transaction first then packed regular tx, this function's logic may make a valid block invalid
+ */
+ func buildOrderTable(store database.MovStore, txs []*types.Tx) (*match.OrderTable, error) {
+       var nonMatchedTxs []*types.Tx
+       for _, tx := range txs {
+               if !common.IsMatchedTx(tx) {
+                       nonMatchedTxs = append(nonMatchedTxs, tx)
+               }
+       }
+       var arrivalAddOrders, arrivalDelOrders []*common.Order
+       for _, tx := range nonMatchedTxs {
+               addOrders, err := getAddOrdersFromTx(tx)
+               if err != nil {
+                       return nil, err
+               }
+               delOrders, err := getDeleteOrdersFromTx(tx)
+               if err != nil {
+                       return nil, err
+               }
+               arrivalAddOrders = append(arrivalAddOrders, addOrders...)
+               arrivalDelOrders = append(arrivalDelOrders, delOrders...)
+       }
+       return match.NewOrderTable(store, arrivalAddOrders, arrivalDelOrders), nil
+ }
+ func calcMatchedTxGasUsed(tx *types.Tx) int64 {
+       return int64(len(tx.Inputs))*150 + int64(tx.SerializedSize)
+ }
+ func getAddOrdersFromTx(tx *types.Tx) ([]*common.Order, error) {
+       var orders []*common.Order
+       for i, output := range tx.Outputs {
+               if output.OutputType() != types.IntraChainOutputType || !segwit.IsP2WMCScript(output.ControlProgram()) {
+                       continue
+               }
+               order, err := common.NewOrderFromOutput(tx, i)
+               if err != nil {
+                       return nil, err
+               }
+               orders = append(orders, order)
+       }
+       return orders, nil
+ }
+ func getDeleteOrdersFromTx(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()) {
+                       continue
+               }
+               order, err := common.NewOrderFromInput(tx, i)
+               if err != nil {
+                       return nil, err
+               }
+               orders = append(orders, order)
+       }
+       return orders, nil
+ }
+ func getSortedTradePairsFromMatchedTx(tx *types.Tx) ([]*common.TradePair, error) {
+       assetMap := make(map[bc.AssetID]bc.AssetID)
+       var firstTradePair *common.TradePair
+       for _, tx := range tx.Inputs {
+               contractArgs, err := segwit.DecodeP2WMCProgram(tx.ControlProgram())
+               if err != nil {
+                       return nil, err
+               }
+               assetMap[tx.AssetID()] = contractArgs.RequestedAsset
+               if firstTradePair == nil {
+                       firstTradePair = &common.TradePair{FromAssetID: tx.AssetAmount().AssetId, ToAssetID: &contractArgs.RequestedAsset}
+               }
+       }
+       tradePairs := []*common.TradePair{firstTradePair}
+       for tradePair := firstTradePair; *tradePair.ToAssetID != *firstTradePair.FromAssetID; {
+               nextTradePairToAssetID, ok := assetMap[*tradePair.ToAssetID]
+               if !ok {
+                       return nil, errInvalidTradePairs
+               }
+               tradePair = &common.TradePair{FromAssetID: tradePair.ToAssetID, ToAssetID: &nextTradePairToAssetID}
+               tradePairs = append(tradePairs, tradePair)
+       }
+       if len(tradePairs) != len(tx.Inputs) {
+               return nil, errInvalidTradePairs
+       }
+       return tradePairs, nil
+ }
+ func mergeOrders(addOrderMap, deleteOrderMap map[string]*common.Order) ([]*common.Order, []*common.Order) {
+       var deleteOrders, addOrders []*common.Order
+       for orderID, order := range addOrderMap {
+               if _, ok := deleteOrderMap[orderID]; ok {
+                       delete(deleteOrderMap, orderID)
+                       continue
+               }
+               addOrders = append(addOrders, order)
+       }
+       for _, order := range deleteOrderMap {
+               deleteOrders = append(deleteOrders, order)
+       }
+       return addOrders, deleteOrders
+ }
index 0000000,e83ce3e..26ed967
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,451 +1,451 @@@
 -      "github.com/vapor/application/mov/common"
 -      "github.com/vapor/application/mov/database"
 -      "github.com/vapor/application/mov/mock"
 -      "github.com/vapor/consensus"
 -      dbm "github.com/vapor/database/leveldb"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/bc/types"
 -      "github.com/vapor/protocol/vm"
 -      "github.com/vapor/testutil"
+ package mov
+ import (
+       "math"
+       "os"
+       "testing"
++      "github.com/bytom/vapor/application/mov/common"
++      "github.com/bytom/vapor/application/mov/database"
++      "github.com/bytom/vapor/application/mov/mock"
++      "github.com/bytom/vapor/consensus"
++      dbm "github.com/bytom/vapor/database/leveldb"
++      "github.com/bytom/vapor/protocol/bc"
++      "github.com/bytom/vapor/protocol/bc/types"
++      "github.com/bytom/vapor/protocol/vm"
++      "github.com/bytom/vapor/testutil"
+ )
+ /*
+       @addTest:BeforeProposalBlock: will gas affect generate tx? will be packed tx affect generate tx?
+       @addTest:TestApplyBlock: one block has two different trade pairs & different trade pair won't affect each order(attach & detach)
+       @addTest:TestApplyBlock: node packed maker tx and match transaction in random order(attach & detach)
+       @addTest:TestValidateBlock: one tx has trade input and cancel input mixed
+       @addTest:TestValidateBlock: regular match transaction's seller program is also a P2WMCProgram
+ */
+ func TestApplyBlock(t *testing.T) {
+       initBlockHeader := &types.BlockHeader{Height: 1, PreviousBlockHash: bc.Hash{}}
+       cases := []struct {
+               desc        string
+               block       *types.Block
+               blockFunc   testFun
+               initOrders  []*common.Order
+               wantOrders  []*common.Order
+               wantDBState *common.MovDatabaseState
+               wantError   error
+       }{
+               {
+                       desc: "apply block has pending order transaction",
+                       block: &types.Block{
+                               BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
+                               Transactions: []*types.Tx{
+                                       mock.Btc2EthMakerTxs[0], mock.Eth2BtcMakerTxs[0],
+                               },
+                       },
+                       blockFunc:   applyBlock,
+                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.Btc2EthMakerTxs[0], 0), mock.MustNewOrderFromOutput(mock.Eth2BtcMakerTxs[0], 0)},
+                       wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
+               },
+               {
+                       desc: "apply block has full matched transaction",
+                       block: &types.Block{
+                               BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
+                               Transactions: []*types.Tx{
+                                       mock.MatchedTxs[1],
+                               },
+                       },
+                       blockFunc:   applyBlock,
+                       initOrders:  []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[0]},
+                       wantOrders:  []*common.Order{mock.Btc2EthOrders[1]},
+                       wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
+               },
+               {
+                       desc: "apply block has partial matched transaction",
+                       block: &types.Block{
+                               BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
+                               Transactions: []*types.Tx{
+                                       mock.MatchedTxs[0],
+                               },
+                       },
+                       blockFunc:   applyBlock,
+                       initOrders:  []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[1]},
+                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[0], 1)},
+                       wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
+               },
+               {
+                       desc: "apply block has two partial matched transaction",
+                       block: &types.Block{
+                               BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
+                               Transactions: []*types.Tx{
+                                       mock.MatchedTxs[2], mock.MatchedTxs[3],
+                               },
+                       },
+                       blockFunc:   applyBlock,
+                       initOrders:  []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[2]},
+                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[3], 1)},
+                       wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
+               },
+               {
+                       desc: "apply block has partial matched transaction by pending orders from tx pool",
+                       block: &types.Block{
+                               BlockHeader: types.BlockHeader{Height: 2, PreviousBlockHash: initBlockHeader.Hash()},
+                               Transactions: []*types.Tx{
+                                       mock.Btc2EthMakerTxs[0],
+                                       mock.Eth2BtcMakerTxs[1],
+                                       mock.MatchedTxs[4],
+                               },
+                       },
+                       blockFunc:   applyBlock,
+                       initOrders:  []*common.Order{},
+                       wantOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[4], 1)},
+                       wantDBState: &common.MovDatabaseState{Height: 2, Hash: hashPtr(testutil.MustDecodeHash("88dbcde57bb2b53b107d7494f20f1f1a892307a019705980c3510890449c0020"))},
+               },
+               {
+                       desc: "detach block has pending order transaction",
+                       block: &types.Block{
+                               BlockHeader: *initBlockHeader,
+                               Transactions: []*types.Tx{
+                                       mock.Btc2EthMakerTxs[0], mock.Eth2BtcMakerTxs[1],
+                               },
+                       },
+                       blockFunc:   detachBlock,
+                       initOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.Btc2EthMakerTxs[0], 0), mock.MustNewOrderFromOutput(mock.Eth2BtcMakerTxs[1], 0)},
+                       wantOrders:  []*common.Order{},
+                       wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
+               },
+               {
+                       desc: "detach block has full matched transaction",
+                       block: &types.Block{
+                               BlockHeader: *initBlockHeader,
+                               Transactions: []*types.Tx{
+                                       mock.MatchedTxs[1],
+                               },
+                       },
+                       blockFunc:   detachBlock,
+                       initOrders:  []*common.Order{mock.Btc2EthOrders[1]},
+                       wantOrders:  []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Eth2BtcOrders[0]},
+                       wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
+               },
+               {
+                       desc: "detach block has partial matched transaction",
+                       block: &types.Block{
+                               BlockHeader: *initBlockHeader,
+                               Transactions: []*types.Tx{
+                                       mock.MatchedTxs[0],
+                               },
+                       },
+                       blockFunc:   detachBlock,
+                       initOrders:  []*common.Order{mock.MustNewOrderFromOutput(mock.MatchedTxs[0], 1)},
+                       wantOrders:  []*common.Order{mock.Btc2EthOrders[0], mock.Eth2BtcOrders[1]},
+                       wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
+               },
+               {
+                       desc: "detach block has two partial matched transaction",
+                       block: &types.Block{
+                               BlockHeader: *initBlockHeader,
+                               Transactions: []*types.Tx{
+                                       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]},
+                       wantDBState: &common.MovDatabaseState{Height: 0, Hash: &bc.Hash{}},
+               },
+       }
+       defer os.RemoveAll("temp")
+       for i, c := range cases {
+               testDB := dbm.NewDB("testdb", "leveldb", "temp")
+               store := database.NewLevelDBMovStore(testDB)
+               if err := store.InitDBState(0, &bc.Hash{}); err != nil {
+                       t.Fatal(err)
+               }
+               if err := store.ProcessOrders(c.initOrders, nil, initBlockHeader); err != nil {
+                       t.Fatal(err)
+               }
+               movCore := &MovCore{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)
+               }
+               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)
+               }
+               dbState, err := store.GetMovDatabaseState()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               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)
+               }
+               testDB.Close()
+               os.RemoveAll("temp")
+       }
+ }
+ func TestValidateBlock(t *testing.T) {
+       cases := []struct {
+               desc          string
+               block         *types.Block
+               verifyResults []*bc.TxVerifyResult
+               wantError     error
+       }{
+               {
+                       desc: "block only has maker tx",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       mock.Eth2BtcMakerTxs[0],
+                                       mock.Btc2EthMakerTxs[0],
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: false}},
+                       wantError:     nil,
+               },
+               {
+                       desc: "block only has matched tx",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       mock.MatchedTxs[0],
+                                       mock.MatchedTxs[1],
+                                       mock.MatchedTxs[2],
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: false}, {StatusFail: false}},
+                       wantError:     nil,
+               },
+               {
+                       desc: "block has maker tx and matched tx",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       mock.Eth2BtcMakerTxs[0],
+                                       mock.Btc2EthMakerTxs[0],
+                                       mock.MatchedTxs[0],
+                                       mock.MatchedTxs[1],
+                                       mock.MatchedTxs[2],
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: false}, {StatusFail: false}, {StatusFail: false}, {StatusFail: false}},
+                       wantError:     nil,
+               },
+               {
+                       desc: "status fail of maker tx is true",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       mock.Eth2BtcMakerTxs[0],
+                                       mock.Btc2EthMakerTxs[0],
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: true}},
+                       wantError:     errStatusFailMustFalse,
+               },
+               {
+                       desc: "status fail of matched tx is true",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       mock.MatchedTxs[1],
+                                       mock.MatchedTxs[2],
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: true}},
+                       wantError:     errStatusFailMustFalse,
+               },
+               {
+                       desc: "asset id in matched tx is not unique",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       types.NewTx(types.TxData{
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram),
+                                                       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}),
+                                               },
+                                       }),
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}, {StatusFail: true}},
+                       wantError:     errAssetIDMustUniqueInMatchedTx,
+               },
+               {
+                       desc: "common input in the matched tx",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       types.NewTx(types.TxData{
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram),
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *mock.Eth2BtcOrders[0].Utxo.SourceID, *mock.Eth2BtcOrders[0].FromAssetID, mock.Eth2BtcOrders[0].Utxo.Amount, mock.Eth2BtcOrders[0].Utxo.SourcePos, mock.Eth2BtcOrders[0].Utxo.ControlProgram),
+                                                       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(*consensus.BTMAssetID, 100, []byte{0x51}),
+                                               },
+                                       }),
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
+                       wantError:     errInputProgramMustP2WMCScript,
+               },
+               {
+                       desc: "cancel order in the matched tx",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       types.NewTx(types.TxData{
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram),
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *mock.Eth2BtcOrders[0].Utxo.SourceID, *mock.Eth2BtcOrders[0].FromAssetID, mock.Eth2BtcOrders[0].Utxo.Amount, mock.Eth2BtcOrders[0].Utxo.SourcePos, mock.Eth2BtcOrders[0].Utxo.ControlProgram),
+                                                       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, []byte{0x51}),
+                                               },
+                                       }),
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
+                       wantError:     errExistCancelOrderInMatchedTx,
+               },
+               {
+                       desc: "common input in the cancel order tx",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       types.NewTx(types.TxData{
+                                               Inputs: []*types.TxInput{
+                                                       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),
+                                                       types.NewSpendInput(nil, testutil.MustDecodeHash("28b7b53d8dc90006bf97e0a4eaae2a72ec3d869873188698b694beaf20789f21"), *consensus.BTMAssetID, 100, 0, []byte{0x51}),
+                                               },
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, 10, testutil.MustDecodeHexString("51")),
+                                                       types.NewIntraChainOutput(*consensus.BTMAssetID, 100, []byte{0x51}),
+                                               },
+                                       }),
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
+                       wantError:     errInputProgramMustP2WMCScript,
+               },
+               {
+                       desc: "amount of fee greater than max fee amount",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       types.NewTx(types.TxData{
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram),
+                                                       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("51")),
+                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 10, testutil.MustDecodeHexString("55")),
+                                                       // re-order
+                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 270, mock.Eth2BtcOrders[2].Utxo.ControlProgram),
+                                                       // fee
+                                                       types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 40, []byte{0x59}),
+                                               },
+                                       }),
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
+                       wantError:     errAmountOfFeeGreaterThanMaximum,
+               },
+               {
+                       desc: "ratio numerator is zero",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       types.NewTx(types.TxData{
+                                               Inputs:  []*types.TxInput{types.NewSpendInput(nil, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, []byte{0x51})},
+                                               Outputs: []*types.TxOutput{types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.MustCreateP2WMCProgram(mock.ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"), 0, 1))},
+                                       }),
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
+                       wantError:     errRatioOfTradeLessThanZero,
+               },
+               {
+                       desc: "ratio denominator is zero",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       types.NewTx(types.TxData{
+                                               Inputs:  []*types.TxInput{types.NewSpendInput(nil, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, []byte{0x51})},
+                                               Outputs: []*types.TxOutput{types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.MustCreateP2WMCProgram(mock.ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"), 1, 0))},
+                                       }),
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
+                       wantError:     errRatioOfTradeLessThanZero,
+               },
+               {
+                       desc: "want amount is overflow",
+                       block: &types.Block{
+                               Transactions: []*types.Tx{
+                                       types.NewTx(types.TxData{
+                                               Inputs:  []*types.TxInput{types.NewSpendInput(nil, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, []byte{0x51})},
+                                               Outputs: []*types.TxOutput{types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.MustCreateP2WMCProgram(mock.ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"), math.MaxInt64, 1))},
+                                       }),
+                               },
+                       },
+                       verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
+                       wantError:     errRequestAmountMath,
+               },
+       }
+       for i, c := range cases {
+               movCore := &MovCore{}
+               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)
+               }
+       }
+ }
+ type testFun func(movCore *MovCore, block *types.Block) error
+ func applyBlock(movCore *MovCore, block *types.Block) error {
+       return movCore.ApplyBlock(block)
+ }
+ func detachBlock(movCore *MovCore, block *types.Block) error {
+       return movCore.DetachBlock(block)
+ }
+ func queryAllOrders(store *database.LevelDBMovStore) []*common.Order {
+       var orders []*common.Order
+       tradePairIterator := database.NewTradePairIterator(store)
+       for tradePairIterator.HasNext() {
+               orderIterator := database.NewOrderIterator(store, tradePairIterator.Next())
+               for orderIterator.HasNext() {
+                       orders = append(orders, orderIterator.NextBatch()...)
+               }
+       }
+       return orders
+ }
+ func ordersEquals(orders1 []*common.Order, orders2 []*common.Order) bool {
+       orderMap1 := make(map[string]*common.Order)
+       for _, order := range orders1 {
+               orderMap1[order.Key()] = order
+       }
+       orderMap2 := make(map[string]*common.Order)
+       for _, order := range orders2 {
+               orderMap2[order.Key()] = order
+       }
+       return testutil.DeepEqual(orderMap1, orderMap2)
+ }
+ func hashPtr(hash bc.Hash) *bc.Hash {
+       return &hash
+ }
Simple merge
Simple merge
Simple merge
@@@ -3,11 -3,13 +3,13 @@@ package segwi
  import (
        "errors"
  
 -      "github.com/vapor/consensus"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/vm"
 -      "github.com/vapor/protocol/vm/vmutil"
 +      "github.com/bytom/vapor/consensus"
++      "github.com/bytom/vapor/protocol/bc"
 +      "github.com/bytom/vapor/protocol/vm"
 +      "github.com/bytom/vapor/protocol/vm/vmutil"
  )
  
+ // IsP2WScript is used to determine whether it is a P2WScript or not
  func IsP2WScript(prog []byte) bool {
        return IsP2WPKHScript(prog) || IsP2WSHScript(prog) || IsStraightforward(prog)
  }
@@@ -93,9 -93,10 +93,10 @@@ UNLOCK TABLES
  CREATE TABLE `assets` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `asset_id` varchar(64) NOT NULL,
 -  `issuance_program` varchar(128) NOT NULL,
 +  `issuance_program` mediumtext NOT NULL,
    `vm_version` int(11) NOT NULL DEFAULT '1',
    `definition` text,
+   `is_open_federation_issue` tinyint(1) DEFAULT '0',
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
Simple merge
Simple merge
Simple merge
diff --cc node/node.go
@@@ -14,24 -14,25 +14,25 @@@ import 
        cmn "github.com/tendermint/tmlibs/common"
        browser "github.com/toqueteos/webbrowser"
  
 -      "github.com/vapor/accesstoken"
 -      "github.com/vapor/account"
 -      "github.com/vapor/api"
 -      "github.com/vapor/application/mov"
 -      "github.com/vapor/asset"
 -      "github.com/vapor/blockchain/pseudohsm"
 -      cfg "github.com/vapor/config"
 -      "github.com/vapor/consensus"
 -      "github.com/vapor/database"
 -      dbm "github.com/vapor/database/leveldb"
 -      "github.com/vapor/env"
 -      "github.com/vapor/event"
 -      vaporLog "github.com/vapor/log"
 -      "github.com/vapor/net/websocket"
 -      "github.com/vapor/netsync"
 -      "github.com/vapor/proposal/blockproposer"
 -      "github.com/vapor/protocol"
 -      "github.com/vapor/protocol/bc/types"
 -      w "github.com/vapor/wallet"
 +      "github.com/bytom/vapor/accesstoken"
 +      "github.com/bytom/vapor/account"
 +      "github.com/bytom/vapor/api"
++      "github.com/bytom/vapor/application/mov"
 +      "github.com/bytom/vapor/asset"
 +      "github.com/bytom/vapor/blockchain/pseudohsm"
 +      cfg "github.com/bytom/vapor/config"
 +      "github.com/bytom/vapor/consensus"
 +      "github.com/bytom/vapor/database"
 +      dbm "github.com/bytom/vapor/database/leveldb"
 +      "github.com/bytom/vapor/env"
 +      "github.com/bytom/vapor/event"
 +      vaporLog "github.com/bytom/vapor/log"
 +      "github.com/bytom/vapor/net/websocket"
 +      "github.com/bytom/vapor/netsync"
 +      "github.com/bytom/vapor/proposal/blockproposer"
 +      "github.com/bytom/vapor/protocol"
 +      "github.com/bytom/vapor/protocol/bc/types"
 +      w "github.com/bytom/vapor/wallet"
  )
  
  const (
@@@ -7,19 -7,202 +7,202 @@@ import 
  
        log "github.com/sirupsen/logrus"
  
 -      "github.com/vapor/account"
 -      "github.com/vapor/blockchain/txbuilder"
 -      "github.com/vapor/consensus"
 -      "github.com/vapor/errors"
 -      "github.com/vapor/protocol"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/bc/types"
 -      "github.com/vapor/protocol/state"
 -      "github.com/vapor/protocol/validation"
 -      "github.com/vapor/protocol/vm/vmutil"
 +      "github.com/bytom/vapor/account"
 +      "github.com/bytom/vapor/blockchain/txbuilder"
 +      "github.com/bytom/vapor/consensus"
 +      "github.com/bytom/vapor/errors"
 +      "github.com/bytom/vapor/protocol"
 +      "github.com/bytom/vapor/protocol/bc"
 +      "github.com/bytom/vapor/protocol/bc/types"
 +      "github.com/bytom/vapor/protocol/state"
 +      "github.com/bytom/vapor/protocol/validation"
 +      "github.com/bytom/vapor/protocol/vm/vmutil"
  )
  
- const logModule = "mining"
+ const (
+       logModule     = "mining"
+       batchApplyNum = 64
+       timeoutOk = iota + 1
+       timeoutWarn
+       timeoutCritical
+ )
+ // NewBlockTemplate returns a new block template that is ready to be solved
+ func NewBlockTemplate(chain *protocol.Chain, accountManager *account.Manager, timestamp uint64, warnDuration, criticalDuration time.Duration) (*types.Block, error) {
+       builder := newBlockBuilder(chain, accountManager, timestamp, warnDuration, criticalDuration)
+       return builder.build()
+ }
+ type blockBuilder struct {
+       chain          *protocol.Chain
+       accountManager *account.Manager
+       block    *types.Block
+       txStatus *bc.TransactionStatus
+       utxoView *state.UtxoViewpoint
+       warnTimeoutCh     <-chan time.Time
+       criticalTimeoutCh <-chan time.Time
+       timeoutStatus     uint8
+       gasLeft           int64
+ }
+ func newBlockBuilder(chain *protocol.Chain, accountManager *account.Manager, timestamp uint64, warnDuration, criticalDuration time.Duration) *blockBuilder {
+       preBlockHeader := chain.BestBlockHeader()
+       block := &types.Block{
+               BlockHeader: types.BlockHeader{
+                       Version:           1,
+                       Height:            preBlockHeader.Height + 1,
+                       PreviousBlockHash: preBlockHeader.Hash(),
+                       Timestamp:         timestamp,
+                       BlockCommitment:   types.BlockCommitment{},
+                       BlockWitness:      types.BlockWitness{Witness: make([][]byte, consensus.ActiveNetParams.NumOfConsensusNode)},
+               },
+       }
+       builder := &blockBuilder{
+               chain:             chain,
+               accountManager:    accountManager,
+               block:             block,
+               txStatus:          bc.NewTransactionStatus(),
+               utxoView:          state.NewUtxoViewpoint(),
+               warnTimeoutCh:     time.After(warnDuration),
+               criticalTimeoutCh: time.After(criticalDuration),
+               gasLeft:           int64(consensus.ActiveNetParams.MaxBlockGas),
+               timeoutStatus:     timeoutOk,
+       }
+       return builder
+ }
+ func (b *blockBuilder) applyCoinbaseTransaction() error {
+       coinbaseTx, err := b.createCoinbaseTx()
+       if err != nil {
+               return errors.Wrap(err, "fail on create coinbase tx")
+       }
+       gasState, err := validation.ValidateTx(coinbaseTx.Tx, &bc.Block{BlockHeader: &bc.BlockHeader{Height: b.block.Height}, Transactions: []*bc.Tx{coinbaseTx.Tx}})
+       if err != nil {
+               return err
+       }
+       b.block.Transactions = append(b.block.Transactions, coinbaseTx)
+       if err := b.txStatus.SetStatus(0, false); err != nil {
+               return err
+       }
+       b.gasLeft -= gasState.GasUsed
+       return nil
+ }
+ func (b *blockBuilder) applyTransactions(txs []*types.Tx, timeoutStatus uint8) error {
+       tempTxs := []*types.Tx{}
+       for i := 0; i < len(txs); i++ {
+               if tempTxs = append(tempTxs, txs[i]); len(tempTxs) < batchApplyNum && i != len(txs)-1 {
+                       continue
+               }
+               results, gasLeft := preValidateTxs(tempTxs, b.chain, b.utxoView, b.gasLeft)
+               for _, result := range results {
+                       if result.err != nil && !result.gasOnly {
+                               log.WithFields(log.Fields{"module": logModule, "error": result.err}).Error("mining block generation: skip tx due to")
+                               b.chain.GetTxPool().RemoveTransaction(&result.tx.ID)
+                               continue
+                       }
+                       if err := b.txStatus.SetStatus(len(b.block.Transactions), result.gasOnly); err != nil {
+                               return err
+                       }
+                       b.block.Transactions = append(b.block.Transactions, result.tx)
+               }
+               b.gasLeft = gasLeft
+               tempTxs = []*types.Tx{}
+               if b.getTimeoutStatus() >= timeoutStatus {
+                       break
+               }
+       }
+       return nil
+ }
+ func (b *blockBuilder) applyTransactionFromPool() error {
+       txDescList := b.chain.GetTxPool().GetTransactions()
+       sort.Sort(byTime(txDescList))
+       poolTxs := make([]*types.Tx, len(txDescList))
+       for i, txDesc := range txDescList {
+               poolTxs[i] = txDesc.Tx
+       }
+       return b.applyTransactions(poolTxs, timeoutWarn)
+ }
+ func (b *blockBuilder) applyTransactionFromSubProtocol() error {
+       cp, err := b.accountManager.GetCoinbaseControlProgram()
+       if err != nil {
+               return err
+       }
+       isTimeout := func() bool {
+               return b.getTimeoutStatus() > timeoutOk
+       }
+       for i, p := range b.chain.SubProtocols() {
+               if b.gasLeft <= 0 || isTimeout() {
+                       break
+               }
+               subTxs, err := p.BeforeProposalBlock(b.block.Transactions, cp, b.block.Height, b.gasLeft, isTimeout)
+               if err != nil {
+                       log.WithFields(log.Fields{"module": logModule, "index": i, "error": err}).Error("failed on sub protocol txs package")
+                       continue
+               }
+               if err := b.applyTransactions(subTxs, timeoutCritical); err != nil {
+                       return err
+               }
+       }
+       return nil
+ }
+ func (b *blockBuilder) build() (*types.Block, error) {
+       if err := b.applyCoinbaseTransaction(); err != nil {
+               return nil, err
+       }
+       if err := b.applyTransactionFromPool(); err != nil {
+               return nil, err
+       }
+       if err := b.applyTransactionFromSubProtocol(); err != nil {
+               return nil, err
+       }
+       if err := b.calcBlockCommitment(); err != nil {
+               return nil, err
+       }
+       if err := b.chain.SignBlockHeader(&b.block.BlockHeader); err != nil {
+               return nil, err
+       }
+       return b.block, nil
+ }
+ func (b *blockBuilder) calcBlockCommitment() (err error) {
+       var txEntries []*bc.Tx
+       for _, tx := range b.block.Transactions {
+               txEntries = append(txEntries, tx.Tx)
+       }
+       b.block.BlockHeader.BlockCommitment.TransactionsMerkleRoot, err = types.TxMerkleRoot(txEntries)
+       if err != nil {
+               return err
+       }
+       b.block.BlockHeader.BlockCommitment.TransactionStatusHash, err = types.TxStatusMerkleRoot(b.txStatus.VerifyStatus)
+       return err
+ }
  
  // createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy
  // based on the passed block height to the provided address.  When the address
Simple merge
Simple merge
Simple merge
@@@ -3,13 -3,11 +3,11 @@@ package protoco
  import (
        log "github.com/sirupsen/logrus"
  
-       "github.com/bytom/vapor/config"
 -      "github.com/vapor/errors"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/bc/types"
 -      "github.com/vapor/protocol/state"
 -      "github.com/vapor/protocol/validation"
 +      "github.com/bytom/vapor/errors"
-       "github.com/bytom/vapor/event"
 +      "github.com/bytom/vapor/protocol/bc"
 +      "github.com/bytom/vapor/protocol/bc/types"
 +      "github.com/bytom/vapor/protocol/state"
 +      "github.com/bytom/vapor/protocol/validation"
  )
  
  var (
@@@ -5,12 -5,13 +5,13 @@@ import 
  
        log "github.com/sirupsen/logrus"
  
 -      "github.com/vapor/common"
 -      "github.com/vapor/config"
 -      "github.com/vapor/errors"
 -      "github.com/vapor/event"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/bc/types"
 -      "github.com/vapor/protocol/state"
 +      "github.com/bytom/vapor/common"
 +      "github.com/bytom/vapor/config"
++      "github.com/bytom/vapor/errors"
 +      "github.com/bytom/vapor/event"
 +      "github.com/bytom/vapor/protocol/bc"
 +      "github.com/bytom/vapor/protocol/bc/types"
 +      "github.com/bytom/vapor/protocol/state"
  )
  
  const (
diff --cc protocol/tx.go
Simple merge
Simple merge
@@@ -3,20 -3,18 +3,18 @@@ package validatio
  import (
        "fmt"
        "math"
+       "runtime"
        "sync"
  
 -      "github.com/vapor/common"
 -      "github.com/vapor/config"
 -      "github.com/vapor/consensus"
 -      "github.com/vapor/errors"
 -      "github.com/vapor/math/checked"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/vm"
++      "github.com/bytom/vapor/common"
 +      "github.com/bytom/vapor/config"
 +      "github.com/bytom/vapor/consensus"
 +      "github.com/bytom/vapor/errors"
 +      "github.com/bytom/vapor/math/checked"
 +      "github.com/bytom/vapor/protocol/bc"
 +      "github.com/bytom/vapor/protocol/vm"
  )
  
- const (
-       validateWorkerNum = 32
- )
  // validate transaction error
  var (
        ErrTxVersion                 = errors.New("invalid transaction version")
Simple merge
Simple merge
@@@ -2,8 -2,9 +2,9 @@@ package v
  
  import (
        "math"
+       "math/big"
  
 -      "github.com/vapor/math/checked"
 +      "github.com/bytom/vapor/math/checked"
  )
  
  func op1Add(vm *virtualMachine) error {
Simple merge
Simple merge
@@@ -1,9 -1,10 +1,10 @@@
  package vmutil
  
  import (
 -      "github.com/vapor/crypto/ed25519"
 -      "github.com/vapor/errors"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/protocol/vm"
 +      "github.com/bytom/vapor/crypto/ed25519"
 +      "github.com/bytom/vapor/errors"
++      "github.com/bytom/vapor/protocol/bc"
 +      "github.com/bytom/vapor/protocol/vm"
  )
  
  // pre-define errors
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc test/util.go
Simple merge
Simple merge
Simple merge
@@@ -14,14 -14,15 +14,15 @@@ import 
        "github.com/jinzhu/gorm"
        log "github.com/sirupsen/logrus"
  
 -      vpCommon "github.com/vapor/common"
 -      "github.com/vapor/consensus"
 -      "github.com/vapor/errors"
 -      "github.com/vapor/protocol/bc"
 -      "github.com/vapor/toolbar/federation/common"
 -      "github.com/vapor/toolbar/federation/config"
 -      "github.com/vapor/toolbar/federation/database"
 -      "github.com/vapor/toolbar/federation/database/orm"
 -      "github.com/vapor/toolbar/federation/service"
++      vpCommon "github.com/bytom/vapor/common"
 +      "github.com/bytom/vapor/consensus"
 +      "github.com/bytom/vapor/errors"
 +      "github.com/bytom/vapor/protocol/bc"
 +      "github.com/bytom/vapor/toolbar/federation/common"
 +      "github.com/bytom/vapor/toolbar/federation/config"
 +      "github.com/bytom/vapor/toolbar/federation/database"
 +      "github.com/bytom/vapor/toolbar/federation/database/orm"
 +      "github.com/bytom/vapor/toolbar/federation/service"
  )
  
  type mainchainKeeper struct {