OSDN Git Service

Merge pull request #473 from Bytom/master
authorPaladz <yzhu101@uottawa.ca>
Thu, 26 Dec 2019 06:46:26 +0000 (14:46 +0800)
committerGitHub <noreply@github.com>
Thu, 26 Dec 2019 06:46:26 +0000 (14:46 +0800)
Fix bug (#472)

60 files changed:
.travis.yml
application/mov/common/type.go
application/mov/common/type_test.go [new file with mode: 0644]
application/mov/common/util.go [new file with mode: 0644]
application/mov/contract/contract.go [new file with mode: 0644]
application/mov/database/mov_iterator.go [new file with mode: 0644]
application/mov/database/mov_iterator_test.go [new file with mode: 0644]
application/mov/database/mov_store.go
application/mov/database/mov_store_test.go
application/mov/match/match.go [new file with mode: 0644]
application/mov/match/match_test.go [new file with mode: 0644]
application/mov/match/order_book.go [new file with mode: 0644]
application/mov/match/order_book_test.go [new file with mode: 0644]
application/mov/mock/mock.go [new file with mode: 0644]
application/mov/mock/mock_mov_store.go [new file with mode: 0644]
application/mov/mov_core.go [new file with mode: 0644]
application/mov/mov_core_test.go [new file with mode: 0644]
blockchain/txbuilder/actions.go
common/crossin_asset.go [new file with mode: 0644]
config/federation_test.go
config/toml.go
consensus/general.go
consensus/segwit/segwit.go
docs/federation/sql_dump/federation_shema.sql
math/algorithm.go [new file with mode: 0644]
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/bc.pb.go
protocol/bc/bc.proto
protocol/bc/crosschain_input.go
protocol/bc/entry_test.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
toolbar/precognitive/config/config.go

index f32dc51..436a5c6 100644 (file)
@@ -11,7 +11,7 @@ branches:
     only:
         - master
         - dev
-        - v0.1
+        - mov
 
 script:
     - make ci
index d56dac0..0ea9f28 100644 (file)
@@ -1,7 +1,17 @@
 package common
 
-import "github.com/bytom/vapor/protocol/bc"
+import (
+       "encoding/hex"
+       "fmt"
+       "math/big"
 
+       "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
@@ -9,19 +19,134 @@ type MovUtxo struct {
        ControlProgram []byte
 }
 
+// Order store all the order information
 type Order struct {
-       FromAssetID *bc.AssetID
-       ToAssetID   *bc.AssetID
-       Utxo        *MovUtxo
-       Rate        float64
+       FromAssetID      *bc.AssetID
+       ToAssetID        *bc.AssetID
+       Utxo             *MovUtxo
+       RatioNumerator   int64
+       RatioDenominator int64
+}
+
+func (o *Order) Rate() float64 {
+       if o.RatioDenominator == 0 {
+               return 0
+       }
+       rate := big.NewFloat(0).SetInt64(o.RatioNumerator)
+       rate.Quo(rate, big.NewFloat(0).SetInt64(o.RatioDenominator))
+       result, _ := rate.Float64()
+       return result
+}
+
+// OrderSlice is define for order's sort
+type OrderSlice []*Order
+
+func (o OrderSlice) Len() int      { return len(o) }
+func (o OrderSlice) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
+func (o OrderSlice) Less(i, j int) bool {
+       if o[i].Rate() == o[j].Rate() {
+               return hex.EncodeToString(o[i].UTXOHash().Bytes()) < hex.EncodeToString(o[j].UTXOHash().Bytes())
+       }
+       return o[i].Rate() < o[j].Rate()
+}
+
+// NewOrderFromOutput convert txinput to order
+func NewOrderFromOutput(tx *types.Tx, outputIndex int) (*Order, error) {
+       outputID := tx.OutputID(outputIndex)
+       output, err := tx.IntraChainOutput(*outputID)
+       if err != nil {
+               return nil, err
+       }
+
+       contractArgs, err := segwit.DecodeP2WMCProgram(output.ControlProgram.Code)
+       if err != nil {
+               return nil, err
+       }
+
+       assetAmount := output.Source.Value
+       return &Order{
+               FromAssetID:      assetAmount.AssetId,
+               ToAssetID:        &contractArgs.RequestedAsset,
+               RatioNumerator:   contractArgs.RatioNumerator,
+               RatioDenominator: contractArgs.RatioDenominator,
+               Utxo: &MovUtxo{
+                       SourceID:       output.Source.Ref,
+                       Amount:         assetAmount.Amount,
+                       SourcePos:      uint64(outputIndex),
+                       ControlProgram: output.ControlProgram.Code,
+               },
+       }, nil
+}
+
+// NewOrderFromInput convert txoutput to order
+func NewOrderFromInput(tx *types.Tx, inputIndex int) (*Order, error) {
+       input, ok := tx.Inputs[inputIndex].TypedInput.(*types.SpendInput)
+       if !ok {
+               return nil, errors.New("input is not type of spend input")
+       }
+
+       contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram)
+       if err != nil {
+               return nil, err
+       }
+
+       return &Order{
+               FromAssetID:      input.AssetId,
+               ToAssetID:        &contractArgs.RequestedAsset,
+               RatioNumerator:   contractArgs.RatioNumerator,
+               RatioDenominator: contractArgs.RatioDenominator,
+               Utxo: &MovUtxo{
+                       SourceID:       &input.SourceID,
+                       Amount:         input.Amount,
+                       SourcePos:      input.SourcePosition,
+                       ControlProgram: input.ControlProgram,
+               },
+       }, nil
+}
+
+// Key return the unique key for representing this order
+func (o *Order) Key() string {
+       return fmt.Sprintf("%s:%d", o.Utxo.SourceID, o.Utxo.SourcePos)
+}
+
+// TradePair return the trade pair info
+func (o *Order) TradePair() *TradePair {
+       return &TradePair{FromAssetID: o.FromAssetID, ToAssetID: o.ToAssetID}
 }
 
+// UTXOHash calculate the utxo hash of this order
+func (o *Order) UTXOHash() *bc.Hash {
+       prog := &bc.Program{VmVersion: 1, Code: o.Utxo.ControlProgram}
+       src := &bc.ValueSource{
+               Ref:      o.Utxo.SourceID,
+               Value:    &bc.AssetAmount{AssetId: o.FromAssetID, Amount: o.Utxo.Amount},
+               Position: o.Utxo.SourcePos,
+       }
+       hash := bc.EntryID(bc.NewIntraChainOutput(src, prog, 0))
+       return &hash
+}
+
+// TradePair is the object for record trade pair info
 type TradePair struct {
        FromAssetID *bc.AssetID
        ToAssetID   *bc.AssetID
        Count       int
 }
 
+// Key return the unique key for representing this trade pair
+func (t *TradePair) Key() string {
+       return fmt.Sprintf("%s:%s", t.FromAssetID, t.ToAssetID)
+}
+
+// Reverse return the reverse trade pair object
+func (t *TradePair) Reverse() *TradePair {
+       return &TradePair{
+               FromAssetID: t.ToAssetID,
+               ToAssetID:   t.FromAssetID,
+       }
+}
+
+// MovDatabaseState is object to record DB image status
 type MovDatabaseState struct {
        Height uint64
        Hash   *bc.Hash
diff --git a/application/mov/common/type_test.go b/application/mov/common/type_test.go
new file mode 100644 (file)
index 0000000..8fe9e81
--- /dev/null
@@ -0,0 +1,27 @@
+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")
+       }
+}
diff --git a/application/mov/common/util.go b/application/mov/common/util.go
new file mode 100644 (file)
index 0000000..09413c9
--- /dev/null
@@ -0,0 +1,30 @@
+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
+}
diff --git a/application/mov/contract/contract.go b/application/mov/contract/contract.go
new file mode 100644 (file)
index 0000000..75cd00d
--- /dev/null
@@ -0,0 +1,41 @@
+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))
+}
diff --git a/application/mov/database/mov_iterator.go b/application/mov/database/mov_iterator.go
new file mode 100644 (file)
index 0000000..b06debf
--- /dev/null
@@ -0,0 +1,104 @@
+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
+}
diff --git a/application/mov/database/mov_iterator_test.go b/application/mov/database/mov_iterator_test.go
new file mode 100644 (file)
index 0000000..5df84e1
--- /dev/null
@@ -0,0 +1,166 @@
+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, RatioNumerator: 1, RatioDenominator: 10}
+       order2 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, RatioNumerator: 2, RatioDenominator: 10}
+       order3 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, RatioNumerator: 3, RatioDenominator: 10}
+       order4 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, RatioNumerator: 4, RatioDenominator: 10}
+       order5 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, RatioNumerator: 5, RatioDenominator: 10}
+)
+
+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)
+               }
+       }
+}
index 252886a..e9b7183 100644 (file)
@@ -12,15 +12,27 @@ import (
        "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
 
-       tradePairsNum = 1024
-       ordersNum     = 10240
-       assetIDLen    = 32
-       rateByteLen   = 8
+       fromAssetIDPos = 0
+       toAssetIDPos   = 1
+       assetIDLen     = 32
+       rateByteLen    = 8
+
+       tradePairsNum = 32
+       ordersNum     = 128
 )
 
 var (
@@ -30,6 +42,12 @@ var (
        bestMatchStore   = append(movStore, matchStatus)
 )
 
+type orderData struct {
+       Utxo             *common.MovUtxo
+       RatioNumerator   int64
+       RatioDenominator int64
+}
+
 func calcOrderKey(fromAssetID, toAssetID *bc.AssetID, utxoHash *bc.Hash, rate float64) []byte {
        buf := make([]byte, 8)
        binary.BigEndian.PutUint64(buf, math.Float64bits(rate))
@@ -44,27 +62,16 @@ func calcTradePairKey(fromAssetID, toAssetID *bc.AssetID) []byte {
        return append(key, toAssetID.Bytes()...)
 }
 
-func calcUTXOHash(order *common.Order) *bc.Hash {
-       prog := &bc.Program{VmVersion: 1, Code: order.Utxo.ControlProgram}
-       src := &bc.ValueSource{
-               Ref:      order.Utxo.SourceID,
-               Value:    &bc.AssetAmount{AssetId: order.FromAssetID, Amount: order.Utxo.Amount},
-               Position: order.Utxo.SourcePos,
-       }
-       hash := bc.EntryID(bc.NewIntraChainOutput(src, prog, 0))
-       return &hash
-}
-
-func getAssetIDFromTradePairKey(key []byte, prefix []byte, posIndex int) *bc.AssetID {
+func getAssetIDFromTradePairKey(key []byte, posIndex int) *bc.AssetID {
        b := [32]byte{}
-       pos := len(prefix) + assetIDLen*posIndex
+       pos := len(tradePairsPrefix) + assetIDLen*posIndex
        copy(b[:], key[pos:pos+assetIDLen])
        assetID := bc.NewAssetID(b)
        return &assetID
 }
 
-func getRateFromOrderKey(key []byte, prefix []byte) float64 {
-       ratePos := len(prefix) + assetIDLen*2
+func getRateFromOrderKey(key []byte) float64 {
+       ratePos := len(ordersPrefix) + assetIDLen*2
        return math.Float64frombits(binary.BigEndian.Uint64(key[ratePos : ratePos+rateByteLen]))
 }
 
@@ -72,24 +79,40 @@ type tradePairData struct {
        Count int
 }
 
-type MovStore struct {
+// LevelDBMovStore is the LevelDB implementation for MovStore
+type LevelDBMovStore struct {
        db dbm.DB
 }
 
-func NewMovStore(db dbm.DB, height uint64, hash *bc.Hash) (*MovStore, error) {
-       if value := db.Get(bestMatchStore); value == nil {
-               state := &common.MovDatabaseState{Height: height, Hash: hash}
-               value, err := json.Marshal(state)
-               if err != nil {
-                       return nil, err
-               }
+// NewLevelDBMovStore create a new LevelDBMovStore object
+func NewLevelDBMovStore(db dbm.DB) *LevelDBMovStore {
+       return &LevelDBMovStore{db: db}
+}
+
+// GetMovDatabaseState return the current DB's image status
+func (m *LevelDBMovStore) GetMovDatabaseState() (*common.MovDatabaseState, error) {
+       if value := m.db.Get(bestMatchStore); value != nil {
+               state := &common.MovDatabaseState{}
+               return state, json.Unmarshal(value, state)
+       }
+
+       return nil, errors.New("don't find state of mov-database")
+}
 
-               db.Set(bestMatchStore, value)
+// InitDBState set the DB's image status
+func (m *LevelDBMovStore) InitDBState(height uint64, hash *bc.Hash) error {
+       state := &common.MovDatabaseState{Height: height, Hash: hash}
+       value, err := json.Marshal(state)
+       if err != nil {
+               return err
        }
-       return &MovStore{db: db}, nil
+
+       m.db.Set(bestMatchStore, value)
+       return nil
 }
 
-func (m *MovStore) ListOrders(orderAfter *common.Order) ([]*common.Order, error) {
+// ListOrders return n orders after the input order
+func (m *LevelDBMovStore) ListOrders(orderAfter *common.Order) ([]*common.Order, error) {
        if orderAfter.FromAssetID == nil || orderAfter.ToAssetID == nil {
                return nil, errors.New("assetID is nil")
        }
@@ -98,8 +121,9 @@ func (m *MovStore) ListOrders(orderAfter *common.Order) ([]*common.Order, error)
        orderPrefix = append(orderPrefix, orderAfter.ToAssetID.Bytes()...)
 
        var startKey []byte
-       if orderAfter.Rate > 0 {
-               startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, calcUTXOHash(orderAfter), orderAfter.Rate)
+
+       if orderAfter.Rate() > 0 {
+               startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, orderAfter.UTXOHash(), orderAfter.Rate())
        }
 
        itr := m.db.IteratorPrefixWithStart(orderPrefix, startKey, false)
@@ -107,41 +131,71 @@ func (m *MovStore) ListOrders(orderAfter *common.Order) ([]*common.Order, error)
 
        var orders []*common.Order
        for txNum := 0; txNum < ordersNum && itr.Next(); txNum++ {
-               movUtxo := &common.MovUtxo{}
-               if err := json.Unmarshal(itr.Value(), movUtxo); err != nil {
+               orderData := &orderData{}
+               if err := json.Unmarshal(itr.Value(), orderData); err != nil {
                        return nil, err
                }
 
-               order := &common.Order{
-                       FromAssetID: orderAfter.FromAssetID,
-                       ToAssetID:   orderAfter.ToAssetID,
-                       Rate:        getRateFromOrderKey(itr.Key(), ordersPrefix),
-                       Utxo:        movUtxo,
-               }
-               orders = append(orders, order)
+               orders = append(orders, &common.Order{
+                       FromAssetID:      orderAfter.FromAssetID,
+                       ToAssetID:        orderAfter.ToAssetID,
+                       Utxo:             orderData.Utxo,
+                       RatioNumerator:   orderData.RatioNumerator,
+                       RatioDenominator: orderData.RatioDenominator,
+               })
        }
        return orders, nil
 }
 
-func (m *MovStore) ProcessOrders(addOrders []*common.Order, delOreders []*common.Order, blockHeader *types.BlockHeader) error {
+// ListTradePairsWithStart return n trade pairs after the input trade pair
+func (m *LevelDBMovStore) ListTradePairsWithStart(fromAssetIDAfter, toAssetIDAfter *bc.AssetID) ([]*common.TradePair, error) {
+       var startKey []byte
+       if fromAssetIDAfter != nil && toAssetIDAfter != nil {
+               startKey = calcTradePairKey(fromAssetIDAfter, toAssetIDAfter)
+       }
+
+       itr := m.db.IteratorPrefixWithStart(tradePairsPrefix, startKey, false)
+       defer itr.Release()
+
+       var tradePairs []*common.TradePair
+       for txNum := 0; txNum < tradePairsNum && itr.Next(); txNum++ {
+               key := itr.Key()
+               fromAssetID := getAssetIDFromTradePairKey(key, fromAssetIDPos)
+               toAssetID := getAssetIDFromTradePairKey(key, toAssetIDPos)
+
+               tradePairData := &tradePairData{}
+               if err := json.Unmarshal(itr.Value(), tradePairData); err != nil {
+                       return nil, err
+               }
+
+               tradePairs = append(tradePairs, &common.TradePair{FromAssetID: fromAssetID, ToAssetID: toAssetID, Count: tradePairData.Count})
+       }
+       return tradePairs, nil
+}
+
+// ProcessOrders update the DB's image by add new orders, delete the used order
+func (m *LevelDBMovStore) ProcessOrders(addOrders []*common.Order, delOrders []*common.Order, blockHeader *types.BlockHeader) error {
        if err := m.checkMovDatabaseState(blockHeader); err != nil {
                return err
        }
 
        batch := m.db.NewBatch()
-       tradePairsCnt := make(map[common.TradePair]int)
+       tradePairsCnt := make(map[string]*common.TradePair)
        if err := m.addOrders(batch, addOrders, tradePairsCnt); err != nil {
                return err
        }
 
-       m.deleteOrders(batch, delOreders, tradePairsCnt)
-
+       m.deleteOrders(batch, delOrders, tradePairsCnt)
        if err := m.updateTradePairs(batch, tradePairsCnt); err != nil {
                return err
        }
 
-       hash := blockHeader.Hash()
-       if err := m.saveMovDatabaseState(batch, &common.MovDatabaseState{Height: blockHeader.Height, Hash: &hash}); err != nil {
+       state, err := m.calcNextDatabaseState(blockHeader)
+       if err != nil {
+               return err
+       }
+
+       if err := m.saveMovDatabaseState(batch, state); err != nil {
                return err
        }
 
@@ -149,85 +203,103 @@ func (m *MovStore) ProcessOrders(addOrders []*common.Order, delOreders []*common
        return nil
 }
 
-func (m *MovStore) addOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[common.TradePair]int) error {
+func (m *LevelDBMovStore) addOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[string]*common.TradePair) error {
        for _, order := range orders {
-               data, err := json.Marshal(order.Utxo)
+               orderData := &orderData{
+                       Utxo:             order.Utxo,
+                       RatioNumerator:   order.RatioNumerator,
+                       RatioDenominator: order.RatioDenominator,
+               }
+               data, err := json.Marshal(orderData)
                if err != nil {
                        return err
                }
 
-               key := calcOrderKey(order.FromAssetID, order.ToAssetID, calcUTXOHash(order), order.Rate)
+               key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate())
                batch.Set(key, data)
 
-               tradePair := common.TradePair{
+               tradePair := &common.TradePair{
                        FromAssetID: order.FromAssetID,
                        ToAssetID:   order.ToAssetID,
                }
-               tradePairsCnt[tradePair] += 1
+               if _, ok := tradePairsCnt[tradePair.Key()]; !ok {
+                       tradePairsCnt[tradePair.Key()] = tradePair
+               }
+               tradePairsCnt[tradePair.Key()].Count++
        }
        return nil
 }
 
-func (m *MovStore) deleteOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[common.TradePair]int) {
-       for _, order := range orders {
-               key := calcOrderKey(order.FromAssetID, order.ToAssetID, calcUTXOHash(order), order.Rate)
-               batch.Delete(key)
+func (m *LevelDBMovStore) calcNextDatabaseState(blockHeader *types.BlockHeader) (*common.MovDatabaseState, error) {
+       hash := blockHeader.Hash()
+       height := blockHeader.Height
 
-               tradePair := common.TradePair{
-                       FromAssetID: order.FromAssetID,
-                       ToAssetID:   order.ToAssetID,
-               }
-               tradePairsCnt[tradePair] -= 1
+       state, err := m.GetMovDatabaseState()
+       if err != nil {
+               return nil, err
        }
-}
 
-func (m *MovStore) GetMovDatabaseState() (*common.MovDatabaseState, error) {
-       if value := m.db.Get(bestMatchStore); value != nil {
-               state := &common.MovDatabaseState{}
-               return state, json.Unmarshal(value, state)
+       if *state.Hash == hash {
+               hash = blockHeader.PreviousBlockHash
+               height = blockHeader.Height - 1
        }
 
-       return nil, errors.New("don't find state of mov-database")
+       return &common.MovDatabaseState{Height: height, Hash: &hash}, nil
 }
 
-func (m *MovStore) ListTradePairsWithStart(fromAssetIDAfter, toAssetIDAfter *bc.AssetID) ([]*common.TradePair, error) {
-       var startKey []byte
-       if fromAssetIDAfter != nil && toAssetIDAfter != nil {
-               startKey = calcTradePairKey(fromAssetIDAfter, toAssetIDAfter)
+func (m *LevelDBMovStore) checkMovDatabaseState(header *types.BlockHeader) error {
+       state, err := m.GetMovDatabaseState()
+       if err != nil {
+               return err
        }
 
-       itr := m.db.IteratorPrefixWithStart(tradePairsPrefix, startKey, false)
-       defer itr.Release()
+       if (*state.Hash == header.PreviousBlockHash && (state.Height+1) == header.Height) || *state.Hash == header.Hash() {
+               return nil
+       }
 
-       var tradePairs []*common.TradePair
-       for txNum := 0; txNum < tradePairsNum && itr.Next(); txNum++ {
-               key := itr.Key()
-               fromAssetID := getAssetIDFromTradePairKey(key, tradePairsPrefix, 0)
-               toAssetID := getAssetIDFromTradePairKey(key, tradePairsPrefix, 1)
+       return errors.New("the status of the block is inconsistent with that of mov-database")
+}
 
-               tradePairData := &tradePairData{}
-               if err := json.Unmarshal(itr.Value(), tradePairData); err != nil {
-                       return nil, err
+func (m *LevelDBMovStore) deleteOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[string]*common.TradePair) {
+       for _, order := range orders {
+               key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate())
+               batch.Delete(key)
+
+               tradePair := &common.TradePair{
+                       FromAssetID: order.FromAssetID,
+                       ToAssetID:   order.ToAssetID,
                }
+               if _, ok := tradePairsCnt[tradePair.Key()]; !ok {
+                       tradePairsCnt[tradePair.Key()] = tradePair
+               }
+               tradePairsCnt[tradePair.Key()].Count--
+       }
+}
 
-               tradePairs = append(tradePairs, &common.TradePair{FromAssetID: fromAssetID, ToAssetID: toAssetID, Count: tradePairData.Count})
+func (m *LevelDBMovStore) saveMovDatabaseState(batch dbm.Batch, state *common.MovDatabaseState) error {
+       value, err := json.Marshal(state)
+       if err != nil {
+               return err
        }
-       return tradePairs, nil
+
+       batch.Set(bestMatchStore, value)
+       return nil
 }
 
-func (m *MovStore) updateTradePairs(batch dbm.Batch, tradePairs map[common.TradePair]int) error {
-       for k, v := range tradePairs {
-               key := calcTradePairKey(k.FromAssetID, k.ToAssetID)
+func (m *LevelDBMovStore) updateTradePairs(batch dbm.Batch, tradePairs map[string]*common.TradePair) error {
+       for _, v := range tradePairs {
+               key := calcTradePairKey(v.FromAssetID, v.ToAssetID)
                tradePairData := &tradePairData{}
                if value := m.db.Get(key); value != nil {
                        if err := json.Unmarshal(value, tradePairData); err != nil {
                                return err
                        }
-               } else if v < 0 {
-                       return errors.New("don't find trade pair")
                }
 
-               tradePairData.Count += v
+               if tradePairData.Count += v.Count; tradePairData.Count < 0 {
+                       return errors.New("negative trade count")
+               }
+
                if tradePairData.Count > 0 {
                        value, err := json.Marshal(tradePairData)
                        if err != nil {
@@ -241,27 +313,3 @@ func (m *MovStore) updateTradePairs(batch dbm.Batch, tradePairs map[common.Trade
        }
        return nil
 }
-
-func (m *MovStore) checkMovDatabaseState(header *types.BlockHeader) error {
-       state, err := m.GetMovDatabaseState()
-       if err != nil {
-               return err
-       }
-
-       blockHash := header.Hash()
-       if (state.Hash.String() == header.PreviousBlockHash.String() && (state.Height+1) == header.Height) || state.Hash.String() == blockHash.String() {
-               return nil
-       }
-
-       return errors.New("the status of the block is inconsistent with that of mov-database")
-}
-
-func (m *MovStore) saveMovDatabaseState(batch dbm.Batch, state *common.MovDatabaseState) error {
-       value, err := json.Marshal(state)
-       if err != nil {
-               return err
-       }
-
-       batch.Set(bestMatchStore, value)
-       return nil
-}
index f212ae0..aeb9c3f 100644 (file)
@@ -10,10 +10,8 @@ import (
        "github.com/stretchr/testify/require"
 
        "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"
@@ -28,31 +26,203 @@ var (
        assetID6 = &bc.AssetID{V0: 6}
        assetID7 = &bc.AssetID{V0: 7}
        assetID8 = &bc.AssetID{V0: 8}
-)
 
-func TestCalcUTXOHash(t *testing.T) {
-       wantHash := "7cbaf92f950f2a6bededd6cc5ec08c924505f5365b0a8af963e1d52912c99667"
-       controlProgramStr := "0014ab5acbea076f269bfdc8ededbed7d0a13e6e0b19"
-
-       var controlProgram chainjson.HexBytes
-       controlProgram.UnmarshalText([]byte(controlProgramStr))
-
-       sourceID := testutil.MustDecodeHash("ca2faf5fcbf8ee2b43560a32594f608528b12a1fe79cee85252564f886f91060")
-       order := &common.Order{
-               FromAssetID: consensus.BTMAssetID,
-               Utxo: &common.MovUtxo{
-                       SourceID:       &sourceID,
-                       SourcePos:      0,
-                       Amount:         31249300000,
-                       ControlProgram: controlProgram[:],
+       mockOrders = []*common.Order{
+               &common.Order{
+                       FromAssetID:      assetID1,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   100090,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 21},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID1,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   90,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 22},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID1,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   97,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 23},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID1,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   98,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 13},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID1,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   98,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 24},
+                               Amount:         10,
+                               SourcePos:      1,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID1,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   99,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 24},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID1,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   96,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 25},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID1,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   95,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 26},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID1,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   90,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 1},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID1,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   90,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 2},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID3,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   96,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 33},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID4,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   95,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 34},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID4,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   96,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 36},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID5,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   96,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 37},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
+               },
+               &common.Order{
+                       FromAssetID:      assetID6,
+                       ToAssetID:        assetID2,
+                       RatioNumerator:   98,
+                       RatioDenominator: 100000,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       &bc.Hash{V0: 38},
+                               Amount:         1,
+                               SourcePos:      0,
+                               ControlProgram: []byte("aa"),
+                       },
                },
        }
+)
+
+func TestGetAssetIDFromTradePairKey(t *testing.T) {
+       b := calcTradePairKey(assetID1, assetID2)
+       gotA := getAssetIDFromTradePairKey(b, fromAssetIDPos)
+       gotB := getAssetIDFromTradePairKey(b, toAssetIDPos)
 
-       hash := calcUTXOHash(order)
-       if hash.String() != wantHash {
-               t.Fatal("The function is incorrect")
+       if *gotA != *assetID1 {
+               t.Fatalf("got wrong from asset id got %s, want %s", gotA.String(), assetID1.String())
        }
 
+       if *gotB != *assetID2 {
+               t.Fatalf("got wrong to asset id got %s, want %s", gotB.String(), assetID2.String())
+       }
 }
 
 func TestSortOrderKey(t *testing.T) {
@@ -75,15 +245,16 @@ func TestSortOrderKey(t *testing.T) {
        }
 
        cases := []struct {
-               orders []common.Order
+               orders []*common.Order
                want   []expectedData
        }{
                {
-                       orders: []common.Order{
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        1.00090,
+                       orders: []*common.Order{
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   100090,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 21},
                                                Amount:         1,
@@ -91,10 +262,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00090,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   90,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 22},
                                                Amount:         1,
@@ -102,10 +274,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00097,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   97,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 23},
                                                Amount:         1,
@@ -113,10 +286,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00098,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   98,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 13},
                                                Amount:         1,
@@ -124,10 +298,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00098,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   98,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 24},
                                                Amount:         10,
@@ -135,10 +310,47 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00099,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   98,
+                                       RatioDenominator: 100000,
+                                       Utxo: &common.MovUtxo{
+                                               SourceID:       &bc.Hash{V0: 25},
+                                               Amount:         10,
+                                               SourcePos:      1,
+                                               ControlProgram: []byte("aa"),
+                                       },
+                               },
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   98,
+                                       RatioDenominator: 100000,
+                                       Utxo: &common.MovUtxo{
+                                               SourceID:       &bc.Hash{V0: 26},
+                                               Amount:         10,
+                                               SourcePos:      1,
+                                               ControlProgram: []byte("aa"),
+                                       },
+                               },
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   98,
+                                       RatioDenominator: 100000,
+                                       Utxo: &common.MovUtxo{
+                                               SourceID:       &bc.Hash{V0: 27},
+                                               Amount:         10,
+                                               SourcePos:      1,
+                                               ControlProgram: []byte("aa"),
+                                       },
+                               },
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   99,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 24},
                                                Amount:         1,
@@ -146,10 +358,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00096,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   96,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 25},
                                                Amount:         1,
@@ -157,10 +370,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00095,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   95,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 26},
                                                Amount:         1,
@@ -168,10 +382,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00091,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   91,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 26},
                                                Amount:         1,
@@ -179,10 +394,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00092,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   92,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 27},
                                                Amount:         1,
@@ -190,10 +406,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00093,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   93,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 28},
                                                Amount:         1,
@@ -201,10 +418,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00094,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   94,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 29},
                                                Amount:         1,
@@ -212,10 +430,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00077,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   77,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 30},
                                                Amount:         1,
@@ -223,10 +442,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        0.00088,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   88,
+                                       RatioDenominator: 100000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 31},
                                                Amount:         1,
@@ -234,10 +454,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        999999.9521,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   9999999521,
+                                       RatioDenominator: 10000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 32},
                                                Amount:         1,
@@ -245,10 +466,11 @@ func TestSortOrderKey(t *testing.T) {
                                                ControlProgram: []byte("aa"),
                                        },
                                },
-                               common.Order{
-                                       FromAssetID: &bc.AssetID{V0: 1},
-                                       ToAssetID:   &bc.AssetID{V0: 0},
-                                       Rate:        888888.7954,
+                               &common.Order{
+                                       FromAssetID:      &bc.AssetID{V0: 1},
+                                       ToAssetID:        &bc.AssetID{V0: 0},
+                                       RatioNumerator:   8888887954,
+                                       RatioDenominator: 10000,
                                        Utxo: &common.MovUtxo{
                                                SourceID:       &bc.Hash{V0: 33},
                                                Amount:         1,
@@ -300,10 +522,22 @@ func TestSortOrderKey(t *testing.T) {
                                },
                                expectedData{
                                        rate:     0.00098,
+                                       utxoHash: "14b51a6103f75d9cacdf0f9551467588c687ed3b029e25c646d276720569e227",
+                               },
+                               expectedData{
+                                       rate:     0.00098,
                                        utxoHash: "1fa9fae83d0a5401a4e92f80636966486e763eecca588aa11dff02b415320602",
                                },
                                expectedData{
                                        rate:     0.00098,
+                                       utxoHash: "6687d18ddbe4e7381a844e393ca3032a412285c9da6988eff182106e28ba09ca",
+                               },
+                               expectedData{
+                                       rate:     0.00098,
+                                       utxoHash: "841b1de7c871dfe6e2d1886809d9ae12ec45e570233b03879305232b096fda43",
+                               },
+                               expectedData{
+                                       rate:     0.00098,
                                        utxoHash: "a4bc534c267d35a9eafc25cd66e0cb270a2537a51186605b7f7591bc567ab4c6",
                                },
                                expectedData{
@@ -328,7 +562,7 @@ func TestSortOrderKey(t *testing.T) {
 
        for i, c := range cases {
                for _, order := range c.orders {
-                       key := calcOrderKey(order.FromAssetID, order.ToAssetID, calcUTXOHash(&order), order.Rate)
+                       key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate())
                        data, err := json.Marshal(order.Utxo)
                        if err != nil {
                                t.Fatal(err)
@@ -338,7 +572,6 @@ func TestSortOrderKey(t *testing.T) {
                }
 
                got := []expectedData{}
-
                itr := db.IteratorPrefixWithStart(nil, nil, false)
                for itr.Next() {
                        key := itr.Key()
@@ -347,9 +580,8 @@ func TestSortOrderKey(t *testing.T) {
                        copy(b[:], key[pos+8:])
                        utxoHash := bc.NewHash(b)
 
-                       rate := getRateFromOrderKey(key, ordersPrefix)
                        got = append(got, expectedData{
-                               rate:     rate,
+                               rate:     getRateFromOrderKey(key),
                                utxoHash: utxoHash.String(),
                        })
                }
@@ -358,7 +590,6 @@ func TestSortOrderKey(t *testing.T) {
                if !testutil.DeepEqual(c.want, got) {
                        t.Errorf("case %v: got recovery status, got: %v, want: %v.", i, got, c.want)
                }
-
        }
 }
 
@@ -378,1584 +609,210 @@ func TestMovStore(t *testing.T) {
                {
                        desc: "add order",
                        addOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
+                       },
+                       blockHeader: &types.BlockHeader{Height: 1, PreviousBlockHash: bc.Hash{V0: 524821139490765641, V1: 2484214155808702787, V2: 9108473449351508820, V3: 7972721253564512122}},
+                       wantOrders: []*common.Order{
+                               mockOrders[1],
+                               mockOrders[7],
+                               mockOrders[6],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[0],
+                       },
+                       wantTradePairs: []*common.TradePair{
+                               &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2, Count: 8},
+                       },
+                       wantDBState: &common.MovDatabaseState{Height: 1, Hash: &bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
+               },
+               {
+                       desc: "del some order",
+                       beforeOrders: []*common.Order{
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
+                       },
+                       beforeTradePairs: []*common.TradePair{
+                               &common.TradePair{
                                        FromAssetID: assetID1,
                                        ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
+                                       Count:       8,
                                },
-                               &common.Order{
+                       },
+                       beforeDBStatus: &common.MovDatabaseState{Height: 1, Hash: &bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
+                       delOrders: []*common.Order{
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                       },
+                       blockHeader: &types.BlockHeader{Height: 2, PreviousBlockHash: bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
+                       wantOrders: []*common.Order{
+                               mockOrders[7],
+                               mockOrders[6],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                       },
+                       wantTradePairs: []*common.TradePair{
+                               &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2, Count: 5},
+                       },
+                       wantDBState: &common.MovDatabaseState{Height: 2, Hash: &bc.Hash{V0: 3724755213446347384, V1: 158878632373345042, V2: 18283800951484248781, V3: 7520797730449067221}},
+               },
+               {
+                       desc: "del all order",
+                       beforeOrders: []*common.Order{
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
+                       },
+                       beforeTradePairs: []*common.TradePair{
+                               &common.TradePair{
                                        FromAssetID: assetID1,
                                        ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
+                                       Count:       8,
                                },
-                               &common.Order{
+                       },
+                       beforeDBStatus: &common.MovDatabaseState{Height: 1, Hash: &bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
+                       delOrders: []*common.Order{
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
+                       },
+                       blockHeader:    &types.BlockHeader{Height: 2, PreviousBlockHash: bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
+                       wantOrders:     []*common.Order{},
+                       wantTradePairs: []*common.TradePair{},
+                       wantDBState:    &common.MovDatabaseState{Height: 2, Hash: &bc.Hash{V0: 3724755213446347384, V1: 158878632373345042, V2: 18283800951484248781, V3: 7520797730449067221}},
+               },
+               {
+                       desc: "Add and delete the same trade pair", // Add and delete different transaction pairs
+                       beforeOrders: []*common.Order{
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
+                       },
+                       beforeTradePairs: []*common.TradePair{
+                               &common.TradePair{
                                        FromAssetID: assetID1,
                                        ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
+                                       Count:       8,
                                },
                        },
-                       blockHeader: &types.BlockHeader{Height: 1, PreviousBlockHash: bc.Hash{V0: 524821139490765641, V1: 2484214155808702787, V2: 9108473449351508820, V3: 7972721253564512122}},
+                       beforeDBStatus: &common.MovDatabaseState{Height: 1, Hash: &bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
+                       addOrders: []*common.Order{
+                               mockOrders[8],
+                               mockOrders[9],
+                       },
+                       delOrders: []*common.Order{
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
+                       },
+                       blockHeader: &types.BlockHeader{Height: 2, PreviousBlockHash: bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
                        wantOrders: []*common.Order{
-                               &common.Order{
+                               mockOrders[9],
+                               mockOrders[8],
+                       },
+                       wantTradePairs: []*common.TradePair{
+                               &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2, Count: 2},
+                       },
+                       wantDBState: &common.MovDatabaseState{Height: 2, Hash: &bc.Hash{V0: 3724755213446347384, V1: 158878632373345042, V2: 18283800951484248781, V3: 7520797730449067221}},
+               },
+               {
+                       desc: "Add and delete different transaction pairs",
+                       beforeOrders: []*common.Order{
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
+                               mockOrders[10],
+                               mockOrders[11],
+                       },
+                       beforeTradePairs: []*common.TradePair{
+                               &common.TradePair{
                                        FromAssetID: assetID1,
                                        ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
+                                       Count:       8,
                                },
-                               &common.Order{
-                                       FromAssetID: assetID1,
+                               &common.TradePair{
+                                       FromAssetID: assetID3,
                                        ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
+                                       Count:       1,
                                },
-                               &common.Order{
-                                       FromAssetID: assetID1,
+                               &common.TradePair{
+                                       FromAssetID: assetID4,
                                        ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       wantTradePairs: []*common.TradePair{
-                               &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2, Count: 8},
-                       },
-                       wantDBState: &common.MovDatabaseState{Height: 1, Hash: &bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
-               },
-               {
-                       desc: "del some order",
-                       beforeOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       beforeTradePairs: []*common.TradePair{
-                               &common.TradePair{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Count:       8,
+                                       Count:       1,
                                },
                        },
                        beforeDBStatus: &common.MovDatabaseState{Height: 1, Hash: &bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
+                       addOrders: []*common.Order{
+                               mockOrders[12],
+                               mockOrders[13],
+                               mockOrders[14],
+                       },
                        delOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
+                               mockOrders[10],
                        },
                        blockHeader: &types.BlockHeader{Height: 2, PreviousBlockHash: bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
                        wantOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
+                               mockOrders[11],
+                               mockOrders[12],
+                               mockOrders[13],
+                               mockOrders[14],
                        },
                        wantTradePairs: []*common.TradePair{
-                               &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2, Count: 5},
+                               &common.TradePair{FromAssetID: assetID4, ToAssetID: assetID2, Count: 2},
+                               &common.TradePair{FromAssetID: assetID5, ToAssetID: assetID2, Count: 1},
+                               &common.TradePair{FromAssetID: assetID6, ToAssetID: assetID2, Count: 1},
                        },
                        wantDBState: &common.MovDatabaseState{Height: 2, Hash: &bc.Hash{V0: 3724755213446347384, V1: 158878632373345042, V2: 18283800951484248781, V3: 7520797730449067221}},
                },
-               {
-                       desc: "del all order",
-                       beforeOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       beforeTradePairs: []*common.TradePair{
-                               &common.TradePair{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Count:       8,
-                               },
-                       },
-                       beforeDBStatus: &common.MovDatabaseState{Height: 1, Hash: &bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
-                       delOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       blockHeader:    &types.BlockHeader{Height: 2, PreviousBlockHash: bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
-                       wantOrders:     []*common.Order{},
-                       wantTradePairs: []*common.TradePair{},
-                       wantDBState:    &common.MovDatabaseState{Height: 2, Hash: &bc.Hash{V0: 3724755213446347384, V1: 158878632373345042, V2: 18283800951484248781, V3: 7520797730449067221}},
-               },
-               {
-                       desc: "Add and delete the same trade pair", //Add and delete different transaction pairs
-                       beforeOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       beforeTradePairs: []*common.TradePair{
-                               &common.TradePair{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Count:       8,
-                               },
-                       },
-                       beforeDBStatus: &common.MovDatabaseState{Height: 1, Hash: &bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
-                       addOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 1},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 2},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       delOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       blockHeader: &types.BlockHeader{Height: 2, PreviousBlockHash: bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
-                       wantOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 2},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 1},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       wantTradePairs: []*common.TradePair{
-                               &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2, Count: 2},
-                       },
-                       wantDBState: &common.MovDatabaseState{Height: 2, Hash: &bc.Hash{V0: 3724755213446347384, V1: 158878632373345042, V2: 18283800951484248781, V3: 7520797730449067221}},
-               },
-               {
-                       desc: "Add and delete different transaction pairs",
-                       beforeOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID3,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 33},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID4,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 34},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       beforeTradePairs: []*common.TradePair{
-                               &common.TradePair{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Count:       8,
-                               },
-                               &common.TradePair{
-                                       FromAssetID: assetID3,
-                                       ToAssetID:   assetID2,
-                                       Count:       1,
-                               },
-                               &common.TradePair{
-                                       FromAssetID: assetID4,
-                                       ToAssetID:   assetID2,
-                                       Count:       1,
-                               },
-                       },
-                       beforeDBStatus: &common.MovDatabaseState{Height: 1, Hash: &bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
-                       addOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID4,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 36},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID5,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 37},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID6,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 38},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       delOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID3,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 33},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       blockHeader: &types.BlockHeader{Height: 2, PreviousBlockHash: bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}},
-                       wantOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID4,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 34},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID4,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 36},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID5,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 37},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID6,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 38},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       wantTradePairs: []*common.TradePair{
-                               &common.TradePair{FromAssetID: assetID4, ToAssetID: assetID2, Count: 2},
-                               &common.TradePair{FromAssetID: assetID5, ToAssetID: assetID2, Count: 1},
-                               &common.TradePair{FromAssetID: assetID6, ToAssetID: assetID2, Count: 1},
-                       },
-                       wantDBState: &common.MovDatabaseState{Height: 2, Hash: &bc.Hash{V0: 3724755213446347384, V1: 158878632373345042, V2: 18283800951484248781, V3: 7520797730449067221}},
-               },
-       }
-
-       initBlockHeader := &types.BlockHeader{
-               Height:  0,
-               Version: 1,
-       }
-
-       height := initBlockHeader.Height
-       hash := initBlockHeader.Hash()
-
-       defer os.RemoveAll("temp")
-       for i, c := range cases {
-               testDB := dbm.NewDB("testdb", "leveldb", "temp")
-               movStore, err := NewMovStore(testDB, height, &hash)
-               if err != nil {
-                       t.Fatalf("case %d: NewMovStore error %v.", i, err)
-               }
-
-               batch := movStore.db.NewBatch()
-               tradePairsCnt := make(map[common.TradePair]int)
-               movStore.addOrders(batch, c.beforeOrders, tradePairsCnt)
-               if len(c.beforeOrders) > 0 {
-                       tradePairsCnt = make(map[common.TradePair]int)
-                       for _, tradePair := range c.beforeTradePairs {
-                               tradePairsCnt[*tradePair] = tradePair.Count
-                       }
-                       movStore.updateTradePairs(batch, tradePairsCnt)
-                       movStore.saveMovDatabaseState(batch, c.beforeDBStatus)
-               }
-               batch.Write()
-
-               if err := movStore.ProcessOrders(c.addOrders, c.delOrders, c.blockHeader); err != nil {
-                       t.Fatalf("case %d: ProcessOrders error %v.", i, err)
-               }
-
-               var gotOrders []*common.Order
-
-               tmp, err := movStore.ListOrders(&common.Order{FromAssetID: assetID1, ToAssetID: assetID2, Rate: 0})
-               if err != nil {
-                       t.Fatalf("case %d: ListOrders(assetID1 and assetID2) error %v.", i, err)
-               }
-
-               gotOrders = append(gotOrders, tmp...)
-
-               tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID3, ToAssetID: assetID2, Rate: 0})
-               if err != nil {
-                       t.Fatalf("case %d: ListOrders(assetID3 and assetID2)  error %v.", i, err)
-               }
-
-               gotOrders = append(gotOrders, tmp...)
-
-               tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID4, ToAssetID: assetID2, Rate: 0})
-               if err != nil {
-                       t.Fatalf("case %d: ListOrders(assetID4 and assetID2)  error %v.", i, err)
-               }
-
-               gotOrders = append(gotOrders, tmp...)
-
-               tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID5, ToAssetID: assetID2, Rate: 0})
-               if err != nil {
-                       t.Fatalf("case %d: ListOrders(assetID5 and assetID2)  error %v.", i, err)
-               }
-
-               gotOrders = append(gotOrders, tmp...)
-
-               tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID6, ToAssetID: assetID2, Rate: 0})
-               if err != nil {
-                       t.Fatalf("case %d: ListOrders(assetID6 and assetID2)  error %v.", i, err)
-               }
-
-               gotOrders = append(gotOrders, tmp...)
-
-               if !testutil.DeepEqual(gotOrders, c.wantOrders) {
-                       t.Fatalf("case %d: got orders , gotOrders: %v, wantOrders: %v.", i, gotOrders, c.wantOrders)
-               }
-
-               gotTradePairs, err := movStore.ListTradePairsWithStart(nil, nil)
-               if err != nil {
-                       t.Fatalf("case %d: ListTradePairsWithStart error %v.", i, err)
-               }
-
-               if !testutil.DeepEqual(gotTradePairs, c.wantTradePairs) {
-                       t.Fatalf("case %d: got tradePairs, gotTradePairs: %v, wantTradePairs: %v.", i, gotTradePairs, c.wantTradePairs)
-               }
-
-               gotDBState, err := movStore.GetMovDatabaseState()
-               if err != nil {
-                       t.Fatalf("case %d: GetMovDatabaseState error %v.", i, err)
-               }
-
-               if !testutil.DeepEqual(gotDBState, c.wantDBState) {
-                       t.Fatalf("case %d: got tradePairs, gotDBState: %v, wantDBStatus: %v.", i, gotDBState, c.wantDBState)
-               }
-
-               testDB.Close()
-               os.RemoveAll("temp")
-       }
-}
-
-func TestListOrders(t *testing.T) {
-       cases := []struct {
-               desc        string
-               storeOrders []*common.Order
-               query       *common.Order
-               wantOrders  []*common.Order
-       }{
-               {
-                       desc:       "empty",
-                       query:      &common.Order{FromAssetID: assetID1, ToAssetID: assetID2},
-                       wantOrders: []*common.Order{},
-               },
-               {
-                       desc: "query from first",
-                       storeOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       query: &common.Order{FromAssetID: assetID1, ToAssetID: assetID2},
-                       wantOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-               },
-               {
-                       desc: "query from middle",
-                       storeOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       query: &common.Order{
-                               FromAssetID: assetID1,
-                               ToAssetID:   assetID2,
-                               Rate:        0.00098,
-                               Utxo: &common.MovUtxo{
-                                       SourceID:       &bc.Hash{V0: 13},
-                                       Amount:         1,
-                                       SourcePos:      0,
-                                       ControlProgram: []byte("aa"),
-                               },
-                       },
-                       wantOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-               },
        }
 
        initBlockHeader := &types.BlockHeader{
@@ -1969,404 +826,239 @@ func TestListOrders(t *testing.T) {
        defer os.RemoveAll("temp")
        for i, c := range cases {
                testDB := dbm.NewDB("testdb", "leveldb", "temp")
-               movStore, err := NewMovStore(testDB, height, &hash)
-               if err != nil {
-                       t.Fatalf("case %d: NewMovStore error %v.", i, err)
+               movStore := NewLevelDBMovStore(testDB)
+               if err := movStore.InitDBState(height, &hash); err != nil {
+                       t.Fatalf("case %d: InitDBState error %v.", i, err)
                }
 
                batch := movStore.db.NewBatch()
-               tradePairsCnt := make(map[common.TradePair]int)
-               movStore.addOrders(batch, c.storeOrders, tradePairsCnt)
-               movStore.updateTradePairs(batch, tradePairsCnt)
+               tradePairsCnt := make(map[string]*common.TradePair)
+               movStore.addOrders(batch, c.beforeOrders, tradePairsCnt)
+               if len(c.beforeOrders) > 0 {
+                       tradePairsCnt = make(map[string]*common.TradePair)
+                       for _, tradePair := range c.beforeTradePairs {
+                               tradePairsCnt[tradePair.Key()] = tradePair
+                       }
+                       movStore.updateTradePairs(batch, tradePairsCnt)
+                       movStore.saveMovDatabaseState(batch, c.beforeDBStatus)
+               }
                batch.Write()
 
-               gotOrders, err := movStore.ListOrders(c.query)
+               if err := movStore.ProcessOrders(c.addOrders, c.delOrders, c.blockHeader); err != nil {
+                       t.Fatalf("case %d: ProcessOrders error %v.", i, err)
+               }
+
+               var gotOrders []*common.Order
+
+               tmp, err := movStore.ListOrders(&common.Order{FromAssetID: assetID1, ToAssetID: assetID2, RatioNumerator: 0, RatioDenominator: 1})
                if err != nil {
-                       t.Fatalf("case %d: ListOrders error %v.", i, err)
+                       t.Fatalf("case %d: ListOrders(assetID1 and assetID2) error %v.", i, err)
                }
 
-               if !testutil.DeepEqual(gotOrders, c.wantOrders) {
-                       t.Fatalf("case %d: got orders , gotOrders: %v, wantOrders: %v.", i, gotOrders, c.wantOrders)
+               gotOrders = append(gotOrders, tmp...)
+
+               tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID3, ToAssetID: assetID2, RatioNumerator: 0, RatioDenominator: 1})
+               if err != nil {
+                       t.Fatalf("case %d: ListOrders(assetID3 and assetID2)  error %v.", i, err)
                }
 
-               testDB.Close()
-               os.RemoveAll("temp")
-       }
-}
+               gotOrders = append(gotOrders, tmp...)
 
-func TestAddOrders(t *testing.T) {
-       cases := []struct {
-               desc         string
-               beforeOrders []*common.Order
-               addOrders    []*common.Order
-               wantOrders   []*common.Order
-       }{
-               {
-                       desc: "empty",
-                       addOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                       },
-                       wantOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
+               tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID4, ToAssetID: assetID2, RatioNumerator: 0, RatioDenominator: 1})
+               if err != nil {
+                       t.Fatalf("case %d: ListOrders(assetID4 and assetID2)  error %v.", i, err)
+               }
+
+               gotOrders = append(gotOrders, tmp...)
+
+               tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID5, ToAssetID: assetID2, RatioNumerator: 0, RatioDenominator: 1})
+               if err != nil {
+                       t.Fatalf("case %d: ListOrders(assetID5 and assetID2)  error %v.", i, err)
+               }
+
+               gotOrders = append(gotOrders, tmp...)
+
+               tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID6, ToAssetID: assetID2, RatioNumerator: 0, RatioDenominator: 1})
+               if err != nil {
+                       t.Fatalf("case %d: ListOrders(assetID6 and assetID2)  error %v.", i, err)
+               }
+
+               gotOrders = append(gotOrders, tmp...)
+
+               if !testutil.DeepEqual(gotOrders, c.wantOrders) {
+                       t.Fatalf("case %d: got orders , gotOrders: %v, wantOrders: %v.", i, gotOrders, c.wantOrders)
+               }
+
+               gotTradePairs, err := movStore.ListTradePairsWithStart(nil, nil)
+               if err != nil {
+                       t.Fatalf("case %d: ListTradePairsWithStart error %v.", i, err)
+               }
+
+               if !testutil.DeepEqual(gotTradePairs, c.wantTradePairs) {
+                       t.Fatalf("case %d: got tradePairs, gotTradePairs: %v, wantTradePairs: %v.", i, gotTradePairs, c.wantTradePairs)
+               }
+
+               gotDBState, err := movStore.GetMovDatabaseState()
+               if err != nil {
+                       t.Fatalf("case %d: GetMovDatabaseState error %v.", i, err)
+               }
+
+               if !testutil.DeepEqual(gotDBState, c.wantDBState) {
+                       t.Fatalf("case %d: got tradePairs, gotDBState: %v, wantDBStatus: %v.", i, gotDBState, c.wantDBState)
+               }
+
+               testDB.Close()
+               os.RemoveAll("temp")
+       }
+}
+
+func TestListOrders(t *testing.T) {
+       cases := []struct {
+               desc        string
+               storeOrders []*common.Order
+               query       *common.Order
+               wantOrders  []*common.Order
+       }{
+               {
+                       desc:       "empty",
+                       query:      &common.Order{FromAssetID: assetID1, ToAssetID: assetID2},
+                       wantOrders: []*common.Order{},
+               },
+               {
+                       desc: "query from first",
+                       storeOrders: []*common.Order{
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
+                               mockOrders[10],
+                       },
+                       query: &common.Order{FromAssetID: assetID1, ToAssetID: assetID2},
+                       wantOrders: []*common.Order{
+                               mockOrders[1],
+                               mockOrders[7],
+                               mockOrders[6],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[0],
                        },
                },
                {
-                       desc: "Stored data already exists",
-                       beforeOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
+                       desc: "query from middle",
+                       storeOrders: []*common.Order{
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
+                       },
+                       query: mockOrders[3],
+                       wantOrders: []*common.Order{
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[0],
                        },
+               },
+       }
+
+       initBlockHeader := &types.BlockHeader{
+               Height:  0,
+               Version: 1,
+       }
+
+       height := initBlockHeader.Height
+       hash := initBlockHeader.Hash()
+
+       defer os.RemoveAll("temp")
+       for i, c := range cases {
+               testDB := dbm.NewDB("testdb", "leveldb", "temp")
+               movStore := NewLevelDBMovStore(testDB)
+               if err := movStore.InitDBState(height, &hash); err != nil {
+                       t.Fatalf("case %d: InitDBState error %v.", i, err)
+               }
+
+               batch := movStore.db.NewBatch()
+               tradePairsCnt := make(map[string]*common.TradePair)
+               movStore.addOrders(batch, c.storeOrders, tradePairsCnt)
+               movStore.updateTradePairs(batch, tradePairsCnt)
+               batch.Write()
+
+               gotOrders, err := movStore.ListOrders(c.query)
+               if err != nil {
+                       t.Fatalf("case %d: ListOrders error %v.", i, err)
+               }
+
+               if !testutil.DeepEqual(gotOrders, c.wantOrders) {
+                       t.Fatalf("case %d: got orders , gotOrders: %v, wantOrders: %v.", i, gotOrders, c.wantOrders)
+               }
+
+               testDB.Close()
+               os.RemoveAll("temp")
+       }
+}
+
+func TestAddOrders(t *testing.T) {
+       cases := []struct {
+               desc         string
+               beforeOrders []*common.Order
+               addOrders    []*common.Order
+               wantOrders   []*common.Order
+       }{
+               {
+                       desc: "empty",
                        addOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
                        },
-                       wantOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
+                       wantOrders: []*common.Order{
+                               mockOrders[1],
+                               mockOrders[7],
+                               mockOrders[6],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[0],
+                       },
+               },
+               {
+                       desc: "Stored data already exists",
+                       beforeOrders: []*common.Order{
+                               mockOrders[0],
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                       },
+                       addOrders: []*common.Order{
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
+                       },
+                       wantOrders: []*common.Order{
+                               mockOrders[1],
+                               mockOrders[7],
+                               mockOrders[6],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[0],
                        },
                },
        }
@@ -2382,18 +1074,18 @@ func TestAddOrders(t *testing.T) {
        defer os.RemoveAll("temp")
        for i, c := range cases {
                testDB := dbm.NewDB("testdb", "leveldb", "temp")
-               movStore, err := NewMovStore(testDB, height, &hash)
-               if err != nil {
-                       t.Fatalf("case %d: NewMovStore error %v.", i, err)
+               movStore := NewLevelDBMovStore(testDB)
+               if err := movStore.InitDBState(height, &hash); err != nil {
+                       t.Fatalf("case %d: InitDBState error %v.", i, err)
                }
 
                batch := movStore.db.NewBatch()
-               tradePairsCnt := make(map[common.TradePair]int)
+               tradePairsCnt := make(map[string]*common.TradePair)
                movStore.addOrders(batch, c.beforeOrders, tradePairsCnt)
                movStore.updateTradePairs(batch, tradePairsCnt)
                batch.Write()
 
-               tradePairsCnt = make(map[common.TradePair]int)
+               tradePairsCnt = make(map[string]*common.TradePair)
                movStore.addOrders(batch, c.addOrders, tradePairsCnt)
                batch.Write()
 
@@ -2422,28 +1114,8 @@ func TestDelOrders(t *testing.T) {
                {
                        desc: "empty",
                        delOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
+                               mockOrders[0],
+                               mockOrders[1],
                        },
                        wantOrders: []*common.Order{},
                        err:        errors.New("don't find trade pair"),
@@ -2451,260 +1123,40 @@ func TestDelOrders(t *testing.T) {
                {
                        desc: "Delete existing data",
                        beforeOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
+                               mockOrders[1],
+                               mockOrders[7],
+                               mockOrders[6],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[0],
                        },
                        delOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         10,
-                                               SourcePos:      1,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
+                               mockOrders[4],
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
                        },
                        wantOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 22},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00097,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 23},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00098,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 13},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        1.00090,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 21},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
+                               mockOrders[1],
+                               mockOrders[2],
+                               mockOrders[3],
+                               mockOrders[0],
                        },
                        err: nil,
                },
                {
                        desc: "Delete all data",
                        beforeOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
+                               mockOrders[7],
+                               mockOrders[6],
+                               mockOrders[5],
                        },
                        delOrders: []*common.Order{
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00099,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 24},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00096,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 25},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
-                               &common.Order{
-                                       FromAssetID: assetID1,
-                                       ToAssetID:   assetID2,
-                                       Rate:        0.00095,
-                                       Utxo: &common.MovUtxo{
-                                               SourceID:       &bc.Hash{V0: 26},
-                                               Amount:         1,
-                                               SourcePos:      0,
-                                               ControlProgram: []byte("aa"),
-                                       },
-                               },
+                               mockOrders[5],
+                               mockOrders[6],
+                               mockOrders[7],
                        },
                        wantOrders: []*common.Order{},
                        err:        nil,
@@ -2722,18 +1174,18 @@ func TestDelOrders(t *testing.T) {
        defer os.RemoveAll("temp")
        for i, c := range cases {
                testDB := dbm.NewDB("testdb", "leveldb", "temp")
-               movStore, err := NewMovStore(testDB, height, &hash)
-               if err != nil {
-                       t.Fatalf("case %d: NewMovStore error %v.", i, err)
+               movStore := NewLevelDBMovStore(testDB)
+               if err := movStore.InitDBState(height, &hash); err != nil {
+                       t.Fatalf("case %d: InitDBState error %v.", i, err)
                }
 
                batch := movStore.db.NewBatch()
-               tradePairsCnt := make(map[common.TradePair]int)
+               tradePairsCnt := make(map[string]*common.TradePair)
                movStore.addOrders(batch, c.beforeOrders, tradePairsCnt)
                movStore.updateTradePairs(batch, tradePairsCnt)
                batch.Write()
 
-               tradePairsCnt = make(map[common.TradePair]int)
+               tradePairsCnt = make(map[string]*common.TradePair)
                movStore.deleteOrders(batch, c.delOrders, tradePairsCnt)
                movStore.updateTradePairs(batch, tradePairsCnt)
                batch.Write()
@@ -2755,7 +1207,7 @@ func TestDelOrders(t *testing.T) {
 func TestListTradePairsWithStart(t *testing.T) {
        cases := []struct {
                desc            string
-               storeTradePairs map[common.TradePair]int
+               storeTradePairs map[string]*common.TradePair
                query           *common.TradePair
                wantTradePairs  []*common.TradePair
        }{
@@ -2766,13 +1218,13 @@ func TestListTradePairsWithStart(t *testing.T) {
                },
                {
                        desc: "query from first",
-                       storeTradePairs: map[common.TradePair]int{
-                               common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2}: 1,
-                               common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3}: 2,
-                               common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4}: 3,
-                               common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5}: 4,
-                               common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6}: 5,
-                               common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7}: 6,
+                       storeTradePairs: map[string]*common.TradePair{
+                               (&common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2}).Key(): {FromAssetID: assetID1, ToAssetID: assetID2, Count: 1},
+                               (&common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3}).Key(): {FromAssetID: assetID2, ToAssetID: assetID3, Count: 2},
+                               (&common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4}).Key(): {FromAssetID: assetID3, ToAssetID: assetID4, Count: 3},
+                               (&common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5}).Key(): {FromAssetID: assetID4, ToAssetID: assetID5, Count: 4},
+                               (&common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6}).Key(): {FromAssetID: assetID4, ToAssetID: assetID6, Count: 5},
+                               (&common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7}).Key(): {FromAssetID: assetID5, ToAssetID: assetID7, Count: 6},
                        },
                        query: &common.TradePair{},
                        wantTradePairs: []*common.TradePair{
@@ -2786,14 +1238,14 @@ func TestListTradePairsWithStart(t *testing.T) {
                },
                {
                        desc: "query from middle",
-                       storeTradePairs: map[common.TradePair]int{
-                               common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2}: 1,
-                               common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3}: 2,
-                               common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4}: 3,
-                               common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5}: 4,
-                               common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6}: 5,
-                               common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7}: 6,
-                               common.TradePair{FromAssetID: assetID6, ToAssetID: assetID8}: 7,
+                       storeTradePairs: map[string]*common.TradePair{
+                               (&common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2}).Key(): {FromAssetID: assetID1, ToAssetID: assetID2, Count: 1},
+                               (&common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3}).Key(): {FromAssetID: assetID2, ToAssetID: assetID3, Count: 2},
+                               (&common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4}).Key(): {FromAssetID: assetID3, ToAssetID: assetID4, Count: 3},
+                               (&common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5}).Key(): {FromAssetID: assetID4, ToAssetID: assetID5, Count: 4},
+                               (&common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6}).Key(): {FromAssetID: assetID4, ToAssetID: assetID6, Count: 5},
+                               (&common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7}).Key(): {FromAssetID: assetID5, ToAssetID: assetID7, Count: 6},
+                               (&common.TradePair{FromAssetID: assetID6, ToAssetID: assetID8}).Key(): {FromAssetID: assetID6, ToAssetID: assetID8, Count: 7},
                        },
                        query: &common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4, Count: 3},
                        wantTradePairs: []*common.TradePair{
@@ -2816,9 +1268,9 @@ func TestListTradePairsWithStart(t *testing.T) {
        defer os.RemoveAll("temp")
        for i, c := range cases {
                testDB := dbm.NewDB("testdb", "leveldb", "temp")
-               movStore, err := NewMovStore(testDB, height, &hash)
-               if err != nil {
-                       t.Fatalf("case %d: NewMovStore error %v.", i, err)
+               movStore := NewLevelDBMovStore(testDB)
+               if err := movStore.InitDBState(height, &hash); err != nil {
+                       t.Fatalf("case %d: InitDBState error %v.", i, err)
                }
 
                batch := movStore.db.NewBatch()
@@ -2842,20 +1294,20 @@ func TestListTradePairsWithStart(t *testing.T) {
 func TestUpdateTradePairs(t *testing.T) {
        cases := []struct {
                desc             string
-               beforeTradePairs map[common.TradePair]int
-               addTradePairs    map[common.TradePair]int
-               delTradePairs    map[common.TradePair]int
+               beforeTradePairs map[string]*common.TradePair
+               addTradePairs    map[string]*common.TradePair
+               delTradePairs    map[string]*common.TradePair
                wantTradePairs   []*common.TradePair
        }{
                {
                        desc: "empty",
-                       addTradePairs: map[common.TradePair]int{
-                               common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2}: 1,
-                               common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3}: 2,
-                               common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4}: 3,
-                               common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5}: 4,
-                               common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6}: 5,
-                               common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7}: 6,
+                       addTradePairs: map[string]*common.TradePair{
+                               (&common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2}).Key(): {FromAssetID: assetID1, ToAssetID: assetID2, Count: 1},
+                               (&common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3}).Key(): {FromAssetID: assetID2, ToAssetID: assetID3, Count: 2},
+                               (&common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4}).Key(): {FromAssetID: assetID3, ToAssetID: assetID4, Count: 3},
+                               (&common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5}).Key(): {FromAssetID: assetID4, ToAssetID: assetID5, Count: 4},
+                               (&common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6}).Key(): {FromAssetID: assetID4, ToAssetID: assetID6, Count: 5},
+                               (&common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7}).Key(): {FromAssetID: assetID5, ToAssetID: assetID7, Count: 6},
                        },
                        wantTradePairs: []*common.TradePair{
                                &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2, Count: 1},
@@ -2868,15 +1320,15 @@ func TestUpdateTradePairs(t *testing.T) {
                },
                {
                        desc: "Stored data already exists",
-                       beforeTradePairs: map[common.TradePair]int{
-                               common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2}: 1,
-                               common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3}: 2,
-                               common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4}: 3,
+                       beforeTradePairs: map[string]*common.TradePair{
+                               (&common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2}).Key(): {FromAssetID: assetID1, ToAssetID: assetID2, Count: 1},
+                               (&common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3}).Key(): {FromAssetID: assetID2, ToAssetID: assetID3, Count: 2},
+                               (&common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4}).Key(): {FromAssetID: assetID3, ToAssetID: assetID4, Count: 3},
                        },
-                       addTradePairs: map[common.TradePair]int{
-                               common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5}: 4,
-                               common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6}: 5,
-                               common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7}: 6,
+                       addTradePairs: map[string]*common.TradePair{
+                               (&common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5}).Key(): {FromAssetID: assetID4, ToAssetID: assetID5, Count: 4},
+                               (&common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6}).Key(): {FromAssetID: assetID4, ToAssetID: assetID6, Count: 5},
+                               (&common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7}).Key(): {FromAssetID: assetID5, ToAssetID: assetID7, Count: 6},
                        },
                        wantTradePairs: []*common.TradePair{
                                &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2, Count: 1},
@@ -2889,19 +1341,19 @@ func TestUpdateTradePairs(t *testing.T) {
                },
                {
                        desc: "delete some data",
-                       beforeTradePairs: map[common.TradePair]int{
-                               common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2}: 1,
-                               common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3}: 2,
-                               common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4}: 3,
-                               common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5}: 4,
-                               common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6}: 5,
-                               common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7}: 6,
-                       },
-                       delTradePairs: map[common.TradePair]int{
-                               common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2}: -1,
-                               common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5}: -4,
-                               common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6}: -2,
-                               common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7}: -4,
+                       beforeTradePairs: map[string]*common.TradePair{
+                               (&common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2}).Key(): {FromAssetID: assetID1, ToAssetID: assetID2, Count: 1},
+                               (&common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3}).Key(): {FromAssetID: assetID2, ToAssetID: assetID3, Count: 2},
+                               (&common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4}).Key(): {FromAssetID: assetID3, ToAssetID: assetID4, Count: 3},
+                               (&common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5}).Key(): {FromAssetID: assetID4, ToAssetID: assetID5, Count: 4},
+                               (&common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6}).Key(): {FromAssetID: assetID4, ToAssetID: assetID6, Count: 5},
+                               (&common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7}).Key(): {FromAssetID: assetID5, ToAssetID: assetID7, Count: 6},
+                       },
+                       delTradePairs: map[string]*common.TradePair{
+                               (&common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2}).Key(): {FromAssetID: assetID1, ToAssetID: assetID2, Count: -1},
+                               (&common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5}).Key(): {FromAssetID: assetID4, ToAssetID: assetID5, Count: -4},
+                               (&common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6}).Key(): {FromAssetID: assetID4, ToAssetID: assetID6, Count: -2},
+                               (&common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7}).Key(): {FromAssetID: assetID5, ToAssetID: assetID7, Count: -4},
                        },
                        wantTradePairs: []*common.TradePair{
                                &common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3, Count: 2},
@@ -2923,9 +1375,9 @@ func TestUpdateTradePairs(t *testing.T) {
        defer os.RemoveAll("temp")
        for i, c := range cases {
                testDB := dbm.NewDB("testdb", "leveldb", "temp")
-               movStore, err := NewMovStore(testDB, height, &hash)
-               if err != nil {
-                       t.Fatalf("case %d: NewMovStore error %v.", i, err)
+               movStore := NewLevelDBMovStore(testDB)
+               if err := movStore.InitDBState(height, &hash); err != nil {
+                       t.Fatalf("case %d: InitDBState error %v.", i, err)
                }
 
                batch := movStore.db.NewBatch()
@@ -2995,9 +1447,9 @@ func TestCheckMovDatabaseState(t *testing.T) {
        defer os.RemoveAll("temp")
        for i, c := range cases {
                testDB := dbm.NewDB("testdb", "leveldb", "temp")
-               movStore, err := NewMovStore(testDB, height, &hash)
-               if err != nil {
-                       t.Fatalf("case %d: NewMovStore error %v.", i, err)
+               movStore := NewLevelDBMovStore(testDB)
+               if err := movStore.InitDBState(height, &hash); err != nil {
+                       t.Fatalf("case %d: InitDBState error %v.", i, err)
                }
 
                batch := movStore.db.NewBatch()
diff --git a/application/mov/match/match.go b/application/mov/match/match.go
new file mode 100644 (file)
index 0000000..4c0234f
--- /dev/null
@@ -0,0 +1,310 @@
+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 {
+       orderBook   *OrderBook
+       maxFeeRate  float64
+       nodeProgram []byte
+}
+
+// NewEngine return a new Engine
+func NewEngine(orderBook *OrderBook, maxFeeRate float64, nodeProgram []byte) *Engine {
+       return &Engine{orderBook: orderBook, 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.orderBook.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(sortOrders(e.orderBook.PeekOrders(tradePairs)))
+       if err != nil {
+               return nil, err
+       }
+
+       for _, tradePair := range tradePairs {
+               e.orderBook.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.orderBook.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
+}
+
+// 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 {
+       sortedOrders := sortOrders(orders)
+       if len(sortedOrders) == 0 {
+               return false
+       }
+
+       rate := orderRatio(sortedOrders[0])
+       oppositeRate := big.NewFloat(0).SetInt64(1)
+       for i := 1; i < len(sortedOrders); i++ {
+               oppositeRate.Mul(oppositeRate, orderRatio(sortedOrders[i]))
+       }
+
+       one := big.NewFloat(0).SetInt64(1)
+       return one.Quo(one, rate).Cmp(oppositeRate) >= 0
+}
+
+func orderRatio(order *common.Order) *big.Float {
+       ratio := big.NewFloat(0).SetInt64(order.RatioNumerator)
+       ratio.Quo(ratio, big.NewFloat(0).SetInt64(order.RatioDenominator))
+       return ratio
+}
+
+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")
+       }
+
+       assetMap := make(map[string]bool)
+       for _, tradePair := range tradePairs {
+               assetMap[tradePair.FromAssetID.String()] = true
+               if *tradePair.FromAssetID == *tradePair.ToAssetID {
+                       return errors.New("from asset id can't equal to asset id")
+               }
+       }
+
+       for _, tradePair := range tradePairs {
+               key := tradePair.ToAssetID.String()
+               if _, ok := assetMap[key]; !ok {
+                       return errors.New("invalid trade pairs")
+               }
+               delete(assetMap, key)
+       }
+       return nil
+}
+
+func sortOrders(orders []*common.Order) []*common.Order {
+       orderMap := make(map[bc.AssetID]*common.Order)
+       firstOrder := orders[0]
+       for i := 1; i < len(orders); i++ {
+               orderMap[*orders[i].FromAssetID] = orders[i]
+       }
+
+       sortedOrders := []*common.Order{firstOrder}
+       for order := firstOrder; *order.ToAssetID != *firstOrder.FromAssetID; {
+               nextOrder, ok := orderMap[*order.ToAssetID]
+               if !ok {
+                       return nil
+               }
+
+               sortedOrders = append(sortedOrders, nextOrder)
+               order = nextOrder
+       }
+       return sortedOrders
+}
diff --git a/application/mov/match/match_test.go b/application/mov/match/match_test.go
new file mode 100644 (file)
index 0000000..640f4cf
--- /dev/null
@@ -0,0 +1,258 @@
+package match
+
+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/protocol/bc/types"
+       "github.com/bytom/vapor/protocol/validation"
+       "github.com/bytom/vapor/testutil"
+)
+
+func TestGenerateMatchedTxs(t *testing.T) {
+       btc2eth := &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH}
+       eth2btc := &common.TradePair{FromAssetID: &mock.ETH, ToAssetID: &mock.BTC}
+       eth2eos := &common.TradePair{FromAssetID: &mock.ETH, ToAssetID: &mock.EOS}
+       eos2btc := &common.TradePair{FromAssetID: &mock.EOS, ToAssetID: &mock.BTC}
+
+       cases := []struct {
+               desc            string
+               tradePairs      []*common.TradePair
+               initStoreOrders []*common.Order
+               wantMatchedTxs  []*types.Tx
+       }{
+               {
+                       desc:       "full matched",
+                       tradePairs: []*common.TradePair{btc2eth, eth2btc},
+                       initStoreOrders: []*common.Order{
+                               mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
+                               mock.Eth2BtcOrders[0],
+                       },
+                       wantMatchedTxs: []*types.Tx{
+                               mock.MatchedTxs[1],
+                       },
+               },
+               {
+                       desc:       "partial matched",
+                       tradePairs: []*common.TradePair{btc2eth, eth2btc},
+                       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",
+                       tradePairs: []*common.TradePair{btc2eth, eth2btc},
+                       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",
+                       tradePairs: []*common.TradePair{btc2eth, eth2btc},
+                       initStoreOrders: []*common.Order{
+                               mock.Btc2EthOrders[1],
+                               mock.Eth2BtcOrders[0],
+                       },
+                       wantMatchedTxs: []*types.Tx{},
+               },
+               {
+                       desc:       "cycle match",
+                       tradePairs: []*common.TradePair{btc2eth, eth2eos, eos2btc},
+                       initStoreOrders: []*common.Order{
+                               mock.Btc2EthOrders[0], mock.Eth2EosOrders[0], mock.Eos2BtcOrders[0],
+                       },
+                       wantMatchedTxs: []*types.Tx{
+                               mock.MatchedTxs[6],
+                       },
+               },
+       }
+
+       for i, c := range cases {
+               movStore := mock.NewMovStore([]*common.TradePair{btc2eth, eth2btc}, c.initStoreOrders)
+               matchEngine := NewEngine(NewOrderBook(movStore, nil, nil), 0.05, mock.NodeProgram)
+               var gotMatchedTxs []*types.Tx
+               for matchEngine.HasMatchedTx(c.tradePairs...) {
+                       matchedTx, err := matchEngine.NextMatchedTx(c.tradePairs...)
+                       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 {
+                       if _, err := validation.ValidateTx(gotMatchedTx.Tx, &bc.Block{BlockHeader: &bc.BlockHeader{Version: 1}}); err != nil {
+                               t.Fatal(err)
+                       }
+
+                       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)
+               }
+       }
+}
+
+func TestValidateTradePairs(t *testing.T) {
+       cases := []struct {
+               desc       string
+               tradePairs []*common.TradePair
+               wantError  bool
+       }{
+               {
+                       desc: "valid case of two trade pairs",
+                       tradePairs: []*common.TradePair{
+                               {
+                                       FromAssetID: &mock.BTC,
+                                       ToAssetID:   &mock.ETH,
+                               },
+                               {
+                                       FromAssetID: &mock.ETH,
+                                       ToAssetID:   &mock.BTC,
+                               },
+                       },
+                       wantError: false,
+               },
+               {
+                       desc: "invalid case of two trade pairs",
+                       tradePairs: []*common.TradePair{
+                               {
+                                       FromAssetID: &mock.BTC,
+                                       ToAssetID:   &mock.ETH,
+                               },
+                               {
+                                       FromAssetID: &mock.ETH,
+                                       ToAssetID:   &mock.EOS,
+                               },
+                       },
+                       wantError: true,
+               },
+               {
+                       desc: "valid case of three trade pairs",
+                       tradePairs: []*common.TradePair{
+                               {
+                                       FromAssetID: &mock.BTC,
+                                       ToAssetID:   &mock.ETH,
+                               },
+                               {
+                                       FromAssetID: &mock.ETH,
+                                       ToAssetID:   &mock.EOS,
+                               },
+                               {
+                                       FromAssetID: &mock.EOS,
+                                       ToAssetID:   &mock.BTC,
+                               },
+                       },
+                       wantError: false,
+               },
+               {
+                       desc: "invalid case of three trade pairs",
+                       tradePairs: []*common.TradePair{
+                               {
+                                       FromAssetID: &mock.BTC,
+                                       ToAssetID:   &mock.ETH,
+                               },
+                               {
+                                       FromAssetID: &mock.ETH,
+                                       ToAssetID:   &mock.BTC,
+                               },
+                               {
+                                       FromAssetID: &mock.BTC,
+                                       ToAssetID:   &mock.BTC,
+                               },
+                       },
+                       wantError: true,
+               },
+               {
+                       desc: "valid case 2 of three trade pairs",
+                       tradePairs: []*common.TradePair{
+                               {
+                                       FromAssetID: &mock.BTC,
+                                       ToAssetID:   &mock.ETH,
+                               },
+                               {
+                                       FromAssetID: &mock.EOS,
+                                       ToAssetID:   &mock.BTC,
+                               },
+                               {
+                                       FromAssetID: &mock.ETH,
+                                       ToAssetID:   &mock.EOS,
+                               },
+                       },
+                       wantError: false,
+               },
+       }
+
+       for i, c := range cases {
+               err := validateTradePairs(c.tradePairs)
+               if c.wantError && err == nil {
+                       t.Errorf("#%d(%s): want error, got no error", i, c.desc)
+               }
+
+               if !c.wantError && err != nil {
+                       t.Errorf("#%d(%s): want no error, got error (%v)", i, c.desc, err)
+               }
+       }
+}
diff --git a/application/mov/match/order_book.go b/application/mov/match/order_book.go
new file mode 100644 (file)
index 0000000..f4cd852
--- /dev/null
@@ -0,0 +1,152 @@
+package match
+
+import (
+       "sort"
+
+       "github.com/bytom/vapor/application/mov/common"
+       "github.com/bytom/vapor/application/mov/database"
+       "github.com/bytom/vapor/errors"
+)
+
+// OrderBook is used to handle the mov orders in memory like stack
+type OrderBook 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
+}
+
+// NewOrderBook create a new OrderBook object
+func NewOrderBook(movStore database.MovStore, arrivalAddOrders, arrivalDelOrders []*common.Order) *OrderBook {
+       return &OrderBook{
+               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 *OrderBook) 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 *OrderBook) 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
+}
+
+// PeekOrders return the next lowest orders by given array of trade pairs
+func (o *OrderBook) PeekOrders(tradePairs []*common.TradePair) []*common.Order {
+       var orders []*common.Order
+       for _, tradePair := range tradePairs {
+               order := o.PeekOrder(tradePair)
+               if order == nil {
+                       return nil
+               }
+
+               orders = append(orders, order)
+       }
+       return orders
+}
+
+// PopOrder delete the next lowest order of given trade pair
+func (o *OrderBook) 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]
+       }
+}
+
+// PopOrders delete the next lowest orders by given trade pairs
+func (o *OrderBook) PopOrders(tradePairs []*common.TradePair) []*common.Order {
+       var orders []*common.Order
+       for _, tradePair := range tradePairs {
+               o.PopOrder(tradePair)
+       }
+       return orders
+}
+
+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 *OrderBook) 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 *OrderBook) peekArrivalOrder(tradePair *common.TradePair) *common.Order {
+       if arrivalAddOrders := o.arrivalAddOrders[tradePair.Key()]; len(arrivalAddOrders) > 0 {
+               return arrivalAddOrders[len(arrivalAddOrders)-1]
+       }
+       return nil
+}
diff --git a/application/mov/match/order_book_test.go b/application/mov/match/order_book_test.go
new file mode 100644 (file)
index 0000000..e401970
--- /dev/null
@@ -0,0 +1,195 @@
+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 TestOrderBook(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 {
+               orderBook := NewOrderBook(c.initMovStore, c.initArrivalAddOrders, c.initArrivalDelOrders)
+               for _, order := range c.addOrders {
+                       if err := orderBook.AddOrder(order); err != nil {
+                               t.Fatal(err)
+                       }
+               }
+
+               for _, tradePair := range c.popOrders {
+                       orderBook.PopOrder(tradePair)
+               }
+
+               for tradePair, wantOrder := range c.wantPeekedOrders {
+                       gotOrder := orderBook.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)
+                       }
+               }
+       }
+}
diff --git a/application/mov/mock/mock.go b/application/mov/mock/mock.go
new file mode 100644 (file)
index 0000000..b810795
--- /dev/null
@@ -0,0 +1,337 @@
+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})
+       EOS         = bc.NewAssetID([32]byte{3})
+       ETC         = bc.NewAssetID([32]byte{4})
+       NodeProgram = []byte{0x58}
+
+       Btc2EthOrders = []*common.Order{
+               {
+                       FromAssetID:      &BTC,
+                       ToAssetID:        &ETH,
+                       RatioNumerator:   50,
+                       RatioDenominator: 1,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("37b8edf656e45a7addf47f5626e114a8c394d918a36f61b5a2905675a09b40ae")),
+                               SourcePos:      0,
+                               Amount:         10,
+                               ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251"), 50, 1),
+                       },
+               },
+               {
+                       FromAssetID:      &BTC,
+                       ToAssetID:        &ETH,
+                       RatioNumerator:   53,
+                       RatioDenominator: 1,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("3ec2bbfb499a8736d377b547eee5392bcddf7ec2b287e9ed20b5938c3d84e7cd")),
+                               SourcePos:      0,
+                               Amount:         20,
+                               ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252"), 53, 1),
+                       },
+               },
+               {
+                       FromAssetID:      &BTC,
+                       ToAssetID:        &ETH,
+                       RatioNumerator:   52,
+                       RatioDenominator: 1,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("1232bbfb499a8736d377b547eee5392bcddf7ec2b287e9ed20b5938c3d84e7cd")),
+                               SourcePos:      0,
+                               Amount:         15,
+                               ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252"), 53, 1),
+                       },
+               },
+               {
+                       FromAssetID:      &BTC,
+                       ToAssetID:        &ETH,
+                       RatioNumerator:   49,
+                       RatioDenominator: 1,
+                       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,
+                       RatioNumerator:   1,
+                       RatioDenominator: 51,
+                       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,
+                       RatioNumerator:   1,
+                       RatioDenominator: 52,
+                       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,
+                       RatioNumerator:   1,
+                       RatioDenominator: 54,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("119a02980796dc352cf6475457463aef5666f66622088de3551fa73a65f0d201")),
+                               SourcePos:      0,
+                               Amount:         810,
+                               ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255"), 1, 54.0),
+                       },
+               },
+       }
+
+       Eos2EtcOrders = []*common.Order{
+               {
+                       FromAssetID:      &EOS,
+                       ToAssetID:        &ETC,
+                       RatioNumerator:   1,
+                       RatioDenominator: 2,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("119a02980796dc352cf6475457463aef5666f66622088de3551fa73a65f0d202")),
+                               SourcePos:      0,
+                               Amount:         100,
+                               ControlProgram: MustCreateP2WMCProgram(ETC, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255"), 1, 2.0),
+                       },
+               },
+       }
+
+       Etc2EosOrders = []*common.Order{
+               {
+                       FromAssetID:      &ETC,
+                       ToAssetID:        &EOS,
+                       RatioNumerator:   2,
+                       RatioDenominator: 1,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("119a02980796dc352cf6475457463aef5666f66622088de3551fa73a65f0d203")),
+                               SourcePos:      0,
+                               Amount:         50,
+                               ControlProgram: MustCreateP2WMCProgram(EOS, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3"), 2, 1.0),
+                       },
+               },
+       }
+
+       Eth2EosOrders = []*common.Order{
+               {
+                       FromAssetID:      &ETH,
+                       ToAssetID:        &EOS,
+                       RatioNumerator:   2,
+                       RatioDenominator: 1,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("c1502d03946e4ea92abdb33f51638b181839bd0d8767acc2ee5c665b659c4b13")),
+                               SourcePos:      0,
+                               Amount:         500,
+                               ControlProgram: MustCreateP2WMCProgram(EOS, testutil.MustDecodeHexString("0014e3178c0f294a9a8f4b304236406507913091df86"), 2, 1.0),
+                       },
+               },
+       }
+
+       Eos2BtcOrders = []*common.Order{
+               {
+                       FromAssetID:      &EOS,
+                       ToAssetID:        &BTC,
+                       RatioNumerator:   1,
+                       RatioDenominator: 100,
+                       Utxo: &common.MovUtxo{
+                               SourceID:       hashPtr(testutil.MustDecodeHash("27cf8a0877dc858968cc06396fe6aa9e02d15f3e44c862fe29fa5fd50497cf20")),
+                               SourcePos:      0,
+                               Amount:         1000,
+                               ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("00144d0dfc8a0c5ce41d31d4f61d99aff70588bff8bc"), 1, 100.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")),
+                       },
+               }),
+
+               // full matched transaction from Btc2Eth Eth2Eos Eos2Btc
+               types.NewTx(types.TxData{
+                       Inputs: []*types.TxInput{
+                               types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *Eos2EtcOrders[0].Utxo.SourceID, *Eos2EtcOrders[0].FromAssetID, Eos2EtcOrders[0].Utxo.Amount, Eos2EtcOrders[0].Utxo.SourcePos, Eos2EtcOrders[0].Utxo.ControlProgram),
+                               types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *Etc2EosOrders[0].Utxo.SourceID, *Etc2EosOrders[0].FromAssetID, Etc2EosOrders[0].Utxo.Amount, Etc2EosOrders[0].Utxo.SourcePos, Etc2EosOrders[0].Utxo.ControlProgram),
+                       },
+                       Outputs: []*types.TxOutput{
+                               types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 50, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+                               types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")),
+                       },
+               }),
+
+               // cycle matched
+               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)}, *Eth2EosOrders[0].Utxo.SourceID, *Eth2EosOrders[0].FromAssetID, Eth2EosOrders[0].Utxo.Amount, Eth2EosOrders[0].Utxo.SourcePos, Eth2EosOrders[0].Utxo.ControlProgram),
+                               types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *Eos2BtcOrders[0].Utxo.SourceID, *Eos2BtcOrders[0].FromAssetID, Eos2BtcOrders[0].Utxo.Amount, Eos2BtcOrders[0].Utxo.SourcePos, Eos2BtcOrders[0].Utxo.ControlProgram),
+                       },
+                       Outputs: []*types.TxOutput{
+                               types.NewIntraChainOutput(ETH, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+                               types.NewIntraChainOutput(EOS, 1000, testutil.MustDecodeHexString("0014e3178c0f294a9a8f4b304236406507913091df86")),
+                               types.NewIntraChainOutput(BTC, 10, testutil.MustDecodeHexString("00144d0dfc8a0c5ce41d31d4f61d99aff70588bff8bc")),
+                       },
+               }),
+       }
+)
+
+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
+}
diff --git a/application/mov/mock/mock_mov_store.go b/application/mov/mock/mock_mov_store.go
new file mode 100644 (file)
index 0000000..34a1bd0
--- /dev/null
@@ -0,0 +1,108 @@
+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
+}
diff --git a/application/mov/mov_core.go b/application/mov/mov_core.go
new file mode 100644 (file)
index 0000000..3c60e16
--- /dev/null
@@ -0,0 +1,470 @@
+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")
+       errSpendOutputIDIsIncorrect      = errors.New("spend output id of matched tx is not equals to actual matched tx")
+       errRequestAmountMath             = errors.New("request amount of order less than one or big than max of int64")
+       errNotMatchedOrder               = errors.New("order in matched tx is not matched")
+)
+
+// 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)
+}
+
+// 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
+       }
+
+       orderBook, err := buildOrderBook(m.movStore, txs)
+       if err != nil {
+               return nil, err
+       }
+
+       matchEngine := match.NewEngine(orderBook, 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
+}
+
+func (m *MovCore) validateMatchedTxSequence(txs []*types.Tx) error {
+       orderBook, err := buildOrderBook(m.movStore, txs)
+       if err != nil {
+               return err
+       }
+
+       for _, matchedTx := range txs {
+               if !common.IsMatchedTx(matchedTx) {
+                       continue
+               }
+
+               tradePairs, err := getTradePairsFromMatchedTx(matchedTx)
+               if err != nil {
+                       return err
+               }
+
+               orders := orderBook.PeekOrders(tradePairs)
+               if !match.IsMatched(orders) {
+                       return errNotMatchedOrder
+               }
+
+               if err := validateSpendOrders(matchedTx, orders); err != nil {
+                       return err
+               }
+
+               orderBook.PopOrders(tradePairs)
+
+               for i, output := range matchedTx.Outputs {
+                       if !segwit.IsP2WMCScript(output.ControlProgram()) {
+                               continue
+                       }
+
+                       order, err := common.NewOrderFromOutput(matchedTx, i)
+                       if err != nil {
+                               return err
+                       }
+
+                       if err := orderBook.AddOrder(order); err != nil {
+                               return err
+                       }
+               }
+       }
+       return nil
+}
+
+
+func validateSpendOrders(matchedTx *types.Tx, orders []*common.Order) error {
+       spendOutputIDs := make(map[string]bool)
+       for _, input := range matchedTx.Inputs {
+               spendOutputID, err := input.SpentOutputID()
+               if err != nil {
+                       return err
+               }
+
+               spendOutputIDs[spendOutputID.String()] = true
+       }
+
+       for _, order := range orders {
+               outputID := order.UTXOHash().String()
+               if _, ok := spendOutputIDs[outputID]; !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
+}
+
+func buildOrderBook(store database.MovStore, txs []*types.Tx) (*match.OrderBook, 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.NewOrderBook(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 getTradePairsFromMatchedTx(tx *types.Tx) ([]*common.TradePair, error) {
+       var tradePairs []*common.TradePair
+       for _, tx := range tx.Inputs {
+               contractArgs, err := segwit.DecodeP2WMCProgram(tx.ControlProgram())
+               if err != nil {
+                       return nil, err
+               }
+
+               tradePairs = append(tradePairs, &common.TradePair{FromAssetID: tx.AssetAmount().AssetId, ToAssetID: &contractArgs.RequestedAsset})
+       }
+       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
+}
diff --git a/application/mov/mov_core_test.go b/application/mov/mov_core_test.go
new file mode 100644 (file)
index 0000000..26ed967
--- /dev/null
@@ -0,0 +1,451 @@
+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
+}
index 907d1af..c41d7dc 100644 (file)
@@ -150,13 +150,14 @@ func DecodeCrossOutAction(data []byte) (Action, error) {
 
 type crossOutAction struct {
        bc.AssetAmount
-       Address string `json:"address"`
+       Address string        `json:"address"`
+       Program json.HexBytes `json:"control_program"`
 }
 
 func (a *crossOutAction) Build(ctx context.Context, b *TemplateBuilder) error {
        var missing []string
-       if a.Address == "" {
-               missing = append(missing, "address")
+       if a.Address == "" && len(a.Program) == 0 {
+               missing = append(missing, "address or program")
        }
        if a.AssetId.IsZero() {
                missing = append(missing, "asset_id")
@@ -168,23 +169,25 @@ func (a *crossOutAction) Build(ctx context.Context, b *TemplateBuilder) error {
                return MissingFieldsError(missing...)
        }
 
-       address, err := common.DecodeAddress(a.Address, consensus.BytomMainNetParams(&consensus.ActiveNetParams))
-       if err != nil {
-               return err
-       }
+       program := a.Program
+       if a.Address != "" {
+               address, err := common.DecodeAddress(a.Address, consensus.BytomMainNetParams(&consensus.ActiveNetParams))
+               if err != nil {
+                       return err
+               }
 
-       redeemContract := address.ScriptAddress()
-       program := []byte{}
-       switch address.(type) {
-       case *common.AddressWitnessPubKeyHash:
-               program, err = vmutil.P2WPKHProgram(redeemContract)
-       case *common.AddressWitnessScriptHash:
-               program, err = vmutil.P2WSHProgram(redeemContract)
-       default:
-               return errors.New("unsupport address type")
-       }
-       if err != nil {
-               return err
+               redeemContract := address.ScriptAddress()
+               switch address.(type) {
+               case *common.AddressWitnessPubKeyHash:
+                       program, err = vmutil.P2WPKHProgram(redeemContract)
+               case *common.AddressWitnessScriptHash:
+                       program, err = vmutil.P2WSHProgram(redeemContract)
+               default:
+                       return errors.New("unsupport address type")
+               }
+               if err != nil {
+                       return err
+               }
        }
 
        out := types.NewCrossChainOutput(*a.AssetId, a.Amount, program)
@@ -269,15 +272,15 @@ type crossInAction struct {
        IssuanceProgram   json.HexBytes `json:"issuance_program"`
 }
 
-func (a *crossInAction) Build(ctx context.Context, builder *TemplateBuilder) error {
+func (c *crossInAction) Build(ctx context.Context, builder *TemplateBuilder) error {
        var missing []string
-       if a.SourceID.IsZero() {
+       if c.SourceID.IsZero() {
                missing = append(missing, "source_id")
        }
-       if a.AssetId.IsZero() {
+       if c.AssetId.IsZero() {
                missing = append(missing, "asset_id")
        }
-       if a.Amount == 0 {
+       if c.Amount == 0 {
                missing = append(missing, "amount")
        }
 
@@ -285,20 +288,24 @@ func (a *crossInAction) Build(ctx context.Context, builder *TemplateBuilder) err
                return MissingFieldsError(missing...)
        }
 
-       if err := a.checkAssetID(); err != nil {
+       if err := c.checkAssetID(); err != nil {
                return err
        }
 
        // arguments will be set when materializeWitnesses
-       txin := types.NewCrossChainInput(nil, a.SourceID, *a.AssetId, a.Amount, a.SourcePos, a.VMVersion, a.RawDefinitionByte, a.IssuanceProgram)
+       txin := types.NewCrossChainInput(nil, c.SourceID, *c.AssetId, c.Amount, c.SourcePos, c.VMVersion, c.RawDefinitionByte, c.IssuanceProgram)
        tplIn := &SigningInstruction{}
        fed := cfg.CommonConfig.Federation
-       tplIn.AddRawWitnessKeys(fed.Xpubs, cfg.FedAddressPath, fed.Quorum)
-       tplIn.AddDataWitness(cfg.FederationPMultiSigScript(cfg.CommonConfig))
+
+       if !common.IsOpenFederationIssueAsset(c.RawDefinitionByte) {
+               tplIn.AddRawWitnessKeys(fed.Xpubs, cfg.FedAddressPath, fed.Quorum)
+               tplIn.AddDataWitness(cfg.FederationPMultiSigScript(cfg.CommonConfig))
+       }
+
        return builder.AddInput(txin, tplIn)
 }
 
-func (a *crossInAction) ActionType() string {
+func (c *crossInAction) ActionType() string {
        return "cross_chain_in"
 }
 
diff --git a/common/crossin_asset.go b/common/crossin_asset.go
new file mode 100644 (file)
index 0000000..b5cf283
--- /dev/null
@@ -0,0 +1,27 @@
+package common
+
+import (
+       "encoding/json"
+)
+
+func IsOpenFederationIssueAsset(rawDefinitionByte []byte) bool {
+       var defMap map[string]interface{}
+       if err := json.Unmarshal(rawDefinitionByte, &defMap); err != nil {
+               return false
+       }
+
+       description, ok := defMap["description"].(map[string]interface{})
+       if !ok {
+               return false
+       }
+
+       issueAssetAction, ok := description["issue_asset_action"].(string)
+       if !ok {
+               return false
+       }
+
+       if issueAssetAction != "cross_chain" {
+               return false
+       }
+       return true
+}
index 1c36d86..34e2897 100644 (file)
@@ -9,7 +9,6 @@ import (
 )
 
 func TestFederation(t *testing.T) {
-
        tmpDir, err := ioutil.TempDir(".", "")
        if err != nil {
                t.Fatalf("failed to create temporary data folder: %v", err)
index 8c1d570..4733981 100644 (file)
@@ -36,7 +36,7 @@ seeds = "47.103.79.68:56656,47.103.13.86:56656,47.102.193.119:56656,47.103.17.22
 var testNetConfigTmpl = `chain_id = "testnet"
 [p2p]
 laddr = "tcp://0.0.0.0:56657"
-seeds = "52.82.28.25:56657,52.82.31.195:56657,52.82.31.247:56657"
+seeds = "52.82.7.233:56657,52.82.109.252:56657,52.82.29.30:56657"
 `
 
 var soloNetConfigTmpl = `chain_id = "solonet"
index 07c32e8..11bff3c 100644 (file)
@@ -103,6 +103,7 @@ type Params struct {
        ProducerSubsidys []ProducerSubsidy
 
        SoftForkPoint map[uint64]uint64
+       MovStartHeight uint64
 }
 
 // ActiveNetParams is the active NetParams
@@ -155,12 +156,12 @@ var TestNetParams = Params{
        DNSSeeds:        []string{"www.testnetseed.vapor.io"},
        BasicConfig: BasicConfig{
                MaxBlockGas:                uint64(10000000),
-               MaxGasAmount:               int64(200000),
+               MaxGasAmount:               int64(640000),
                DefaultGasCredit:           int64(160000),
                StorageGasRate:             int64(1),
                VMGasRate:                  int64(200),
-               VotePendingBlockNumber:     uint64(10000),
-               CoinbasePendingBlockNumber: uint64(1200),
+               VotePendingBlockNumber:     uint64(3456000),
+               CoinbasePendingBlockNumber: uint64(7200),
                CoinbaseArbitrarySizeLimit: 128,
        },
        DPOSConfig: DPOSConfig{
index 7515141..8f30320 100644 (file)
@@ -4,14 +4,17 @@ import (
        "errors"
 
        "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)
 }
 
+// IsStraightforward is used to determine whether it is a Straightforward script or not
 func IsStraightforward(prog []byte) bool {
        insts, err := vm.ParseProgram(prog)
        if err != nil {
@@ -23,6 +26,7 @@ func IsStraightforward(prog []byte) bool {
        return insts[0].Op == vm.OP_TRUE || insts[0].Op == vm.OP_FAIL
 }
 
+// IsP2WPKHScript is used to determine whether it is a P2WPKH script or not
 func IsP2WPKHScript(prog []byte) bool {
        insts, err := vm.ParseProgram(prog)
        if err != nil {
@@ -37,6 +41,7 @@ func IsP2WPKHScript(prog []byte) bool {
        return insts[1].Op == vm.OP_DATA_20 && len(insts[1].Data) == consensus.PayToWitnessPubKeyHashDataSize
 }
 
+// IsP2WSHScript is used to determine whether it is a P2WSH script or not
 func IsP2WSHScript(prog []byte) bool {
        insts, err := vm.ParseProgram(prog)
        if err != nil {
@@ -51,6 +56,45 @@ func IsP2WSHScript(prog []byte) bool {
        return insts[1].Op == vm.OP_DATA_32 && len(insts[1].Data) == consensus.PayToWitnessScriptHashDataSize
 }
 
+// IsP2WMCScript is used to determine whether it is a P2WMC script or not
+func IsP2WMCScript(prog []byte) bool {
+       insts, err := vm.ParseProgram(prog)
+       if err != nil {
+               return false
+       }
+
+       if len(insts) != 6 {
+               return false
+       }
+
+       if insts[0].Op > vm.OP_16 {
+               return false
+       }
+
+       if insts[1].Op != vm.OP_DATA_32 || len(insts[1].Data) != 32 {
+               return false
+       }
+
+       if !(insts[2].IsPushdata() && insts[3].IsPushdata() && insts[4].IsPushdata()) {
+               return false
+       }
+
+       if _, err = vm.AsInt64(insts[2].Data); err != nil {
+               return false
+       }
+
+       if _, err = vm.AsInt64(insts[3].Data); err != nil {
+               return false
+       }
+
+       if !IsP2WScript(insts[4].Data) {
+               return false
+       }
+
+       return insts[5].Op == vm.OP_DATA_32 && len(insts[5].Data) == 32
+}
+
+// ConvertP2PKHSigProgram convert standard P2WPKH program into P2PKH program
 func ConvertP2PKHSigProgram(prog []byte) ([]byte, error) {
        insts, err := vm.ParseProgram(prog)
        if err != nil {
@@ -62,6 +106,7 @@ func ConvertP2PKHSigProgram(prog []byte) ([]byte, error) {
        return nil, errors.New("unknow P2PKH version number")
 }
 
+// ConvertP2SHProgram convert standard P2WSH program into P2SH program
 func ConvertP2SHProgram(prog []byte) ([]byte, error) {
        insts, err := vm.ParseProgram(prog)
        if err != nil {
@@ -73,6 +118,46 @@ func ConvertP2SHProgram(prog []byte) ([]byte, error) {
        return nil, errors.New("unknow P2SHP version number")
 }
 
+// ConvertP2MCProgram convert standard P2WMC program into P2MC program
+func ConvertP2MCProgram(prog []byte) ([]byte, error) {
+       magneticContractArgs, err := DecodeP2WMCProgram(prog)
+       if err != nil {
+               return nil, err
+       }
+       return vmutil.P2MCProgram(*magneticContractArgs)
+}
+
+// DecodeP2WMCProgram parse standard P2WMC arguments to magneticContractArgs
+func DecodeP2WMCProgram(prog []byte) (*vmutil.MagneticContractArgs, error) {
+       if !IsP2WMCScript(prog) {
+               return nil, errors.New("invalid P2MC program")
+       }
+
+       insts, err := vm.ParseProgram(prog)
+       if err != nil {
+               return nil, err
+       }
+
+       magneticContractArgs := &vmutil.MagneticContractArgs{
+               SellerProgram: insts[4].Data,
+               SellerKey:     insts[5].Data,
+       }
+       requestedAsset := [32]byte{}
+       copy(requestedAsset[:], insts[1].Data)
+       magneticContractArgs.RequestedAsset = bc.NewAssetID(requestedAsset)
+
+       if magneticContractArgs.RatioNumerator, err = vm.AsInt64(insts[2].Data); err != nil {
+               return nil, err
+       }
+
+       if magneticContractArgs.RatioDenominator, err = vm.AsInt64(insts[3].Data); err != nil {
+               return nil, err
+       }
+
+       return magneticContractArgs, nil
+}
+
+// GetHashFromStandardProg get hash from standard program
 func GetHashFromStandardProg(prog []byte) ([]byte, error) {
        insts, err := vm.ParseProgram(prog)
        if err != nil {
index 36e0061..97b48c0 100644 (file)
@@ -96,6 +96,7 @@ CREATE TABLE `assets` (
   `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`),
diff --git a/math/algorithm.go b/math/algorithm.go
new file mode 100644 (file)
index 0000000..a596496
--- /dev/null
@@ -0,0 +1,8 @@
+package math
+
+func MinUint64(x, y uint64) uint64 {
+       if x < y {
+               return x
+       }
+       return y
+}
index 6b8ea72..10659ce 100644 (file)
@@ -152,7 +152,7 @@ func (fs *fastSync) process() error {
 // sync length cannot be greater than maxFastSyncBlocksNum.
 func (fs *fastSync) findSyncRange() (*types.Block, error) {
        bestHeight := fs.chain.BestBlockHeight()
-       length := fs.mainSyncPeer.IrreversibleHeight() - fastSyncPivotGap - bestHeight
+       length := fs.mainSyncPeer.Height() - fastSyncPivotGap - bestHeight
        if length > maxNumOfBlocksPerSync {
                length = maxNumOfBlocksPerSync
        }
index e57e60a..0e9c620 100644 (file)
@@ -50,6 +50,7 @@ type Switch interface {
 // Mempool is the interface for Bytom mempool
 type Mempool interface {
        GetTransactions() []*core.TxDesc
+       IsDust(tx *types.Tx) bool
 }
 
 //Manager is responsible for the business layer information synchronization
@@ -254,6 +255,11 @@ func (m *Manager) handleTransactionMsg(peer *peers.Peer, msg *msgs.TransactionMe
                return
        }
 
+       if m.mempool.IsDust(tx) {
+               m.peers.ProcessIllegal(peer.ID(), security.LevelMsgIllegal, "receive dust tx msg")
+               return
+       }
+
        m.peers.MarkTx(peer.ID(), tx.ID)
        if isOrphan, err := m.chain.ValidateTx(tx); err != nil && err != core.ErrDustTx && !isOrphan {
                m.peers.ProcessIllegal(peer.ID(), security.LevelMsgIllegal, "fail on validate tx transaction")
@@ -273,6 +279,11 @@ func (m *Manager) handleTransactionsMsg(peer *peers.Peer, msg *msgs.Transactions
        }
 
        for _, tx := range txs {
+               if m.mempool.IsDust(tx) {
+                       m.peers.ProcessIllegal(peer.ID(), security.LevelMsgIllegal, "receive dust txs msg")
+                       continue
+               }
+
                m.peers.MarkTx(peer.ID(), tx.ID)
                if isOrphan, err := m.chain.ValidateTx(tx); err != nil && !isOrphan {
                        m.peers.ProcessIllegal(peer.ID(), security.LevelMsgIllegal, "fail on validate tx transaction")
index cfe8560..00e59e1 100644 (file)
@@ -51,6 +51,10 @@ func (m *mempool) GetTransactions() []*core.TxDesc {
        return txs
 }
 
+func (m *mempool) IsDust(tx *types.Tx) bool {
+       return false
+}
+
 func TestSyncMempool(t *testing.T) {
        tmpDir, err := ioutil.TempDir(".", "")
        if err != nil {
index 1b00877..d71f2e7 100644 (file)
@@ -17,6 +17,7 @@ import (
        "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"
@@ -66,8 +67,8 @@ func NewNode(config *cfg.Config) *Node {
                cmn.Exit(cmn.Fmt("Failed to load federated information:[%s]", err.Error()))
        }
 
-       if err:=vaporLog.InitLogFile(config);err!=nil{
-               log.WithField("err",err).Fatalln("InitLogFile failed")
+       if err := vaporLog.InitLogFile(config); err != nil {
+               log.WithField("err", err).Fatalln("InitLogFile failed")
        }
 
        log.WithFields(log.Fields{
@@ -95,8 +96,9 @@ func NewNode(config *cfg.Config) *Node {
        accessTokens := accesstoken.NewStore(tokenDB)
 
        dispatcher := event.NewDispatcher()
-       txPool := protocol.NewTxPool(store, dispatcher)
-       chain, err := protocol.NewChain(store, txPool, dispatcher)
+       movCore := mov.NewMovCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight)
+       txPool := protocol.NewTxPool(store, []protocol.DustFilterer{movCore}, dispatcher)
+       chain, err := protocol.NewChain(store, txPool, []protocol.Protocoler{movCore}, dispatcher)
        if err != nil {
                cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))
        }
index 70c9018..f7d08b0 100644 (file)
@@ -16,7 +16,11 @@ import (
 )
 
 const (
-       logModule = "blockproposer"
+       logModule         = "blockproposer"
+       warnTimeNum       = 2
+       warnTimeDenom     = 5
+       criticalTimeNum   = 4
+       criticalTimeDenom = 5
 )
 
 // BlockProposer propose several block in specified time range
@@ -74,7 +78,9 @@ func (b *BlockProposer) generateBlocks() {
                        continue
                }
 
-               block, err := proposal.NewBlockTemplate(b.chain, b.txPool, b.accountManager, nextBlockTime)
+               warnDuration := time.Duration(consensus.ActiveNetParams.BlockTimeInterval*warnTimeNum/warnTimeDenom) * time.Millisecond
+               criticalDuration := time.Duration(consensus.ActiveNetParams.BlockTimeInterval*criticalTimeNum/criticalTimeDenom) * time.Millisecond
+               block, err := proposal.NewBlockTemplate(b.chain, b.accountManager, nextBlockTime, warnDuration, criticalDuration)
                if err != nil {
                        log.WithFields(log.Fields{"module": logModule, "error": err}).Error("failed on create NewBlockTemplate")
                        continue
index 0cdb5ad..6a115a7 100644 (file)
@@ -19,12 +19,225 @@ import (
        "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
 // is nil, the coinbase transaction will instead be redeemable by anyone.
-func createCoinbaseTx(accountManager *account.Manager, blockHeight uint64, rewards []state.CoinbaseReward) (tx *types.Tx, err error) {
+func (b *blockBuilder) createCoinbaseTx() (*types.Tx, error) {
+       consensusResult, err := b.chain.GetConsensusResultByHash(&b.block.PreviousBlockHash)
+       if err != nil {
+               return nil, err
+       }
+
+       rewards, err := consensusResult.GetCoinbaseRewards(b.block.Height - 1)
+       if err != nil {
+               return nil, err
+       }
+
+       return createCoinbaseTxByReward(b.accountManager, b.block.Height, rewards)
+}
+
+func (b *blockBuilder) getTimeoutStatus() uint8 {
+       if b.timeoutStatus == timeoutCritical {
+               return b.timeoutStatus
+       }
+
+       select {
+       case <-b.criticalTimeoutCh:
+               b.timeoutStatus = timeoutCritical
+       case <-b.warnTimeoutCh:
+               b.timeoutStatus = timeoutWarn
+       default:
+       }
+
+       return b.timeoutStatus
+}
+
+func createCoinbaseTxByReward(accountManager *account.Manager, blockHeight uint64, rewards []state.CoinbaseReward) (tx *types.Tx, err error) {
        arbitrary := append([]byte{0x00}, []byte(strconv.FormatUint(blockHeight, 10))...)
        var script []byte
        if accountManager == nil {
@@ -73,113 +286,64 @@ func createCoinbaseTx(accountManager *account.Manager, blockHeight uint64, rewar
        return tx, nil
 }
 
-// NewBlockTemplate returns a new block template that is ready to be solved
-func NewBlockTemplate(c *protocol.Chain, txPool *protocol.TxPool, accountManager *account.Manager, timestamp uint64) (b *types.Block, err error) {
-       view := state.NewUtxoViewpoint()
-       txStatus := bc.NewTransactionStatus()
-       if err := txStatus.SetStatus(0, false); err != nil {
-               return nil, err
-       }
-       txEntries := []*bc.Tx{nil}
-       gasUsed := uint64(0)
-
-       // get preblock info for generate next block
-       preBlockHeader := c.BestBlockHeader()
-       preBlockHash := preBlockHeader.Hash()
-       nextBlockHeight := preBlockHeader.Height + 1
-
-       b = &types.Block{
-               BlockHeader: types.BlockHeader{
-                       Version:           1,
-                       Height:            nextBlockHeight,
-                       PreviousBlockHash: preBlockHash,
-                       Timestamp:         timestamp,
-                       BlockCommitment:   types.BlockCommitment{},
-                       BlockWitness:      types.BlockWitness{Witness: make([][]byte, consensus.ActiveNetParams.NumOfConsensusNode)},
-               },
-       }
-       bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: nextBlockHeight}}
-       b.Transactions = []*types.Tx{nil}
+type validateTxResult struct {
+       tx      *types.Tx
+       gasOnly bool
+       err     error
+}
 
-       txs := txPool.GetTransactions()
-       sort.Sort(byTime(txs))
+func preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoViewpoint, gasLeft int64) ([]*validateTxResult, int64) {
+       var results []*validateTxResult
 
-       entriesTxs := []*bc.Tx{}
-       for _, txDesc := range txs {
-               entriesTxs = append(entriesTxs, txDesc.Tx.Tx)
+       bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: chain.BestBlockHeight() + 1}}
+       bcTxs := make([]*bc.Tx, len(txs))
+       for i, tx := range txs {
+               bcTxs[i] = tx.Tx
        }
 
-       validateResults := validation.ValidateTxs(entriesTxs, bcBlock)
-       for i, validateResult := range validateResults {
-               txDesc := txs[i]
-               tx := txDesc.Tx.Tx
+       validateResults := validation.ValidateTxs(bcTxs, bcBlock)
+       for i := 0; i < len(validateResults) && gasLeft > 0; i++ {
                gasOnlyTx := false
-
-               gasStatus := validateResult.GetGasState()
-               if validateResult.GetError() != nil {
+               gasStatus := validateResults[i].GetGasState()
+               if err := validateResults[i].GetError(); err != nil {
                        if !gasStatus.GasValid {
-                               blkGenSkipTxForErr(txPool, &tx.ID, err)
+                               results = append(results, &validateTxResult{tx: txs[i], err: err})
                                continue
                        }
                        gasOnlyTx = true
                }
 
-               if err := c.GetTransactionsUtxo(view, []*bc.Tx{tx}); err != nil {
-                       blkGenSkipTxForErr(txPool, &tx.ID, err)
+               if err := chain.GetTransactionsUtxo(view, []*bc.Tx{bcTxs[i]}); err != nil {
+                       results = append(results, &validateTxResult{tx: txs[i], err: err})
                        continue
                }
 
-               if gasUsed+uint64(gasStatus.GasUsed) > consensus.ActiveNetParams.MaxBlockGas {
+               if gasLeft-gasStatus.GasUsed < 0 {
                        break
                }
 
-               if err := view.ApplyTransaction(bcBlock, tx, gasOnlyTx); err != nil {
-                       blkGenSkipTxForErr(txPool, &tx.ID, err)
+               if err := view.ApplyTransaction(bcBlock, bcTxs[i], gasOnlyTx); err != nil {
+                       results = append(results, &validateTxResult{tx: txs[i], err: err})
                        continue
                }
 
-               if err := txStatus.SetStatus(len(b.Transactions), gasOnlyTx); err != nil {
-                       return nil, err
-               }
-
-               b.Transactions = append(b.Transactions, txDesc.Tx)
-               txEntries = append(txEntries, tx)
-               gasUsed += uint64(gasStatus.GasUsed)
-               if gasUsed == consensus.ActiveNetParams.MaxBlockGas {
-                       break
+               if err := validateBySubProtocols(txs[i], validateResults[i].GetError() != nil, chain.SubProtocols()); err != nil {
+                       results = append(results, &validateTxResult{tx: txs[i], err: err})
+                       continue
                }
 
+               results = append(results, &validateTxResult{tx: txs[i], gasOnly: gasOnlyTx, err: validateResults[i].GetError()})
+               gasLeft -= gasStatus.GasUsed
        }
-
-       consensusResult, err := c.GetConsensusResultByHash(&preBlockHash)
-       if err != nil {
-               return nil, err
-       }
-
-       rewards, err := consensusResult.GetCoinbaseRewards(preBlockHeader.Height)
-       if err != nil {
-               return nil, err
-       }
-
-       // create coinbase transaction
-       b.Transactions[0], err = createCoinbaseTx(accountManager, nextBlockHeight, rewards)
-       if err != nil {
-               return nil, errors.Wrap(err, "fail on createCoinbaseTx")
-       }
-
-       txEntries[0] = b.Transactions[0].Tx
-       b.BlockHeader.BlockCommitment.TransactionsMerkleRoot, err = types.TxMerkleRoot(txEntries)
-       if err != nil {
-               return nil, err
-       }
-
-       b.BlockHeader.BlockCommitment.TransactionStatusHash, err = types.TxStatusMerkleRoot(txStatus.VerifyStatus)
-
-       _, err = c.SignBlock(b)
-       return b, err
+       return results, gasLeft
 }
 
-func blkGenSkipTxForErr(txPool *protocol.TxPool, txHash *bc.Hash, err error) {
-       log.WithFields(log.Fields{"module": logModule, "error": err}).Error("mining block generation: skip tx due to")
-       txPool.RemoveTransaction(txHash)
+func validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.Protocoler) error {
+       for _, subProtocol := range subProtocols {
+               verifyResult := &bc.TxVerifyResult{StatusFail: statusFail}
+               if err := subProtocol.ValidateTx(tx, verifyResult); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
index 73de939..026c627 100644 (file)
@@ -283,7 +283,7 @@ func TestCountCoinbaseTxRewards(t *testing.T) {
                }
 
                // create coinbase transaction
-               c.block.Transactions[0], err = createCoinbaseTx(nil, c.block.Height, rewards)
+               c.block.Transactions[0], err = createCoinbaseTxByReward(nil, c.block.Height, rewards)
                if err != nil {
                        t.Fatal(err)
                }
index d62b3ed..15fb0e2 100644 (file)
@@ -199,27 +199,51 @@ func (c *Chain) ProcessBlockSignature(signature, xPub []byte, blockHash *bc.Hash
        return c.eventDispatcher.Post(event.BlockSignatureEvent{BlockHash: *blockHash, Signature: signature, XPub: xPub})
 }
 
-// SignBlock signing the block if current node is consensus node
-func (c *Chain) SignBlock(block *types.Block) ([]byte, error) {
+// SignBlockHeader signing the block if current node is consensus node
+func (c *Chain) SignBlockHeader(blockHeader *types.BlockHeader) error {
+       _, err := c.signBlockHeader(blockHeader)
+       return err
+}
+
+func (c *Chain) applyBlockSign(blockHeader *types.BlockHeader) error {
+       signature, err := c.signBlockHeader(blockHeader)
+       if err != nil {
+               return err
+       }
+
+       if len(signature) == 0 {
+               return nil
+       }
+
+       if err := c.store.SaveBlockHeader(blockHeader); err != nil {
+               return err
+       }
+
+       xpub := config.CommonConfig.PrivateKey().XPub()
+       return c.eventDispatcher.Post(event.BlockSignatureEvent{BlockHash: blockHeader.Hash(), Signature: signature, XPub: xpub[:]})
+}
+
+func (c *Chain) signBlockHeader(blockHeader *types.BlockHeader) ([]byte, error) {
        xprv := config.CommonConfig.PrivateKey()
-       xpubStr := xprv.XPub().String()
-       node, err := c.getConsensusNode(&block.PreviousBlockHash, xpubStr)
+       xpub := xprv.XPub()
+       node, err := c.getConsensusNode(&blockHeader.PreviousBlockHash, xpub.String())
        if err == errNotFoundConsensusNode {
                return nil, nil
        } else if err != nil {
                return nil, err
        }
 
-       if err := c.checkDoubleSign(&block.BlockHeader, node.XPub.String()); err == errDoubleSignBlock {
+       if err := c.checkDoubleSign(blockHeader, node.XPub.String()); err == errDoubleSignBlock {
                return nil, nil
        } else if err != nil {
                return nil, err
        }
 
-       signature := block.Get(node.Order)
-       if len(signature) == 0 {
-               signature = xprv.Sign(block.Hash().Bytes())
-               block.Set(node.Order, signature)
+       if signature := blockHeader.Get(node.Order); len(signature) != 0 {
+               return nil, nil
        }
+
+       signature := xprv.Sign(blockHeader.Hash().Bytes())
+       blockHeader.Set(node.Order, signature)
        return signature, nil
 }
index b65b3b7..d302d9b 100644 (file)
@@ -698,6 +698,7 @@ type CrossChainInput struct {
        AssetDefinition    *AssetDefinition  `protobuf:"bytes,4,opt,name=asset_definition,json=assetDefinition" json:"asset_definition,omitempty"`
        WitnessArguments   [][]byte          `protobuf:"bytes,5,rep,name=witness_arguments,json=witnessArguments,proto3" json:"witness_arguments,omitempty"`
        Ordinal            uint64            `protobuf:"varint,6,opt,name=ordinal" json:"ordinal,omitempty"`
+       RawDefinitionByte  []byte            `protobuf:"bytes,7,opt,name=rawDefinitionByte,proto3" json:"rawDefinitionByte,omitempty"`
 }
 
 func (m *CrossChainInput) Reset()                    { *m = CrossChainInput{} }
@@ -747,6 +748,13 @@ func (m *CrossChainInput) GetOrdinal() uint64 {
        return 0
 }
 
+func (m *CrossChainInput) GetRawDefinitionByte() []byte {
+       if m != nil {
+               return m.RawDefinitionByte
+       }
+       return nil
+}
+
 func init() {
        proto.RegisterType((*Hash)(nil), "bc.Hash")
        proto.RegisterType((*Program)(nil), "bc.Program")
@@ -773,65 +781,66 @@ func init() {
 func init() { proto.RegisterFile("bc.proto", fileDescriptor0) }
 
 var fileDescriptor0 = []byte{
-       // 951 bytes of a gzipped FileDescriptorProto
-       0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x5f, 0x6f, 0x23, 0x35,
-       0x10, 0x57, 0x36, 0xdb, 0x24, 0x9d, 0xf4, 0x9a, 0xc4, 0xbd, 0x83, 0xd5, 0xe9, 0x10, 0xd5, 0x4a,
-       0x47, 0x0f, 0x21, 0x55, 0xfd, 0x73, 0xfc, 0x79, 0x40, 0x88, 0xd2, 0x72, 0x5c, 0x1e, 0x4e, 0x87,
-       0xdc, 0x2a, 0xaf, 0x2b, 0x67, 0xd7, 0x49, 0x2c, 0x92, 0x75, 0xb0, 0xbd, 0xa1, 0xd7, 0xaf, 0xc0,
-       0x33, 0x0f, 0x7c, 0x22, 0x1e, 0x10, 0xe2, 0x23, 0x81, 0x3c, 0xeb, 0x4d, 0x36, 0x7f, 0xda, 0x82,
-       0x00, 0x01, 0x6f, 0x3b, 0xe3, 0xf1, 0x6f, 0x7e, 0xf3, 0xf3, 0x8c, 0xd7, 0xd0, 0xe8, 0xc7, 0x87,
-       0x53, 0x25, 0x8d, 0x24, 0x5e, 0x3f, 0x0e, 0x5f, 0x80, 0xff, 0x92, 0xe9, 0x11, 0xd9, 0x05, 0x6f,
-       0x76, 0x14, 0x54, 0xf6, 0x2b, 0xcf, 0x6a, 0xd4, 0x9b, 0x1d, 0xa1, 0x7d, 0x1c, 0x78, 0xce, 0x3e,
-       0x46, 0xfb, 0x24, 0xa8, 0x3a, 0xfb, 0x04, 0xed, 0xd3, 0xc0, 0x77, 0xf6, 0x69, 0xf8, 0x29, 0xd4,
-       0xbf, 0x56, 0x72, 0xa8, 0xd8, 0x84, 0xbc, 0x03, 0x30, 0x9b, 0x44, 0x33, 0xae, 0xb4, 0x90, 0x29,
-       0x42, 0xfa, 0x74, 0x7b, 0x36, 0xe9, 0xe5, 0x0e, 0x42, 0xc0, 0x8f, 0x65, 0xc2, 0x11, 0x7b, 0x87,
-       0xe2, 0x77, 0xd8, 0x85, 0xfa, 0x99, 0xd6, 0xdc, 0x74, 0x2f, 0xfe, 0x32, 0x91, 0x57, 0xd0, 0x44,
-       0xa8, 0xb3, 0x89, 0xcc, 0x52, 0x43, 0xde, 0x83, 0x06, 0xb3, 0x66, 0x24, 0x12, 0x04, 0x6d, 0x9e,
-       0x34, 0x0f, 0xfb, 0xf1, 0xa1, 0xcb, 0x46, 0xeb, 0xb8, 0xd8, 0x4d, 0xc8, 0x5b, 0x50, 0x63, 0xb8,
-       0x03, 0x53, 0xf9, 0xd4, 0x59, 0xe1, 0x10, 0x5a, 0x18, 0x7b, 0xc1, 0x07, 0x22, 0x15, 0xc6, 0x16,
-       0xf0, 0x11, 0xb4, 0x85, 0xd6, 0x19, 0x4b, 0x63, 0x1e, 0x4d, 0xf3, 0x9a, 0xcb, 0xd0, 0x4e, 0x06,
-       0xda, 0x2a, 0x82, 0x0a, 0x5d, 0x9e, 0x80, 0x9f, 0x30, 0xc3, 0x30, 0x41, 0xf3, 0xa4, 0x61, 0x63,
-       0xad, 0xf4, 0x14, 0xbd, 0xe1, 0x18, 0x9a, 0x3d, 0x36, 0xce, 0xf8, 0xa5, 0xcc, 0x54, 0xcc, 0xc9,
-       0x63, 0xa8, 0x2a, 0x3e, 0x70, 0xb8, 0x8b, 0x58, 0xeb, 0x24, 0x4f, 0x61, 0x6b, 0x66, 0x43, 0x1d,
-       0x52, 0x6b, 0x5e, 0x50, 0x5e, 0x33, 0xcd, 0x57, 0xc9, 0x63, 0x68, 0x4c, 0xa5, 0x46, 0xce, 0xa8,
-       0x97, 0x4f, 0xe7, 0x76, 0xf8, 0x2d, 0xb4, 0x31, 0xdb, 0x05, 0xd7, 0x46, 0xa4, 0x0c, 0xeb, 0xfa,
-       0x87, 0x53, 0xfe, 0xe6, 0x41, 0xf3, 0x8b, 0xb1, 0x8c, 0xbf, 0x79, 0xc9, 0x59, 0xc2, 0x15, 0x09,
-       0xa0, 0xbe, 0xdc, 0x23, 0x85, 0x69, 0xcf, 0x62, 0xc4, 0xc5, 0x70, 0x34, 0x3f, 0x8b, 0xdc, 0x22,
-       0xcf, 0xa1, 0x33, 0x55, 0x7c, 0x26, 0x64, 0xa6, 0xa3, 0xbe, 0x45, 0xb2, 0x87, 0x5a, 0x5d, 0xa1,
-       0xdb, 0x2a, 0x42, 0x30, 0x57, 0x37, 0x21, 0x4f, 0x60, 0xdb, 0x88, 0x09, 0xd7, 0x86, 0x4d, 0xa6,
-       0xd8, 0x27, 0x3e, 0x5d, 0x38, 0xc8, 0x87, 0xd0, 0x31, 0x8a, 0xa5, 0x9a, 0xc5, 0x96, 0xa4, 0x8e,
-       0x94, 0x94, 0x26, 0xd8, 0x5a, 0xc1, 0x6c, 0x97, 0x43, 0xa8, 0x94, 0x86, 0x7c, 0x0e, 0x6f, 0x97,
-       0x7c, 0x91, 0x36, 0xcc, 0x64, 0x3a, 0x1a, 0x31, 0x3d, 0x0a, 0x6a, 0x2b, 0x9b, 0x1f, 0x95, 0x02,
-       0x2f, 0x31, 0x0e, 0x07, 0xee, 0x02, 0xc8, 0x3a, 0x42, 0x50, 0xc7, 0xcd, 0x8f, 0xec, 0xe6, 0xab,
-       0xd5, 0x6d, 0xb4, 0xb3, 0x86, 0x44, 0x3e, 0x80, 0xce, 0x77, 0xc2, 0xa4, 0x5c, 0xeb, 0x88, 0xa9,
-       0x61, 0x36, 0xe1, 0xa9, 0xd1, 0x41, 0x63, 0xbf, 0xfa, 0x6c, 0x87, 0xb6, 0xdd, 0xc2, 0x59, 0xe1,
-       0x0f, 0x7f, 0xa8, 0x40, 0xe3, 0xea, 0xfa, 0x5e, 0xf9, 0x0f, 0xa0, 0xa5, 0xb9, 0x12, 0x6c, 0x2c,
-       0x6e, 0x78, 0x12, 0x69, 0x71, 0xc3, 0xdd, 0x39, 0xec, 0x2e, 0xdc, 0x97, 0xe2, 0x86, 0xdb, 0x41,
-       0xb7, 0x42, 0x46, 0x8a, 0xa5, 0x43, 0xee, 0xce, 0x1b, 0xa5, 0xa5, 0xd6, 0x41, 0x0e, 0x00, 0x14,
-       0xd7, 0xd9, 0xd8, 0xce, 0x9e, 0x0e, 0xfc, 0xfd, 0xea, 0x92, 0x2c, 0xdb, 0xf9, 0x5a, 0x37, 0xd1,
-       0xe1, 0x31, 0xec, 0x5e, 0x5d, 0xf7, 0xb8, 0x12, 0x83, 0x37, 0x14, 0x9d, 0xe4, 0x5d, 0x68, 0x3a,
-       0x49, 0x07, 0x4c, 0x8c, 0x91, 0x60, 0x83, 0x42, 0xee, 0x7a, 0xc1, 0xc4, 0x38, 0x1c, 0x40, 0x67,
-       0x4d, 0x9f, 0x3b, 0x4a, 0xfa, 0x18, 0x1e, 0xcc, 0x10, 0xbf, 0xd0, 0xd9, 0x43, 0x36, 0x04, 0x75,
-       0x5e, 0x4a, 0x4d, 0x77, 0xf2, 0xc0, 0x1c, 0x32, 0xfc, 0xa5, 0x02, 0xd5, 0x57, 0xd9, 0x35, 0x79,
-       0x1f, 0xea, 0x1a, 0x07, 0x53, 0x07, 0x15, 0xdc, 0x8a, 0x13, 0x50, 0x1a, 0x58, 0x5a, 0xac, 0x93,
-       0xa7, 0x50, 0x2f, 0x6e, 0x05, 0x6f, 0xfd, 0x56, 0x28, 0xd6, 0xc8, 0x57, 0xf0, 0xb0, 0x38, 0xb9,
-       0x64, 0x31, 0x84, 0x3a, 0xa8, 0x22, 0xfc, 0xc3, 0x39, 0x7c, 0x69, 0x42, 0xe9, 0x9e, 0xdb, 0x51,
-       0xf2, 0xdd, 0xd2, 0x02, 0xfe, 0x2d, 0x2d, 0x20, 0xa1, 0x71, 0x2e, 0x45, 0xda, 0x67, 0x9a, 0x93,
-       0x2f, 0x61, 0x6f, 0x03, 0x03, 0x37, 0xff, 0x9b, 0x09, 0x90, 0x75, 0x02, 0x76, 0xbe, 0x98, 0xea,
-       0x0b, 0xa3, 0x98, 0x7a, 0xe3, 0x2e, 0xf5, 0x85, 0x23, 0xfc, 0xbe, 0x02, 0xed, 0x6e, 0x6a, 0x14,
-       0x3b, 0x1f, 0x31, 0x91, 0xbe, 0xce, 0xcc, 0x34, 0x33, 0xe4, 0x00, 0x6a, 0xb9, 0x5a, 0x2e, 0xd9,
-       0x9a, 0x98, 0x6e, 0x99, 0x3c, 0x87, 0x56, 0x2c, 0x53, 0xa3, 0xe4, 0x38, 0xba, 0x43, 0xd3, 0x5d,
-       0x17, 0x53, 0x5c, 0xb4, 0x01, 0xd4, 0xa5, 0x4a, 0x44, 0xca, 0xc6, 0xae, 0x29, 0x0b, 0x13, 0xd9,
-       0x9c, 0x2b, 0xa9, 0xf5, 0x7f, 0x82, 0xcd, 0x8f, 0x15, 0x80, 0x9e, 0x34, 0xfc, 0x5f, 0xe6, 0x61,
-       0xff, 0xc8, 0x33, 0x69, 0x38, 0x5e, 0x8e, 0x3b, 0x14, 0xbf, 0xc3, 0x9f, 0x2b, 0xb0, 0xdd, 0xe3,
-       0x46, 0x76, 0x53, 0x4b, 0xed, 0x08, 0x5a, 0x7a, 0xca, 0x53, 0x13, 0x49, 0xa4, 0xba, 0xf8, 0x99,
-       0x2e, 0xe6, 0xf9, 0x01, 0x06, 0xe4, 0xa5, 0x74, 0x93, 0xdb, 0x9a, 0xcb, 0xfb, 0x93, 0xcd, 0xb5,
-       0xb1, 0xb9, 0xab, 0x9b, 0x9b, 0xbb, 0x5c, 0xa1, 0xbf, 0xac, 0xf4, 0x6b, 0x00, 0xca, 0x8d, 0x50,
-       0xdc, 0x06, 0xfe, 0x71, 0xa1, 0x4b, 0x80, 0xde, 0x32, 0xe0, 0x4f, 0x15, 0xd8, 0xba, 0x9c, 0xf2,
-       0x34, 0xf9, 0xdf, 0x4b, 0xf3, 0xab, 0x07, 0xad, 0xc5, 0x48, 0xe4, 0xc7, 0xfd, 0x09, 0xec, 0x4d,
-       0x98, 0x48, 0x63, 0xeb, 0xb9, 0xa3, 0xae, 0xce, 0x3c, 0xe8, 0xef, 0xae, 0x6d, 0x43, 0x87, 0x57,
-       0xef, 0xef, 0xf0, 0xcf, 0xa0, 0x9d, 0xbf, 0xf5, 0x92, 0xf9, 0x63, 0x0d, 0xab, 0x6d, 0x9e, 0xec,
-       0xcd, 0xdf, 0x2b, 0x8b, 0x77, 0x1c, 0x6d, 0xb1, 0x95, 0x87, 0xdd, 0x46, 0x45, 0xb7, 0xee, 0x57,
-       0xb4, 0xb6, 0xa4, 0x68, 0xbf, 0x86, 0xaf, 0xeb, 0xd3, 0xdf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x20,
-       0xd9, 0x30, 0x59, 0x69, 0x0b, 0x00, 0x00,
+       // 967 bytes of a gzipped FileDescriptorProto
+       0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x4b, 0x6f, 0x23, 0x45,
+       0x10, 0x96, 0xc7, 0x13, 0xdb, 0x29, 0x7b, 0x63, 0xbb, 0xb3, 0x0b, 0xa3, 0xd5, 0x22, 0xa2, 0x91,
+       0x96, 0x2c, 0x02, 0x45, 0x89, 0xb3, 0x3c, 0x0e, 0x08, 0x91, 0x4d, 0x58, 0xd6, 0x87, 0xd5, 0xa2,
+       0x4e, 0xe4, 0xeb, 0xa8, 0x3d, 0xd3, 0xb6, 0x5b, 0xd8, 0xd3, 0xa6, 0xbb, 0xc7, 0x79, 0xfc, 0x05,
+       0xce, 0x1c, 0xf8, 0x45, 0x1c, 0x10, 0x3f, 0x09, 0x81, 0xba, 0x66, 0xc6, 0xe3, 0x57, 0x12, 0x10,
+       0x20, 0xd8, 0xdb, 0xd4, 0xa3, 0xbf, 0xaa, 0xfa, 0xaa, 0xaa, 0xa7, 0xa1, 0xd6, 0x0f, 0x0f, 0xa6,
+       0x4a, 0x1a, 0x49, 0x9c, 0x7e, 0xe8, 0xbf, 0x04, 0xf7, 0x15, 0xd3, 0x23, 0xb2, 0x03, 0xce, 0xec,
+       0xd0, 0x2b, 0xed, 0x95, 0x9e, 0x55, 0xa8, 0x33, 0x3b, 0x44, 0xf9, 0xc8, 0x73, 0x32, 0xf9, 0x08,
+       0xe5, 0x8e, 0x57, 0xce, 0xe4, 0x0e, 0xca, 0xc7, 0x9e, 0x9b, 0xc9, 0xc7, 0xfe, 0x17, 0x50, 0xfd,
+       0x56, 0xc9, 0xa1, 0x62, 0x13, 0xf2, 0x1e, 0xc0, 0x6c, 0x12, 0xcc, 0xb8, 0xd2, 0x42, 0xc6, 0x08,
+       0xe9, 0xd2, 0xed, 0xd9, 0xa4, 0x97, 0x2a, 0x08, 0x01, 0x37, 0x94, 0x11, 0x47, 0xec, 0x06, 0xc5,
+       0x6f, 0xbf, 0x0b, 0xd5, 0x13, 0xad, 0xb9, 0xe9, 0x9e, 0xfd, 0xed, 0x44, 0x5e, 0x43, 0x1d, 0xa1,
+       0x4e, 0x26, 0x32, 0x89, 0x0d, 0xf9, 0x00, 0x6a, 0xcc, 0x8a, 0x81, 0x88, 0x10, 0xb4, 0xde, 0xa9,
+       0x1f, 0xf4, 0xc3, 0x83, 0x2c, 0x1a, 0xad, 0xa2, 0xb1, 0x1b, 0x91, 0x77, 0xa0, 0xc2, 0xf0, 0x04,
+       0x86, 0x72, 0x69, 0x26, 0xf9, 0x43, 0x68, 0xa2, 0xef, 0x19, 0x1f, 0x88, 0x58, 0x18, 0x5b, 0xc0,
+       0xa7, 0xd0, 0x12, 0x5a, 0x27, 0x2c, 0x0e, 0x79, 0x30, 0x4d, 0x6b, 0x5e, 0x84, 0xce, 0x68, 0xa0,
+       0xcd, 0xdc, 0x29, 0xe7, 0xe5, 0x09, 0xb8, 0x11, 0x33, 0x0c, 0x03, 0xd4, 0x3b, 0x35, 0xeb, 0x6b,
+       0xa9, 0xa7, 0xa8, 0xf5, 0xc7, 0x50, 0xef, 0xb1, 0x71, 0xc2, 0xcf, 0x65, 0xa2, 0x42, 0x4e, 0x1e,
+       0x43, 0x59, 0xf1, 0x41, 0x86, 0x5b, 0xf8, 0x5a, 0x25, 0x79, 0x0a, 0x5b, 0x33, 0xeb, 0x9a, 0x21,
+       0x35, 0xe7, 0x05, 0xa5, 0x35, 0xd3, 0xd4, 0x4a, 0x1e, 0x43, 0x6d, 0x2a, 0x35, 0xe6, 0x8c, 0x7c,
+       0xb9, 0x74, 0x2e, 0xfb, 0xdf, 0x43, 0x0b, 0xa3, 0x9d, 0x71, 0x6d, 0x44, 0xcc, 0xb0, 0xae, 0x7f,
+       0x39, 0xe4, 0xef, 0x0e, 0xd4, 0x5f, 0x8c, 0x65, 0xf8, 0xdd, 0x2b, 0xce, 0x22, 0xae, 0x88, 0x07,
+       0xd5, 0xe5, 0x19, 0xc9, 0x45, 0xdb, 0x8b, 0x11, 0x17, 0xc3, 0xd1, 0xbc, 0x17, 0xa9, 0x44, 0x9e,
+       0x43, 0x7b, 0xaa, 0xf8, 0x4c, 0xc8, 0x44, 0x07, 0x7d, 0x8b, 0x64, 0x9b, 0x5a, 0x5e, 0x49, 0xb7,
+       0x99, 0xbb, 0x60, 0xac, 0x6e, 0x44, 0x9e, 0xc0, 0xb6, 0x11, 0x13, 0xae, 0x0d, 0x9b, 0x4c, 0x71,
+       0x4e, 0x5c, 0x5a, 0x28, 0xc8, 0x27, 0xd0, 0x36, 0x8a, 0xc5, 0x9a, 0x85, 0x36, 0x49, 0x1d, 0x28,
+       0x29, 0x8d, 0xb7, 0xb5, 0x82, 0xd9, 0x5a, 0x74, 0xa1, 0x52, 0x1a, 0xf2, 0x15, 0xbc, 0xbb, 0xa0,
+       0x0b, 0xb4, 0x61, 0x26, 0xd1, 0xc1, 0x88, 0xe9, 0x91, 0x57, 0x59, 0x39, 0xfc, 0x68, 0xc1, 0xf1,
+       0x1c, 0xfd, 0x70, 0xe1, 0xce, 0x80, 0xac, 0x23, 0x78, 0x55, 0x3c, 0xfc, 0xc8, 0x1e, 0xbe, 0x58,
+       0x3d, 0x46, 0xdb, 0x6b, 0x48, 0xe4, 0x23, 0x68, 0x5f, 0x0a, 0x13, 0x73, 0xad, 0x03, 0xa6, 0x86,
+       0xc9, 0x84, 0xc7, 0x46, 0x7b, 0xb5, 0xbd, 0xf2, 0xb3, 0x06, 0x6d, 0x65, 0x86, 0x93, 0x5c, 0xef,
+       0xff, 0x58, 0x82, 0xda, 0xc5, 0xd5, 0xbd, 0xf4, 0xef, 0x43, 0x53, 0x73, 0x25, 0xd8, 0x58, 0xdc,
+       0xf0, 0x28, 0xd0, 0xe2, 0x86, 0x67, 0x7d, 0xd8, 0x29, 0xd4, 0xe7, 0xe2, 0x86, 0xdb, 0x45, 0xb7,
+       0x44, 0x06, 0x8a, 0xc5, 0x43, 0x9e, 0xf5, 0x1b, 0xa9, 0xa5, 0x56, 0x41, 0xf6, 0x01, 0x14, 0xd7,
+       0xc9, 0xd8, 0xee, 0x9e, 0xf6, 0xdc, 0xbd, 0xf2, 0x12, 0x2d, 0xdb, 0xa9, 0xad, 0x1b, 0x69, 0xff,
+       0x08, 0x76, 0x2e, 0xae, 0x7a, 0x5c, 0x89, 0xc1, 0x35, 0x45, 0x25, 0x79, 0x1f, 0xea, 0x19, 0xa5,
+       0x03, 0x26, 0xc6, 0x98, 0x60, 0x8d, 0x42, 0xaa, 0x7a, 0xc9, 0xc4, 0xd8, 0x1f, 0x40, 0x7b, 0x8d,
+       0x9f, 0x3b, 0x4a, 0xfa, 0x0c, 0x1e, 0xcc, 0x10, 0x3f, 0xe7, 0xd9, 0xc1, 0x6c, 0x08, 0xf2, 0xbc,
+       0x14, 0x9a, 0x36, 0x52, 0xc7, 0x14, 0xd2, 0xff, 0xb5, 0x04, 0xe5, 0xd7, 0xc9, 0x15, 0xf9, 0x10,
+       0xaa, 0x1a, 0x17, 0x53, 0x7b, 0x25, 0x3c, 0x8a, 0x1b, 0xb0, 0xb0, 0xb0, 0x34, 0xb7, 0x93, 0xa7,
+       0x50, 0xcd, 0x6f, 0x05, 0x67, 0xfd, 0x56, 0xc8, 0x6d, 0xe4, 0x1b, 0x78, 0x98, 0x77, 0x2e, 0x2a,
+       0x96, 0x50, 0x7b, 0x65, 0x84, 0x7f, 0x38, 0x87, 0x5f, 0xd8, 0x50, 0xba, 0x9b, 0x9d, 0x58, 0xd0,
+       0xdd, 0x32, 0x02, 0xee, 0x2d, 0x23, 0x20, 0xa1, 0x76, 0x2a, 0x45, 0xdc, 0x67, 0x9a, 0x93, 0xaf,
+       0x61, 0x77, 0x43, 0x06, 0xd9, 0xfe, 0x6f, 0x4e, 0x80, 0xac, 0x27, 0x60, 0xf7, 0x8b, 0xa9, 0xbe,
+       0x30, 0x8a, 0xa9, 0xeb, 0xec, 0x52, 0x2f, 0x14, 0xfe, 0x0f, 0x25, 0x68, 0x75, 0x63, 0xa3, 0xd8,
+       0xe9, 0x88, 0x89, 0xf8, 0x4d, 0x62, 0xa6, 0x89, 0x21, 0xfb, 0x50, 0x49, 0xd9, 0xca, 0x82, 0xad,
+       0x91, 0x99, 0x99, 0xc9, 0x73, 0x68, 0x86, 0x32, 0x36, 0x4a, 0x8e, 0x83, 0x3b, 0x38, 0xdd, 0xc9,
+       0x7c, 0xf2, 0x8b, 0xd6, 0x83, 0xaa, 0x54, 0x91, 0x88, 0xd9, 0x38, 0x1b, 0xca, 0x5c, 0xc4, 0x6c,
+       0x4e, 0x95, 0xd4, 0xfa, 0x7f, 0x91, 0xcd, 0x4f, 0x25, 0x80, 0x9e, 0x34, 0xfc, 0x3f, 0xce, 0xc3,
+       0xfe, 0x91, 0x67, 0xd2, 0x70, 0xbc, 0x1c, 0x1b, 0x14, 0xbf, 0xfd, 0x5f, 0x4a, 0xb0, 0xdd, 0xe3,
+       0x46, 0x76, 0x63, 0x9b, 0xda, 0x21, 0x34, 0xf5, 0x94, 0xc7, 0x26, 0x90, 0x98, 0x6a, 0xf1, 0x33,
+       0x2d, 0xf6, 0xf9, 0x01, 0x3a, 0xa4, 0xa5, 0x74, 0xa3, 0xdb, 0x86, 0xcb, 0xf9, 0x8b, 0xc3, 0xb5,
+       0x71, 0xb8, 0xcb, 0x9b, 0x87, 0x7b, 0xb1, 0x42, 0x77, 0x99, 0xe9, 0x37, 0x00, 0x94, 0x1b, 0xa1,
+       0xb8, 0x75, 0xfc, 0xf3, 0x44, 0x2f, 0x00, 0x3a, 0xcb, 0x80, 0x3f, 0x97, 0x60, 0xeb, 0x7c, 0xca,
+       0xe3, 0xe8, 0xad, 0xa7, 0xe6, 0x37, 0x07, 0x9a, 0xc5, 0x4a, 0xa4, 0xed, 0xfe, 0x1c, 0x76, 0x27,
+       0x4c, 0xc4, 0xa1, 0xd5, 0xdc, 0x51, 0x57, 0x7b, 0xee, 0xf4, 0x4f, 0xd7, 0xb6, 0x61, 0xc2, 0xcb,
+       0xf7, 0x4f, 0xf8, 0x97, 0xd0, 0x4a, 0xdf, 0x7a, 0xd1, 0xfc, 0xb1, 0x86, 0xd5, 0xd6, 0x3b, 0xbb,
+       0xf3, 0xf7, 0x4a, 0xf1, 0x8e, 0xa3, 0x4d, 0xb6, 0xf2, 0xb0, 0xdb, 0xc8, 0xe8, 0xd6, 0xfd, 0x8c,
+       0x56, 0x96, 0xd7, 0xe9, 0x63, 0x68, 0x2b, 0x76, 0x59, 0xe0, 0xbe, 0xb8, 0x36, 0x1c, 0x7f, 0xec,
+       0x0d, 0xba, 0x6e, 0xe8, 0x57, 0xf0, 0x2d, 0x7e, 0xfc, 0x47, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2a,
+       0xe9, 0x2b, 0xb1, 0x97, 0x0b, 0x00, 0x00,
 }
index 59190d4..ddc90bd 100644 (file)
@@ -132,4 +132,5 @@ message CrossChainInput {
   AssetDefinition  asset_definition          = 4;
   repeated bytes   witness_arguments         = 5;
   uint64           ordinal                   = 6;
+  bytes            rawDefinitionByte         = 7;
 }
index e9014c2..08a7cc7 100644 (file)
@@ -22,11 +22,12 @@ func (cci *CrossChainInput) SetDestination(id *Hash, val *AssetAmount, pos uint6
 }
 
 // NewCrossChainInput creates a new CrossChainInput.
-func NewCrossChainInput(mainchainOutputID *Hash, prog *Program, ordinal uint64, assetDef *AssetDefinition) *CrossChainInput {
+func NewCrossChainInput(mainchainOutputID *Hash, prog *Program, ordinal uint64, assetDef *AssetDefinition, rawDefinitionByte []byte) *CrossChainInput {
        return &CrossChainInput{
                MainchainOutputId: mainchainOutputID,
                Ordinal:           ordinal,
                ControlProgram:    prog,
                AssetDefinition:   assetDef,
+               RawDefinitionByte: rawDefinitionByte,
        }
 }
index 34520be..8ce9fbc 100644 (file)
@@ -76,6 +76,7 @@ func TestEntryID(t *testing.T) {
                                        IssuanceProgram: &Program{VmVersion: 1, Code: []byte{1, 2, 3, 4}},
                                        Data:            &Hash{V0: 0, V1: 1, V2: 2, V3: 3},
                                },
+                               []byte{},
                        ),
                        expectEntryID: "14bb3f6e68f37d037b1f1539a21ab41e182b8d59d703a1af6c426d52cfc775d9",
                },
index 5a86689..65533e2 100644 (file)
@@ -163,7 +163,7 @@ func mapTx(tx *TxData) (headerID bc.Hash, hdr *bc.TxHeader, entryMap map[bc.Hash
                                },
                        }
 
-                       crossIn := bc.NewCrossChainInput(&mainchainOutputID, prog, uint64(i), assetDef)
+                       crossIn := bc.NewCrossChainInput(&mainchainOutputID, prog, uint64(i), assetDef, inp.AssetDefinition)
                        crossIn.WitnessArguments = inp.Arguments
                        crossInID := addEntry(crossIn)
                        muxSources[i] = &bc.ValueSource{
index 937ba5a..907dc07 100644 (file)
@@ -3,9 +3,7 @@ package protocol
 import (
        log "github.com/sirupsen/logrus"
 
-       "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"
@@ -22,7 +20,7 @@ var (
 
 // BlockExist check is a block in chain or orphan
 func (c *Chain) BlockExist(hash *bc.Hash) bool {
-       if _, err := c.store.GetBlockHeader(hash); err == nil {
+       if bh, err := c.store.GetBlockHeader(hash); err == nil && bh.Height <= c.BestBlockHeight() {
                return true
        }
        return c.orphanManage.BlockExist(hash)
@@ -110,6 +108,20 @@ func (c *Chain) connectBlock(block *types.Block) (err error) {
                return err
        }
 
+       for _, p := range c.subProtocols {
+               if err := c.syncProtocolStatus(p); err != nil {
+                       return errors.Wrap(err, p.Name(), "sync sub protocol status")
+               }
+
+               if err := p.ApplyBlock(block); err != nil {
+                       return errors.Wrap(err, p.Name(), "sub protocol connect block")
+               }
+       }
+
+       if err := c.applyBlockSign(&block.BlockHeader); err != nil {
+               return err
+       }
+
        irrBlockHeader := c.lastIrrBlockHeader
        if c.isIrreversible(&block.BlockHeader) && block.Height > irrBlockHeader.Height {
                irrBlockHeader = &block.BlockHeader
@@ -138,6 +150,12 @@ func (c *Chain) reorganizeChain(blockHeader *types.BlockHeader) error {
                return err
        }
 
+       for _, p := range c.subProtocols {
+               if err := c.syncProtocolStatus(p); err != nil {
+                       return errors.Wrap(err, p.Name(), "sync sub protocol status")
+               }
+       }
+
        txsToRestore := map[bc.Hash]*types.Tx{}
        for _, detachBlockHeader := range detachBlockHeaders {
                detachHash := detachBlockHeader.Hash()
@@ -164,6 +182,12 @@ func (c *Chain) reorganizeChain(blockHeader *types.BlockHeader) error {
                        return err
                }
 
+               for _, p := range c.subProtocols {
+                       if err := p.DetachBlock(b); err != nil {
+                               return errors.Wrap(err, p.Name(), "sub protocol detach block")
+                       }
+               }
+
                for _, tx := range b.Transactions {
                        txsToRestore[tx.ID] = tx
                }
@@ -199,10 +223,20 @@ func (c *Chain) reorganizeChain(blockHeader *types.BlockHeader) error {
                        return err
                }
 
+               for _, p := range c.subProtocols {
+                       if err := p.ApplyBlock(b); err != nil {
+                               return errors.Wrap(err, p.Name(), "sub protocol attach block")
+                       }
+               }
+
                if consensusResult.IsFinalize() {
                        consensusResults = append(consensusResults, consensusResult.Fork())
                }
 
+               if err := c.applyBlockSign(attachBlockHeader); err != nil {
+                       return err
+               }
+
                if c.isIrreversible(attachBlockHeader) && attachBlockHeader.Height > irrBlockHeader.Height {
                        irrBlockHeader = attachBlockHeader
                }
@@ -275,22 +309,17 @@ func (c *Chain) saveBlock(block *types.Block) error {
                return errors.Sub(ErrBadBlock, err)
        }
 
-       signature, err := c.SignBlock(block)
-       if err != nil {
-               return errors.Sub(ErrBadBlock, err)
+       for _, p := range c.subProtocols {
+               if err := p.ValidateBlock(block, bcBlock.TransactionStatus.GetVerifyStatus()); err != nil {
+                       return errors.Wrap(err, "sub protocol save block")
+               }
        }
 
        if err := c.store.SaveBlock(block, bcBlock.TransactionStatus); err != nil {
                return err
        }
-       c.orphanManage.Delete(&bcBlock.ID)
 
-       if len(signature) != 0 {
-               xPub := config.CommonConfig.PrivateKey().XPub()
-               if err := c.eventDispatcher.Post(event.BlockSignatureEvent{BlockHash: block.Hash(), Signature: signature, XPub: xPub[:]}); err != nil {
-                       return err
-               }
-       }
+       c.orphanManage.Delete(&bcBlock.ID)
        return nil
 }
 
index 56c85fb..40888aa 100644 (file)
@@ -7,6 +7,7 @@ import (
 
        "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"
@@ -18,12 +19,25 @@ const (
        maxKnownTxs           = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
 )
 
+type Protocoler interface {
+       Name() string
+       StartHeight() uint64
+       BeforeProposalBlock(txs []*types.Tx, nodeProgram []byte, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error)
+       ChainStatus() (uint64, *bc.Hash, error)
+       ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error
+       ValidateTxs(txs []*types.Tx, verifyResults []*bc.TxVerifyResult) error
+       ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error
+       ApplyBlock(block *types.Block) error
+       DetachBlock(block *types.Block) error
+}
+
 // Chain provides functions for working with the Bytom block chain.
 type Chain struct {
        orphanManage   *OrphanManage
        txPool         *TxPool
        store          Store
        processBlockCh chan *processBlockMsg
+       subProtocols   []Protocoler
 
        signatureCache  *common.Cache
        eventDispatcher *event.Dispatcher
@@ -36,12 +50,13 @@ type Chain struct {
 }
 
 // NewChain returns a new Chain using store as the underlying storage.
-func NewChain(store Store, txPool *TxPool, eventDispatcher *event.Dispatcher) (*Chain, error) {
+func NewChain(store Store, txPool *TxPool, subProtocols []Protocoler, eventDispatcher *event.Dispatcher) (*Chain, error) {
        knownTxs, _ := common.NewOrderedSet(maxKnownTxs)
        c := &Chain{
                orphanManage:    NewOrphanManage(),
                txPool:          txPool,
                store:           store,
+               subProtocols:    subProtocols,
                signatureCache:  common.NewCache(maxSignatureCacheSize),
                eventDispatcher: eventDispatcher,
                processBlockCh:  make(chan *processBlockMsg, maxProcessBlockChSize),
@@ -67,6 +82,13 @@ func NewChain(store Store, txPool *TxPool, eventDispatcher *event.Dispatcher) (*
        if err != nil {
                return nil, err
        }
+
+       for _, p := range c.subProtocols {
+               if err := c.syncProtocolStatus(p); err != nil {
+                       return nil, errors.Wrap(err, p.Name(), "sync sub protocol status")
+               }
+       }
+
        go c.blockProcesser()
        return c, nil
 }
@@ -90,7 +112,13 @@ func (c *Chain) initChainStatus() error {
                return err
        }
 
-       consensusResults := []*state.ConsensusResult{&state.ConsensusResult{
+       for _, subProtocol := range c.subProtocols {
+               if err := subProtocol.ApplyBlock(genesisBlock); err != nil {
+                       return err
+               }
+       }
+
+       consensusResults := []*state.ConsensusResult{{
                Seq:            0,
                NumOfVote:      make(map[string]uint64),
                CoinbaseReward: make(map[string]uint64),
@@ -146,6 +174,10 @@ func (c *Chain) InMainChain(hash bc.Hash) bool {
        return *blockHash == hash
 }
 
+func (c *Chain) SubProtocols() []Protocoler {
+       return c.subProtocols
+}
+
 // trace back to the tail of the chain from the given block header
 func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.BlockHeader, error) {
        longestTail, workQueue := blockHeader, []*types.BlockHeader{blockHeader}
@@ -182,6 +214,50 @@ func (c *Chain) markTransactions(txs ...*types.Tx) {
        }
 }
 
+func (c *Chain) syncProtocolStatus(subProtocol Protocoler) error {
+       if c.bestBlockHeader.Height < subProtocol.StartHeight() {
+               return nil
+       }
+
+       protocolHeight, protocolHash, err := subProtocol.ChainStatus()
+       if err != nil {
+               return errors.Wrap(err, "failed on get sub protocol status")
+       }
+
+       if *protocolHash == c.bestBlockHeader.Hash() {
+               return nil
+       }
+
+       for !c.InMainChain(*protocolHash) {
+               block, err := c.GetBlockByHash(protocolHash)
+               if err != nil {
+                       return errors.Wrap(err, subProtocol.Name(), "can't get block by hash in chain")
+               }
+
+               if err := subProtocol.DetachBlock(block); err != nil {
+                       return errors.Wrap(err, subProtocol.Name(), "sub protocol detach block err")
+               }
+
+               protocolHeight, protocolHash = block.Height-1, &block.PreviousBlockHash
+       }
+
+       for height := protocolHeight + 1; height <= c.bestBlockHeader.Height; height++ {
+               block, err := c.GetBlockByHeight(height)
+               if err != nil {
+                       return errors.Wrap(err, subProtocol.Name(), "can't get block by height in chain")
+               }
+
+               if err := subProtocol.ApplyBlock(block); err != nil {
+                       return errors.Wrap(err, subProtocol.Name(), "sub protocol apply block err")
+               }
+
+               blockHash := block.Hash()
+               protocolHeight, protocolHash = block.Height, &blockHash
+       }
+
+       return nil
+}
+
 // This function must be called with mu lock in above level
 func (c *Chain) setState(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, consensusResults []*state.ConsensusResult) error {
        if err := c.store.SaveChainStatus(blockHeader, irrBlockHeader, mainBlockHeaders, view, consensusResults); err != nil {
index b5b4ec5..3c33737 100644 (file)
@@ -52,6 +52,14 @@ func (c *Chain) validateTx(tx *types.Tx, bh *types.BlockHeader) (bool, error) {
                return false, err
        }
 
+       txVerifyResult := &bc.TxVerifyResult{StatusFail: err != nil}
+       for _, p := range c.subProtocols {
+               if err := p.ValidateTx(tx, txVerifyResult); err != nil {
+                       c.txPool.AddErrCache(&tx.ID, err)
+                       return false, err
+               }
+       }
+
        if err != nil {
                log.WithFields(log.Fields{"module": logModule, "tx_id": tx.Tx.ID.String(), "error": err}).Info("transaction status fail")
        }
index 841e549..27d9a9b 100644 (file)
@@ -43,6 +43,10 @@ var (
        ErrDustTx = errors.New("transaction is dust tx")
 )
 
+type DustFilterer interface {
+       IsDust(tx *types.Tx) bool
+}
+
 type TxMsgEvent struct{ TxMsg *TxPoolMsg }
 
 // TxDesc store tx and related info for mining strategy
@@ -76,11 +80,12 @@ type TxPool struct {
        orphans         map[bc.Hash]*orphanTx
        orphansByPrev   map[bc.Hash]map[bc.Hash]*orphanTx
        errCache        *lru.Cache
+       filters         []DustFilterer
        eventDispatcher *event.Dispatcher
 }
 
 // NewTxPool init a new TxPool
-func NewTxPool(store Store, dispatcher *event.Dispatcher) *TxPool {
+func NewTxPool(store Store, filters []DustFilterer, dispatcher *event.Dispatcher) *TxPool {
        tp := &TxPool{
                lastUpdated:     time.Now().Unix(),
                store:           store,
@@ -89,6 +94,7 @@ func NewTxPool(store Store, dispatcher *event.Dispatcher) *TxPool {
                orphans:         make(map[bc.Hash]*orphanTx),
                orphansByPrev:   make(map[bc.Hash]map[bc.Hash]*orphanTx),
                errCache:        lru.New(maxCachedErrTxs),
+               filters:         filters,
                eventDispatcher: dispatcher,
        }
        go tp.orphanExpireWorker()
@@ -210,9 +216,31 @@ func isTransactionZeroOutput(tx *types.Tx) bool {
        return false
 }
 
+func isNoGasStatusFail(tx *types.Tx, statusFail bool) bool {
+       if !statusFail {
+               return false
+       }
+
+       for _, input := range tx.TxData.Inputs {
+               if *consensus.BTMAssetID == input.AssetID() {
+                       return false
+               }
+       }
+       return true
+}
+
 //IsDust checks if a tx has zero output
 func (tp *TxPool) IsDust(tx *types.Tx) bool {
-       return isTransactionZeroOutput(tx)
+       if ok := isTransactionZeroOutput(tx); ok {
+               return ok
+       }
+
+       for _, filter := range tp.filters {
+               if ok := filter.IsDust(tx); ok {
+                       return ok
+               }
+       }
+       return false
 }
 
 func (tp *TxPool) processTransaction(tx *types.Tx, statusFail bool, height, fee uint64) (bool, error) {
@@ -249,6 +277,11 @@ func (tp *TxPool) ProcessTransaction(tx *types.Tx, statusFail bool, height, fee
                log.WithFields(log.Fields{"module": logModule, "tx_id": tx.ID.String()}).Warn("dust tx")
                return false, nil
        }
+
+       if isNoGasStatusFail(tx, statusFail) {
+               log.WithFields(log.Fields{"module": logModule, "tx_id": tx.ID.String()}).Warn("drop no gas status fail tx")
+               return false, nil
+       }
        return tp.processTransaction(tx, statusFail, height, fee)
 }
 
index bb82479..2185eee 100644 (file)
@@ -3,8 +3,10 @@ package validation
 import (
        "fmt"
        "math"
+       "runtime"
        "sync"
 
+       "github.com/bytom/vapor/common"
        "github.com/bytom/vapor/config"
        "github.com/bytom/vapor/consensus"
        "github.com/bytom/vapor/errors"
@@ -13,10 +15,6 @@ import (
        "github.com/bytom/vapor/protocol/vm"
 )
 
-const (
-       validateWorkerNum = 32
-)
-
 // validate transaction error
 var (
        ErrTxVersion                 = errors.New("invalid transaction version")
@@ -277,9 +275,10 @@ func checkValid(vs *validationState, e bc.Entry) (err error) {
                        return errors.New("incorrect asset_id while checking CrossChainInput")
                }
 
-               prog := &bc.Program{
-                       VmVersion: e.ControlProgram.VmVersion,
-                       Code:      config.FederationWScript(config.CommonConfig),
+               prog := e.ControlProgram
+
+               if !common.IsOpenFederationIssueAsset(e.RawDefinitionByte) {
+                       prog.Code = config.FederationWScript(config.CommonConfig)
                }
 
                if _, err := vm.Verify(NewTxVMContext(vs, e, prog, e.WitnessArguments), consensus.ActiveNetParams.DefaultGasCredit); err != nil {
@@ -663,6 +662,7 @@ func validateTxWorker(workCh chan *validateTxWork, resultCh chan *ValidateTxResu
 // ValidateTxs validates txs in async mode
 func ValidateTxs(txs []*bc.Tx, block *bc.Block) []*ValidateTxResult {
        txSize := len(txs)
+       validateWorkerNum := runtime.NumCPU()
        //init the goroutine validate worker
        var wg sync.WaitGroup
        workCh := make(chan *validateTxWork, txSize)
index 619a02e..9908db9 100644 (file)
@@ -877,6 +877,285 @@ func TestValidateTxVersion(t *testing.T) {
        }
 }
 
+func TestMagneticContractTx(t *testing.T) {
+       buyerArgs := vmutil.MagneticContractArgs{
+               RequestedAsset:   bc.AssetID{V0: 1},
+               RatioNumerator:   1,
+               RatioDenominator: 2,
+               SellerProgram:    testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19204"),
+               SellerKey:        testutil.MustDecodeHexString("af1927316233365dd525d3b48f2869f125a656958ee3946286f42904c35b9c91"),
+       }
+
+       sellerArgs := vmutil.MagneticContractArgs{
+               RequestedAsset:   bc.AssetID{V0: 2},
+               RatioNumerator:   2,
+               RatioDenominator: 1,
+               SellerProgram:    testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19204"),
+               SellerKey:        testutil.MustDecodeHexString("af1927316233365dd525d3b48f2869f125a656958ee3946286f42904c35b9c91"),
+       }
+
+       programBuyer, err := vmutil.P2WMCProgram(buyerArgs)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       programSeller, err := vmutil.P2WMCProgram(sellerArgs)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       cases := []struct {
+               desc  string
+               block *bc.Block
+               err   error
+       }{
+               {
+                       desc: "contracts all full trade",
+                       block: &bc.Block{
+                               BlockHeader: &bc.BlockHeader{Version: 0},
+                               Transactions: []*bc.Tx{
+                                       types.MapTx(&types.TxData{
+                                               SerializedSize: 1,
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 100000000, 1, programSeller),
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 200000000, 0, programBuyer),
+                                               },
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewIntraChainOutput(sellerArgs.RequestedAsset, 200000000, sellerArgs.SellerProgram),
+                                                       types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000000, buyerArgs.SellerProgram),
+                                               },
+                                       }),
+                               },
+                       },
+                       err: nil,
+               },
+               {
+                       desc: "first contract partial trade, second contract full trade",
+                       block: &bc.Block{
+                               BlockHeader: &bc.BlockHeader{Version: 0},
+                               Transactions: []*bc.Tx{
+                                       types.MapTx(&types.TxData{
+                                               SerializedSize: 1,
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(100000000), vm.Int64Bytes(0), vm.Int64Bytes(0)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 200000000, 1, programSeller),
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 100000000, 0, programBuyer),
+                                               },
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewIntraChainOutput(sellerArgs.RequestedAsset, 100000000, sellerArgs.SellerProgram),
+                                                       types.NewIntraChainOutput(buyerArgs.RequestedAsset, 150000000, programSeller),
+                                                       types.NewIntraChainOutput(buyerArgs.RequestedAsset, 50000000, buyerArgs.SellerProgram),
+                                               },
+                                       }),
+                               },
+                       },
+                       err: nil,
+               },
+               {
+                       desc: "first contract full trade, second contract partial trade",
+                       block: &bc.Block{
+                               BlockHeader: &bc.BlockHeader{Version: 0},
+                               Transactions: []*bc.Tx{
+                                       types.MapTx(&types.TxData{
+                                               SerializedSize: 1,
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 100000000, 1, programSeller),
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(100000000), vm.Int64Bytes(1), vm.Int64Bytes(0)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 300000000, 0, programBuyer),
+                                               },
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewIntraChainOutput(sellerArgs.RequestedAsset, 200000000, sellerArgs.SellerProgram),
+                                                       types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000000, buyerArgs.SellerProgram),
+                                                       types.NewIntraChainOutput(sellerArgs.RequestedAsset, 100000000, programBuyer),
+                                               },
+                                       }),
+                               },
+                       },
+                       err: nil,
+               },
+               {
+                       desc: "cancel magnetic contract",
+                       block: &bc.Block{
+                               BlockHeader: &bc.BlockHeader{Version: 0},
+                               Transactions: []*bc.Tx{
+                                       types.MapTx(&types.TxData{
+                                               SerializedSize: 1,
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{testutil.MustDecodeHexString("851a14d69076507e202a94a884cdfb3b9f1ecbc1fb0634d2f0d1f9c1a275fdbdf921af0c5309d2d0a0deb85973cba23a4076d2c169c7f08ade2af4048d91d209"), vm.Int64Bytes(0), vm.Int64Bytes(2)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 100000000, 0, programSeller),
+                                               },
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000000, sellerArgs.SellerProgram),
+                                               },
+                                       }),
+                               },
+                       },
+                       err: nil,
+               },
+               {
+                       desc: "wrong signature with cancel magnetic contract",
+                       block: &bc.Block{
+                               BlockHeader: &bc.BlockHeader{Version: 0},
+                               Transactions: []*bc.Tx{
+                                       types.MapTx(&types.TxData{
+                                               SerializedSize: 1,
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{testutil.MustDecodeHexString("686b983a8de1893ef723144389fd1f07b12b048f52f389faa863243195931d5732dbfd15470b43ed63d5067900718cf94f137073f4a972d277bbd967b022545d"), vm.Int64Bytes(0), vm.Int64Bytes(2)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 100000000, 0, programSeller),
+                                               },
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000000, sellerArgs.SellerProgram),
+                                               },
+                                       }),
+                               },
+                       },
+                       err: vm.ErrFalseVMResult,
+               },
+               {
+                       desc: "wrong output amount with contracts all full trade",
+                       block: &bc.Block{
+                               BlockHeader: &bc.BlockHeader{Version: 0},
+                               Transactions: []*bc.Tx{
+                                       types.MapTx(&types.TxData{
+                                               SerializedSize: 1,
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 100000000, 1, programSeller),
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 200000000, 0, programBuyer),
+                                               },
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewIntraChainOutput(sellerArgs.RequestedAsset, 200000000, sellerArgs.SellerProgram),
+                                                       types.NewIntraChainOutput(buyerArgs.RequestedAsset, 50000000, buyerArgs.SellerProgram),
+                                                       types.NewIntraChainOutput(buyerArgs.RequestedAsset, 50000000, []byte{0x55}),
+                                               },
+                                       }),
+                               },
+                       },
+                       err: vm.ErrFalseVMResult,
+               },
+               {
+                       desc: "wrong output assetID with contracts all full trade",
+                       block: &bc.Block{
+                               BlockHeader: &bc.BlockHeader{Version: 0},
+                               Transactions: []*bc.Tx{
+                                       types.MapTx(&types.TxData{
+                                               SerializedSize: 1,
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 100000000, 1, programSeller),
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 200000000, 0, programBuyer),
+                                                       types.NewSpendInput(nil, bc.Hash{V0: 30}, bc.AssetID{V0: 1}, 200000000, 0, []byte{0x51}),
+                                               },
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewIntraChainOutput(bc.AssetID{V0: 1}, 200000000, sellerArgs.SellerProgram),
+                                                       types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000000, buyerArgs.SellerProgram),
+                                                       types.NewIntraChainOutput(sellerArgs.RequestedAsset, 200000000, []byte{0x55}),
+                                               },
+                                       }),
+                               },
+                       },
+                       err: vm.ErrFalseVMResult,
+               },
+               {
+                       desc: "wrong output change program with first contract partial trade and second contract full trade",
+                       block: &bc.Block{
+                               BlockHeader: &bc.BlockHeader{Version: 0},
+                               Transactions: []*bc.Tx{
+                                       types.MapTx(&types.TxData{
+                                               SerializedSize: 1,
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(100000000), vm.Int64Bytes(0), vm.Int64Bytes(0)}, bc.Hash{V0: 10}, buyerArgs.RequestedAsset, 200000000, 1, programSeller),
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 100000000, 0, programBuyer),
+                                               },
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewIntraChainOutput(sellerArgs.RequestedAsset, 100000000, sellerArgs.SellerProgram),
+                                                       types.NewIntraChainOutput(buyerArgs.RequestedAsset, 150000000, []byte{0x55}),
+                                                       types.NewIntraChainOutput(buyerArgs.RequestedAsset, 50000000, buyerArgs.SellerProgram),
+                                               },
+                                       }),
+                               },
+                       },
+                       err: vm.ErrFalseVMResult,
+               },
+       }
+
+       for i, c := range cases {
+               if _, err := ValidateTx(c.block.Transactions[0], c.block); rootErr(err) != c.err {
+                       t.Errorf("case #%d (%s) got error %t, want %t", i, c.desc, rootErr(err), c.err)
+               }
+       }
+}
+
+func TestRingMagneticContractTx(t *testing.T) {
+       aliceArgs := vmutil.MagneticContractArgs{
+               RequestedAsset:   bc.AssetID{V0: 1},
+               RatioNumerator:   2,
+               RatioDenominator: 1,
+               SellerProgram:    testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19204"),
+               SellerKey:        testutil.MustDecodeHexString("960ecabafb88ba460a40912841afecebf0e84884178611ac97210e327c0d1173"),
+       }
+
+       bobArgs := vmutil.MagneticContractArgs{
+               RequestedAsset:   bc.AssetID{V0: 2},
+               RatioNumerator:   2,
+               RatioDenominator: 1,
+               SellerProgram:    testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19204"),
+               SellerKey:        testutil.MustDecodeHexString("ad79ec6bd3a6d6dbe4d0ee902afc99a12b9702fb63edce5f651db3081d868b75"),
+       }
+
+       jackArgs := vmutil.MagneticContractArgs{
+               RequestedAsset:   bc.AssetID{V0: 3},
+               RatioNumerator:   1,
+               RatioDenominator: 4,
+               SellerProgram:    testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19204"),
+               SellerKey:        testutil.MustDecodeHexString("9c19a91988c62046c2767bd7e9999b0c142891b9ebf467bfa59210b435cb0de7"),
+       }
+
+       aliceProgram, err := vmutil.P2WMCProgram(aliceArgs)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       bobProgram, err := vmutil.P2WMCProgram(bobArgs)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       jackProgram, err := vmutil.P2WMCProgram(jackArgs)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       cases := []struct {
+               desc  string
+               block *bc.Block
+               err   error
+       }{
+               {
+                       desc: "contracts all full trade",
+                       block: &bc.Block{
+                               BlockHeader: &bc.BlockHeader{Version: 0},
+                               Transactions: []*bc.Tx{
+                                       types.MapTx(&types.TxData{
+                                               SerializedSize: 1,
+                                               Inputs: []*types.TxInput{
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, bc.Hash{V0: 10}, jackArgs.RequestedAsset, 100000000, 0, aliceProgram),
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, aliceArgs.RequestedAsset, 200000000, 0, bobProgram),
+                                                       types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, bc.Hash{V0: 30}, bobArgs.RequestedAsset, 400000000, 0, jackProgram),
+                                               },
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewIntraChainOutput(aliceArgs.RequestedAsset, 200000000, aliceArgs.SellerProgram),
+                                                       types.NewIntraChainOutput(bobArgs.RequestedAsset, 400000000, bobArgs.SellerProgram),
+                                                       types.NewIntraChainOutput(jackArgs.RequestedAsset, 100000000, jackArgs.SellerProgram),
+                                               },
+                                       }),
+                               },
+                       },
+                       err: nil,
+               },
+       }
+
+       for i, c := range cases {
+               if _, err := ValidateTx(c.block.Transactions[0], c.block); rootErr(err) != c.err {
+                       t.Errorf("case #%d (%s) got error %t, want %t", i, c.desc, rootErr(err), c.err)
+               }
+       }
+}
+
 // A txFixture is returned by sample (below) to produce a sample
 // transaction, which takes a separate, optional _input_ txFixture to
 // affect the transaction that's built. The components of the
index 50c000d..16dd185 100644 (file)
@@ -98,12 +98,17 @@ func NewTxVMContext(vs *validationState, entry bc.Entry, prog *bc.Program, args
 }
 
 func witnessProgram(prog []byte) []byte {
-       if segwit.IsP2WPKHScript(prog) {
-               if witnessProg, err := segwit.ConvertP2PKHSigProgram([]byte(prog)); err == nil {
+       switch {
+       case segwit.IsP2WPKHScript(prog):
+               if witnessProg, err := segwit.ConvertP2PKHSigProgram(prog); err == nil {
                        return witnessProg
                }
-       } else if segwit.IsP2WSHScript(prog) {
-               if witnessProg, err := segwit.ConvertP2SHProgram([]byte(prog)); err == nil {
+       case segwit.IsP2WSHScript(prog):
+               if witnessProg, err := segwit.ConvertP2SHProgram(prog); err == nil {
+                       return witnessProg
+               }
+       case segwit.IsP2WMCScript(prog):
+               if witnessProg, err := segwit.ConvertP2MCProgram(prog); err == nil {
                        return witnessProg
                }
        }
index 0845c9c..5a388ec 100644 (file)
@@ -2,6 +2,7 @@ package vm
 
 import (
        "math"
+       "math/big"
 
        "github.com/bytom/vapor/math/checked"
 )
@@ -457,3 +458,36 @@ func opWithin(vm *virtualMachine) error {
        }
        return vm.pushBool(x >= min && x < max, true)
 }
+
+func opMulFraction(vm *virtualMachine) error {
+       if err := vm.applyCost(8); err != nil {
+               return err
+       }
+
+       z, err := vm.popInt64(true)
+       if err != nil {
+               return err
+       }
+
+       if z == 0 {
+               return ErrDivZero
+       }
+
+       y, err := vm.popInt64(true)
+       if err != nil {
+               return err
+       }
+
+       x, err := vm.popInt64(true)
+       if err != nil {
+               return err
+       }
+
+       res := big.NewInt(x)
+       res.Mul(res, big.NewInt(y)).Quo(res, big.NewInt(z))
+       if !res.IsInt64() {
+               return ErrRange
+       }
+
+       return vm.pushInt64(res.Int64(), true)
+}
index 5275998..d15d119 100644 (file)
@@ -446,6 +446,40 @@ func TestNumericOps(t *testing.T) {
                        deferredCost: -18,
                        dataStack:    [][]byte{{1}},
                },
+       }, {
+               op: OP_MULFRACTION,
+               startVM: &virtualMachine{
+                       runLimit:  50000,
+                       dataStack: [][]byte{{15}, {2}, {3}},
+               },
+               wantVM: &virtualMachine{
+                       runLimit:     49992,
+                       deferredCost: -18,
+                       dataStack:    [][]byte{{10}},
+               },
+       }, {
+               op: OP_MULFRACTION,
+               startVM: &virtualMachine{
+                       runLimit:  50000,
+                       dataStack: [][]byte{Int64Bytes(-654), {3}, {2}},
+               },
+               wantVM: &virtualMachine{
+                       runLimit:     49992,
+                       deferredCost: -18,
+                       dataStack:    [][]byte{Int64Bytes(-981)},
+               },
+       }, {
+               op: OP_MULFRACTION,
+               startVM: &virtualMachine{
+                       runLimit:  50000,
+                       dataStack: [][]byte{Int64Bytes(-654), {3}, {}},
+               },
+               wantVM: &virtualMachine{
+                       runLimit:     49992,
+                       deferredCost: -18,
+                       dataStack:    [][]byte{},
+               },
+               wantErr: ErrDivZero,
        }}
 
        numops := []Op{
@@ -453,6 +487,7 @@ func TestNumericOps(t *testing.T) {
                OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT, OP_BOOLAND,
                OP_BOOLOR, OP_NUMEQUAL, OP_NUMEQUALVERIFY, OP_NUMNOTEQUAL, OP_LESSTHAN,
                OP_LESSTHANOREQUAL, OP_GREATERTHAN, OP_GREATERTHANOREQUAL, OP_MIN, OP_MAX, OP_WITHIN,
+               OP_MULFRACTION,
        }
 
        for _, op := range numops {
@@ -536,6 +571,14 @@ func TestRangeErrs(t *testing.T) {
                {fmt.Sprintf("%d 1 LSHIFT", int64(math.MinInt64)), true},
                {fmt.Sprintf("%d 1 LSHIFT", int64(math.MinInt64)/2), false},
                {fmt.Sprintf("%d 2 LSHIFT", int64(math.MinInt64)/2), true},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MaxInt64)/2-1, 2, 1), false},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MaxInt64)/2+1, 2, 1), true},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MinInt64)/2+1, 2, 1), false},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MinInt64)/2-1, 2, 1), true},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MaxInt64), 3, 2), true},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MaxInt64), 2, 3), false},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MinInt64), 3, 2), true},
+               {fmt.Sprintf("%d %d %d MULFRACTION", int64(math.MinInt64), 2, 3), false},
        }
 
        for i, c := range cases {
index 0659fb7..88dfe1e 100644 (file)
@@ -194,6 +194,7 @@ const (
        OP_MIN                Op = 0xa3
        OP_MAX                Op = 0xa4
        OP_WITHIN             Op = 0xa5
+       OP_MULFRACTION        Op = 0xa6
 
        OP_SHA256        Op = 0xa8
        OP_SHA3          Op = 0xaa
@@ -300,6 +301,7 @@ var (
                OP_MIN:                {OP_MIN, "MIN", opMin},
                OP_MAX:                {OP_MAX, "MAX", opMax},
                OP_WITHIN:             {OP_WITHIN, "WITHIN", opWithin},
+               OP_MULFRACTION:        {OP_MULFRACTION, "MULFRACTION", opMulFraction},
 
                OP_SHA256:        {OP_SHA256, "SHA256", opSha256},
                OP_SHA3:          {OP_SHA3, "SHA3", opSha3},
index 849ec69..21a7cfe 100644 (file)
@@ -3,6 +3,7 @@ package vmutil
 import (
        "github.com/bytom/vapor/crypto/ed25519"
        "github.com/bytom/vapor/errors"
+       "github.com/bytom/vapor/protocol/bc"
        "github.com/bytom/vapor/protocol/vm"
 )
 
@@ -12,6 +13,15 @@ var (
        ErrMultisigFormat = errors.New("bad multisig program format")
 )
 
+// MagneticContractArgs is a struct for magnetic contract arguments
+type MagneticContractArgs struct {
+       RequestedAsset   bc.AssetID
+       RatioNumerator   int64
+       RatioDenominator int64
+       SellerProgram    []byte
+       SellerKey        []byte
+}
+
 // IsUnspendable checks if a contorl program is absolute failed
 func IsUnspendable(prog []byte) bool {
        return len(prog) > 0 && prog[0] == byte(vm.OP_FAIL)
@@ -133,3 +143,225 @@ func checkMultiSigParams(nrequired, npubkeys int64) error {
        }
        return nil
 }
+
+// P2WMCProgram return the segwit pay to magnetic contract
+func P2WMCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
+       builder := NewBuilder()
+       builder.AddInt64(0)
+       builder.AddData(magneticContractArgs.RequestedAsset.Bytes())
+       builder.AddInt64(magneticContractArgs.RatioNumerator)
+       builder.AddInt64(magneticContractArgs.RatioDenominator)
+       builder.AddData(magneticContractArgs.SellerProgram)
+       builder.AddData(magneticContractArgs.SellerKey)
+       return builder.Build()
+}
+
+// P2MCProgram generates the script for control with magnetic contract
+//
+// MagneticContract source code:
+// contract MagneticContract(requestedAsset: Asset,
+//                           ratioNumerator: Integer,
+//                           ratioDenominator: Integer,
+//                           sellerProgram: Program,
+//                           standardProgram: Program,
+//                           sellerKey: PublicKey) locks valueAmount of valueAsset {
+//  clause partialTrade(exchangeAmount: Amount) {
+//      define actualAmount: Integer = exchangeAmount * ratioDenominator / ratioNumerator
+//      verify actualAmount > 0 && actualAmount < valueAmount
+//   lock exchangeAmount of requestedAsset with sellerProgram
+//   lock valueAmount-actualAmount of valueAsset with standardProgram
+//   unlock actualAmount of valueAsset
+//  }
+//  clause fullTrade() {
+//   define requestedAmount: Integer = valueAmount * ratioNumerator / ratioDenominator
+//   verify requestedAmount > 0
+//   lock requestedAmount of requestedAsset with sellerProgram
+//   unlock valueAmount of valueAsset
+//  }
+//  clause cancel(sellerSig: Signature) {
+//   verify checkTxSig(sellerKey, sellerSig)
+//   unlock valueAmount of valueAsset
+//  }
+// }
+//
+// contract stack flow:
+// 7                        [... <position> <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 7]
+// ROLL                     [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <position>]
+// TOALTSTACK               [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
+// 6                        [... <clause selector> sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 6]
+// ROLL                     [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector>]
+// DUP                      [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector> <clause selector>]
+// 2                        [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector> <clause selector> 2]
+// NUMEQUAL                 [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector> (<clause selector> == 2)]
+// JUMPIF:$cancel           [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector>]
+// JUMPIF:$fullTrade        [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
+// $partialTrade            [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
+// 6                        [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 6]
+// PICK                     [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset exchangeAmount]
+// 3                        [... exchangeAmount sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset exchangeAmount 3]
+// ROLL                     [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset exchangeAmount ratioDenominator]
+// 3                        [... exchangeAmount sellerKey standardProgram sellerProgram ratioNumerator requestedAsset exchangeAmount ratioDenominator 3]
+// ROLL                     [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset exchangeAmount ratioDenominator ratioNumerator]
+// MULFRACTION              [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount]
+// AMOUNT                   [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount]
+// OVER                     [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount actualAmount]
+// 0                        [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount actualAmount 0]
+// GREATERTHAN              [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0)]
+// 2                        [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) 2]
+// PICK                     [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) actualAmount]
+// 2                        [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount valueAmount (actualAmount > 0) actualAmount 2]
+// ROLL                     [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount (actualAmount > 0) actualAmount valueAmount]
+// LESSTHAN                 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount (actualAmount > 0) (actualAmount < valueAmount)]
+// BOOLAND                  [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount ((actualAmount > 0) && (actualAmount < valueAmount))]
+// VERIFY                   [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount]
+// FROMALTSTACK             [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position>]
+// DUP                      [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> <position>]
+// TOALTSTACK               [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position>]
+// 6                        [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> 6]
+// ROLL                     [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount]
+// 3                        [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount 3]
+// ROLL                     [... sellerKey standardProgram sellerProgram actualAmount <position> exchangeAmount requestedAsset]
+// 1                        [... sellerKey standardProgram sellerProgram actualAmount <position> exchangeAmount requestedAsset 1]
+// 5                        [... sellerKey standardProgram sellerProgram actualAmount <position> exchangeAmount requestedAsset 1 5]
+// ROLL                     [... sellerKey standardProgram actualAmount <position> exchangeAmount requestedAsset 1 sellerProgram]
+// CHECKOUTPUT              [... sellerKey standardProgram actualAmount checkOutput(exchangeAmount, requestedAsset, sellerProgram)]
+// VERIFY                   [... sellerKey standardProgram actualAmount]
+// FROMALTSTACK             [... sellerKey standardProgram actualAmount <position>]
+// 1                        [... sellerKey standardProgram actualAmount <position> 1]
+// ADD                      [... sellerKey standardProgram actualAmount (<position> + 1)]
+// AMOUNT                   [... sellerKey standardProgram actualAmount (<position> + 1) valueAmount]
+// 2                        [... sellerKey standardProgram actualAmount (<position> + 1) valueAmount 2]
+// ROLL                     [... sellerKey standardProgram (<position> + 1) valueAmount actualAmount]
+// SUB                      [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount)]
+// ASSET                    [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset]
+// 1                        [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset 1]
+// 4                        [... sellerKey standardProgram (<position> + 1) (valueAmount - actualAmount) valueAsset 1 4]
+// ROLL                     [... sellerKey (<position> + 1) (valueAmount - actualAmount) valueAsset 1 standardProgram]
+// CHECKOUTPUT              [... sellerKey checkOutput((valueAmount - actualAmount), valueAsset, standardProgram)]
+// JUMP:$_end               [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
+// $fullTrade               [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
+// AMOUNT                   [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset valueAmount]
+// 2                        [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset valueAmount 2]
+// ROLL                     [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset valueAmount ratioNumerator]
+// 3                        [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset valueAmount ratioNumerator 3]
+// ROLL                     [... sellerKey standardProgram sellerProgram requestedAsset valueAmount ratioNumerator ratioDenominator]
+// MULFRACTION              [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
+// DUP                      [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount requestedAmount]
+// 0                        [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount requestedAmount 0]
+// GREATERTHAN              [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount (requestedAmount > 0)]
+// VERIFY                   [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount]
+// FROMALTSTACK             [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount <position>]
+// SWAP                     [... sellerKey standardProgram sellerProgram requestedAsset <position> requestedAmount]
+// 2                        [... sellerKey standardProgram sellerProgram requestedAsset <position> requestedAmount 2]
+// ROLL                     [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset]
+// 1                        [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset 1]
+// 4                        [... sellerKey standardProgram sellerProgram <position> requestedAmount requestedAsset 1 4]
+// ROLL                     [... sellerKey standardProgram <position> requestedAmount requestedAsset 1 sellerProgram]
+// CHECKOUTPUT              [... sellerKey standardProgram checkOutput(requestedAmount, requestedAsset, sellerProgram)]
+// JUMP:$_end               [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
+// $cancel                  [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset <clause selector>]
+// DROP                     [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
+// 6                        [... sellerSig sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset 6]
+// ROLL                     [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset sellerSig]
+// 6                        [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset sellerSig 6]
+// ROLL                     [... standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset sellerSig sellerKey]
+// TXSIGHASH SWAP CHECKSIG  [... standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset checkTxSig(sellerKey, sellerSig)]
+// $_end                    [... sellerKey standardProgram sellerProgram ratioDenominator ratioNumerator requestedAsset]
+func P2MCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) {
+       standardProgram, err := P2WMCProgram(magneticContractArgs)
+       if err != nil {
+               return nil, err
+       }
+
+       builder := NewBuilder()
+       // contract arguments
+       builder.AddData(magneticContractArgs.SellerKey)
+       builder.AddData(standardProgram)
+       builder.AddData(magneticContractArgs.SellerProgram)
+       builder.AddInt64(magneticContractArgs.RatioDenominator)
+       builder.AddInt64(magneticContractArgs.RatioNumerator)
+       builder.AddData(magneticContractArgs.RequestedAsset.Bytes())
+
+       // contract instructions
+       builder.AddOp(vm.OP_7)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_TOALTSTACK)
+       builder.AddOp(vm.OP_6)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_DUP)
+       builder.AddOp(vm.OP_2)
+       builder.AddOp(vm.OP_NUMEQUAL)
+       builder.AddJumpIf(0)
+       builder.AddJumpIf(1)
+       builder.AddOp(vm.OP_6)
+       builder.AddOp(vm.OP_PICK)
+       builder.AddOp(vm.OP_3)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_3)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_MULFRACTION)
+       builder.AddOp(vm.OP_AMOUNT)
+       builder.AddOp(vm.OP_OVER)
+       builder.AddOp(vm.OP_0)
+       builder.AddOp(vm.OP_GREATERTHAN)
+       builder.AddOp(vm.OP_2)
+       builder.AddOp(vm.OP_PICK)
+       builder.AddOp(vm.OP_ROT)
+       builder.AddOp(vm.OP_LESSTHAN)
+       builder.AddOp(vm.OP_BOOLAND)
+       builder.AddOp(vm.OP_VERIFY)
+       builder.AddOp(vm.OP_FROMALTSTACK)
+       builder.AddOp(vm.OP_DUP)
+       builder.AddOp(vm.OP_TOALTSTACK)
+       builder.AddOp(vm.OP_6)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_3)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_1)
+       builder.AddOp(vm.OP_5)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_CHECKOUTPUT)
+       builder.AddOp(vm.OP_VERIFY)
+       builder.AddOp(vm.OP_FROMALTSTACK)
+       builder.AddOp(vm.OP_1)
+       builder.AddOp(vm.OP_ADD)
+       builder.AddOp(vm.OP_AMOUNT)
+       builder.AddOp(vm.OP_ROT)
+       builder.AddOp(vm.OP_SUB)
+       builder.AddOp(vm.OP_ASSET)
+       builder.AddOp(vm.OP_1)
+       builder.AddOp(vm.OP_4)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_CHECKOUTPUT)
+       builder.AddJump(2)
+       builder.SetJumpTarget(1)
+       builder.AddOp(vm.OP_AMOUNT)
+       builder.AddOp(vm.OP_ROT)
+       builder.AddOp(vm.OP_3)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_MULFRACTION)
+       builder.AddOp(vm.OP_DUP)
+       builder.AddOp(vm.OP_0)
+       builder.AddOp(vm.OP_GREATERTHAN)
+       builder.AddOp(vm.OP_VERIFY)
+       builder.AddOp(vm.OP_FROMALTSTACK)
+       builder.AddOp(vm.OP_SWAP)
+       builder.AddOp(vm.OP_ROT)
+       builder.AddOp(vm.OP_1)
+       builder.AddOp(vm.OP_4)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_CHECKOUTPUT)
+       builder.AddJump(3)
+       builder.SetJumpTarget(0)
+       builder.AddOp(vm.OP_DROP)
+       builder.AddOp(vm.OP_6)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_6)
+       builder.AddOp(vm.OP_ROLL)
+       builder.AddOp(vm.OP_TXSIGHASH)
+       builder.AddOp(vm.OP_SWAP)
+       builder.AddOp(vm.OP_CHECKSIG)
+       builder.SetJumpTarget(2)
+       builder.SetJumpTarget(3)
+       return builder.Build()
+}
index 4fed782..d312889 100644 (file)
@@ -189,9 +189,9 @@ func mockAccountManager(t *testing.T) *mockAccManager {
        dispatcher := event.NewDispatcher()
        store := database.NewStore(testDB)
        accountStore := database.NewAccountStore(testDB)
-       txPool := protocol.NewTxPool(store, dispatcher)
+       txPool := protocol.NewTxPool(store, nil, dispatcher)
        config.CommonConfig = config.DefaultConfig()
-       chain, err := protocol.NewChain(store, txPool, dispatcher)
+       chain, err := protocol.NewChain(store, txPool, nil, dispatcher)
        if err != nil {
                t.Fatal(err)
        }
index 77cf9c1..9da5e3d 100644 (file)
@@ -141,8 +141,8 @@ func GenerateChainData(dirPath string, testDB dbm.DB, txNumber, otherAssetNum in
        config.CommonConfig = config.DefaultConfig()
        store := database.NewStore(testDB)
        dispatcher := event.NewDispatcher()
-       txPool := protocol.NewTxPool(store, dispatcher)
-       chain, err := protocol.NewChain(store, txPool, dispatcher)
+       txPool := protocol.NewTxPool(store, nil, dispatcher)
+       chain, err := protocol.NewChain(store, txPool, nil, dispatcher)
        if err != nil {
                return nil, nil, nil, err
        }
@@ -159,7 +159,7 @@ func InsertChain(chain *protocol.Chain, txPool *protocol.TxPool, txs []*types.Tx
                }
        }
 
-       block, err := proposal.NewBlockTemplate(chain, txPool, nil, uint64(time.Now().UnixNano()/1e6))
+       block, err := proposal.NewBlockTemplate(chain, nil, uint64(time.Now().UnixNano()/1e6), time.Minute, time.Minute)
        if err != nil {
                return err
        }
index a2bec3c..18be6f9 100644 (file)
@@ -22,3 +22,7 @@ func (m *Mempool) AddTx(tx *types.Tx) {
 func (m *Mempool) GetTransactions() []*protocol.TxDesc {
        return m.txs
 }
+
+func (m *Mempool) IsDust(tx *types.Tx) bool {
+       return false
+}
index 22a8720..4fc72b6 100644 (file)
@@ -17,7 +17,7 @@ func BenchmarkNewBlockTpl(b *testing.B) {
        testDB := dbm.NewDB("testdb", "leveldb", "temp")
        defer os.RemoveAll("temp")
 
-       chain, _, txPool, err := test.MockChain(testDB)
+       chain, _, _, err := test.MockChain(testDB)
        if err != nil {
                b.Fatal(err)
        }
@@ -26,6 +26,6 @@ func BenchmarkNewBlockTpl(b *testing.B) {
 
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
-               proposal.NewBlockTemplate(chain, txPool, accountManager, uint64(time.Now().UnixNano()/1e6))
+               proposal.NewBlockTemplate(chain, accountManager, uint64(time.Now().UnixNano()/1e6), time.Minute, time.Minute)
        }
 }
index 4474893..bcce45f 100644 (file)
@@ -29,8 +29,8 @@ func MockChain(testDB dbm.DB) (*protocol.Chain, *database.Store, *protocol.TxPoo
        config.CommonConfig = config.DefaultConfig()
        store := database.NewStore(testDB)
        dispatcher := event.NewDispatcher()
-       txPool := protocol.NewTxPool(store, dispatcher)
-       chain, err := protocol.NewChain(store, txPool, dispatcher)
+       txPool := protocol.NewTxPool(store, nil, dispatcher)
+       chain, err := protocol.NewChain(store, txPool, nil, dispatcher)
        return chain, store, txPool, err
 }
 
index 8f34401..4a656d7 100644 (file)
@@ -41,9 +41,9 @@ func TestWalletUpdate(t *testing.T) {
        store := database.NewStore(testDB)
        walletStore := database.NewWalletStore(testDB)
        dispatcher := event.NewDispatcher()
-       txPool := protocol.NewTxPool(store, dispatcher)
+       txPool := protocol.NewTxPool(store, nil, dispatcher)
 
-       chain, err := protocol.NewChain(store, txPool, dispatcher)
+       chain, err := protocol.NewChain(store, txPool, nil, dispatcher)
        if err != nil {
                t.Fatal(err)
        }
@@ -140,8 +140,8 @@ func TestRescanWallet(t *testing.T) {
 
        store := database.NewStore(testDB)
        dispatcher := event.NewDispatcher()
-       txPool := protocol.NewTxPool(store, dispatcher)
-       chain, err := protocol.NewChain(store, txPool, dispatcher)
+       txPool := protocol.NewTxPool(store, nil, dispatcher)
+       chain, err := protocol.NewChain(store, txPool, nil, dispatcher)
        if err != nil {
                t.Fatal(err)
        }
@@ -191,9 +191,9 @@ func TestMemPoolTxQueryLoop(t *testing.T) {
 
        store := database.NewStore(testDB)
        dispatcher := event.NewDispatcher()
-       txPool := protocol.NewTxPool(store, dispatcher)
+       txPool := protocol.NewTxPool(store, nil, dispatcher)
 
-       chain, err := protocol.NewChain(store, txPool, dispatcher)
+       chain, err := protocol.NewChain(store, txPool, nil, dispatcher)
        if err != nil {
                t.Fatal(err)
        }
index 3888400..9702648 100644 (file)
@@ -34,7 +34,7 @@ func (s *Server) ListCrosschainTxs(c *gin.Context, listTxsReq *listCrosschainTxs
                txFilter.SourceTxHash = txHash
        }
        if txHash, err := listTxsReq.GetFilterString("dest_tx_hash"); err == nil && txHash != "" {
-               txFilter.DestTxHash = sql.NullString{txHash, true}
+               txFilter.DestTxHash = sql.NullString{String: txHash, Valid: true}
        }
 
        txQuery := s.db.Preload("Chain").Preload("Reqs").Preload("Reqs.Asset").Where(txFilter)
index 56c50b1..34917c6 100644 (file)
@@ -5,11 +5,12 @@ import (
 )
 
 type Asset struct {
-       ID              uint64           `gorm:"primary_key;foreignkey:ID" json:"-"`
-       AssetID         string           `json:"asset_id"`
-       IssuanceProgram string           `json:"-"`
-       VMVersion       uint64           `json:"-"`
-       Definition      string           `json:"-"`
-       CreatedAt       common.Timestamp `json:"-"`
-       UpdatedAt       common.Timestamp `json:"-"`
+       ID                    uint64           `gorm:"primary_key;foreignkey:ID" json:"-"`
+       AssetID               string           `json:"asset_id"`
+       IssuanceProgram       string           `json:"-"`
+       VMVersion             uint64           `json:"-"`
+       Definition            string           `json:"-"`
+       IsOpenFederationIssue bool             `json:"_"`
+       CreatedAt             common.Timestamp `json:"-"`
+       UpdatedAt             common.Timestamp `json:"-"`
 }
index 117717c..3606339 100644 (file)
@@ -14,6 +14,7 @@ import (
        "github.com/jinzhu/gorm"
        log "github.com/sirupsen/logrus"
 
+       vpCommon "github.com/bytom/vapor/common"
        "github.com/bytom/vapor/consensus"
        "github.com/bytom/vapor/errors"
        "github.com/bytom/vapor/protocol/bc"
@@ -53,6 +54,8 @@ func NewMainchainKeeper(db *gorm.DB, assetStore *database.AssetStore, cfg *confi
 
 func (m *mainchainKeeper) Run() {
        ticker := time.NewTicker(time.Duration(m.cfg.SyncSeconds) * time.Second)
+       defer ticker.Stop()
+
        for ; true; <-ticker.C {
                for {
                        isUpdate, err := m.syncBlock()
@@ -86,6 +89,10 @@ func (m *mainchainKeeper) createCrossChainReqs(db *gorm.DB, crossTransactionID u
                        return err
                }
 
+               if asset.IsOpenFederationIssue {
+                       continue
+               }
+
                req := &orm.CrossTransactionReq{
                        CrossTransactionID: crossTransactionID,
                        SourcePos:          uint64(i),
@@ -103,30 +110,42 @@ func (m *mainchainKeeper) createCrossChainReqs(db *gorm.DB, crossTransactionID u
        return nil
 }
 
-func (m *mainchainKeeper) isDepositTx(tx *types.Tx) bool {
+func (m *mainchainKeeper) isDepositTx(tx *types.Tx) (bool, error) {
        for _, input := range tx.Inputs {
                if bytes.Equal(input.ControlProgram(), m.federationProg) {
-                       return false
+                       return false, nil
                }
        }
 
        for _, output := range tx.Outputs {
-               if bytes.Equal(output.OutputCommitment.ControlProgram, m.federationProg) {
-                       return true
+               if !bytes.Equal(output.OutputCommitment.ControlProgram, m.federationProg) {
+                       continue
+               }
+
+               if isOFAsset, err := m.isOpenFederationAsset(output.AssetId); err != nil {
+                       return false, err
+               } else if !isOFAsset {
+                       return true, nil
                }
        }
-       return false
+       return false, nil
 }
 
-func (m *mainchainKeeper) isWithdrawalTx(tx *types.Tx) bool {
+func (m *mainchainKeeper) isWithdrawalTx(tx *types.Tx) (bool, error) {
        for _, input := range tx.Inputs {
                if !bytes.Equal(input.ControlProgram(), m.federationProg) {
-                       return false
+                       return false, nil
+               }
+
+               if isOFAsset, err := m.isOpenFederationAsset(input.AssetAmount().AssetId); err != nil {
+                       return false, err
+               } else if isOFAsset {
+                       return false, nil
                }
        }
 
        sourceTxHash := locateSideChainTx(tx.Outputs[len(tx.Outputs)-1])
-       return sourceTxHash != ""
+       return sourceTxHash != "", nil
 }
 
 func locateSideChainTx(output *types.TxOutput) string {
@@ -156,13 +175,17 @@ func (m *mainchainKeeper) processBlock(db *gorm.DB, block *types.Block, txStatus
                        return err
                }
 
-               if m.isDepositTx(tx) {
+               if isDeposit, err := m.isDepositTx(tx); err != nil {
+                       return err
+               } else if isDeposit {
                        if err := m.processDepositTx(db, block, txStatus, i); err != nil {
                                return err
                        }
                }
 
-               if m.isWithdrawalTx(tx) {
+               if isWithdrawal, err := m.isWithdrawalTx(tx); err != nil {
+                       return err
+               } else if isWithdrawal {
                        if err := m.processWithdrawalTx(db, block, i); err != nil {
                                return err
                        }
@@ -188,6 +211,15 @@ func (m *mainchainKeeper) processChainInfo(db *gorm.DB, block *types.Block) erro
        return nil
 }
 
+func (m *mainchainKeeper) isOpenFederationAsset(assetID *btmBc.AssetID) (bool, error) {
+       asset, err := m.assetStore.GetByAssetID(assetID.String())
+       if err != nil {
+               return false, err
+       }
+
+       return asset.IsOpenFederationIssue, nil
+}
+
 func (m *mainchainKeeper) processDepositTx(db *gorm.DB, block *types.Block, txStatus *bc.TransactionStatus, txIndex int) error {
        tx := block.Transactions[txIndex]
        var muxID btmBc.Hash
@@ -242,10 +274,11 @@ func (m *mainchainKeeper) processIssuance(tx *types.Tx) error {
                }
 
                asset := &orm.Asset{
-                       AssetID:         assetID.String(),
-                       IssuanceProgram: hex.EncodeToString(issuance.IssuanceProgram),
-                       VMVersion:       issuance.VMVersion,
-                       Definition:      string(issuance.AssetDefinition),
+                       AssetID:               assetID.String(),
+                       IssuanceProgram:       hex.EncodeToString(issuance.IssuanceProgram),
+                       VMVersion:             issuance.VMVersion,
+                       Definition:            string(issuance.AssetDefinition),
+                       IsOpenFederationIssue: vpCommon.IsOpenFederationIssueAsset(issuance.AssetDefinition),
                }
 
                if err := m.db.Create(asset).Error; err != nil {
index 0ec3d92..0e51314 100644 (file)
@@ -78,6 +78,10 @@ func (s *sidechainKeeper) createCrossChainReqs(db *gorm.DB, crossTransactionID u
                        return err
                }
 
+               if asset.IsOpenFederationIssue {
+                       continue
+               }
+
                prog := rawOutput.ControlProgram()
                req := &orm.CrossTransactionReq{
                        CrossTransactionID: crossTransactionID,
@@ -96,33 +100,49 @@ func (s *sidechainKeeper) createCrossChainReqs(db *gorm.DB, crossTransactionID u
        return nil
 }
 
-func (s *sidechainKeeper) isDepositTx(tx *types.Tx) bool {
+func (s *sidechainKeeper) isDepositTx(tx *types.Tx) (bool, error) {
        for _, input := range tx.Inputs {
-               if input.InputType() == types.CrossChainInputType {
-                       return true
+               if input.InputType() != types.CrossChainInputType {
+                       continue
+               }
+
+               if isOFAsset, err := s.isOpenFederationAsset(input.AssetAmount().AssetId); err != nil {
+                       return false, err
+               } else if !isOFAsset {
+                       return true, nil
                }
        }
-       return false
+       return false, nil
 }
 
-func (s *sidechainKeeper) isWithdrawalTx(tx *types.Tx) bool {
+func (s *sidechainKeeper) isWithdrawalTx(tx *types.Tx) (bool, error) {
        for _, output := range tx.Outputs {
-               if output.OutputType() == types.CrossChainOutputType {
-                       return true
+               if output.OutputType() != types.CrossChainOutputType {
+                       continue
+               }
+
+               if isOFAsset, err := s.isOpenFederationAsset(output.AssetAmount().AssetId); err != nil {
+                       return false, err
+               } else if !isOFAsset {
+                       return true, nil
                }
        }
-       return false
+       return false, nil
 }
 
 func (s *sidechainKeeper) processBlock(db *gorm.DB, block *types.Block, txStatus *bc.TransactionStatus) error {
        for i, tx := range block.Transactions {
-               if s.isDepositTx(tx) {
+               if isDeposit, err := s.isDepositTx(tx); err != nil {
+                       return err
+               } else if isDeposit {
                        if err := s.processDepositTx(db, block, i); err != nil {
                                return err
                        }
                }
 
-               if s.isWithdrawalTx(tx) {
+               if isWithdrawal, err := s.isWithdrawalTx(tx); err != nil {
+                       return err
+               } else if isWithdrawal {
                        if err := s.processWithdrawalTx(db, block, txStatus, i); err != nil {
                                return err
                        }
@@ -177,6 +197,15 @@ func (s *sidechainKeeper) processDepositTx(db *gorm.DB, block *types.Block, txIn
        return nil
 }
 
+func (s *sidechainKeeper) isOpenFederationAsset(assetID *bc.AssetID) (bool, error) {
+       asset, err := s.assetStore.GetByAssetID(assetID.String())
+       if err != nil {
+               return false, err
+       }
+
+       return asset.IsOpenFederationIssue, nil
+}
+
 func (s *sidechainKeeper) processWithdrawalTx(db *gorm.DB, block *types.Block, txStatus *bc.TransactionStatus, txIndex int) error {
        tx := block.Transactions[txIndex]
        var muxID bc.Hash
index 4f40794..6bb3396 100644 (file)
@@ -6,7 +6,6 @@ import (
 
        log "github.com/sirupsen/logrus"
        "github.com/bytom/vapor/crypto/ed25519/chainkd"
-
        "github.com/bytom/vapor/toolbar/common"
 )