package common
import (
+ "encoding/hex"
"fmt"
"github.com/vapor/consensus/segwit"
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
}
}, nil
}
+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
+}
+
func (o *Order) TradePair() *TradePair {
return &TradePair{FromAssetID: o.FromAssetID, ToAssetID: o.ToAssetID}
}
+++ /dev/null
-package database
-
-import (
- "sort"
-
- "github.com/vapor/application/mov/common"
- "github.com/vapor/errors"
- "github.com/vapor/protocol/bc"
- "github.com/vapor/protocol/bc/types"
-)
-
-type MockMovStore struct {
- TradePairs []*common.TradePair
- OrderMap map[string][]*common.Order
- DBState *common.MovDatabaseState
-}
-
-func (m *MockMovStore) GetMovDatabaseState() (*common.MovDatabaseState, error) {
- return m.DBState, nil
-}
-
-func (m *MockMovStore) InitDBState(height uint64, hash *bc.Hash) error {
- return nil
-}
-
-func (m *MockMovStore) 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 *MockMovStore) 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 *MockMovStore) 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
-}
package database
import (
+ "github.com/vapor/application/mov/mock"
"testing"
"github.com/vapor/application/mov/common"
}
for i, c := range cases {
- store := &MockMovStore{TradePairs: c.storeTradePairs}
+ store := mock.NewMovStore(c.storeTradePairs, nil)
var gotTradePairs []*common.TradePair
iterator := NewTradePairIterator(store)
for iterator.HasNext() {
}
for i, c := range cases {
- orderMap := map[string][]*common.Order{c.tradePair.Key(): c.storeOrders}
- store := &MockMovStore{OrderMap: orderMap}
+ store := mock.NewMovStore(nil, c.storeOrders)
var gotOrders []*common.Order
iterator := NewOrderIterator(store, c.tradePair)
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 {
b := [32]byte{}
pos := len(prefix) + assetIDLen*posIndex
db dbm.DB
}
-func NewLevelDBMovStore(db dbm.DB, ) *LevelDBMovStore {
+func NewLevelDBMovStore(db dbm.DB) *LevelDBMovStore {
return &LevelDBMovStore{db: db}
}
var startKey []byte
if orderAfter.Rate > 0 {
- startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, calcUTXOHash(orderAfter), orderAfter.Rate)
+ startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, orderAfter.UTXOHash(), orderAfter.Rate)
}
itr := m.db.IteratorPrefixWithStart(orderPrefix, startKey, false)
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{
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, calcUTXOHash(order), order.Rate)
+ key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate)
batch.Delete(key)
tradePair := &common.TradePair{
},
}
- hash := calcUTXOHash(order)
+ hash := order.UTXOHash()
if hash.String() != wantHash {
t.Fatal("The function is incorrect")
}
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)
import (
"testing"
- "github.com/vapor/protocol/vm"
- "github.com/vapor/testutil"
-
"github.com/vapor/application/mov/common"
- "github.com/vapor/application/mov/database"
- "github.com/vapor/protocol/bc"
+ "github.com/vapor/application/mov/mock"
"github.com/vapor/protocol/bc/types"
- "github.com/vapor/protocol/vm/vmutil"
-)
-
-var (
- btc = bc.NewAssetID([32]byte{1})
- eth = bc.NewAssetID([32]byte{2})
-
- orders = []*common.Order{
- // btc -> eth
- {
- FromAssetID: &btc,
- ToAssetID: ð,
- Rate: 50,
- Utxo: &common.MovUtxo{
- SourceID: hashPtr(testutil.MustDecodeHash("37b8edf656e45a7addf47f5626e114a8c394d918a36f61b5a2905675a09b40ae")),
- SourcePos: 0,
- Amount: 10,
- ControlProgram: mustCreateP2WMCProgram(eth, testutil.MustDecodeHexString("51"), 50, 1),
- },
- },
- {
- FromAssetID: &btc,
- ToAssetID: ð,
- Rate: 53,
- Utxo: &common.MovUtxo{
- SourceID: hashPtr(testutil.MustDecodeHash("3ec2bbfb499a8736d377b547eee5392bcddf7ec2b287e9ed20b5938c3d84e7cd")),
- SourcePos: 0,
- Amount: 20,
- ControlProgram: mustCreateP2WMCProgram(eth, testutil.MustDecodeHexString("52"), 53, 1),
- },
- },
-
- // eth -> btc
- {
- FromAssetID: ð,
- ToAssetID: &btc,
- Rate: 1 / 51.0,
- Utxo: &common.MovUtxo{
- SourceID: hashPtr(testutil.MustDecodeHash("fba43ff5155209cb1769e2ec0e1d4a33accf899c740865edfc6d1de39b873b29")),
- SourcePos: 0,
- Amount: 510,
- ControlProgram: mustCreateP2WMCProgram(btc, testutil.MustDecodeHexString("53"), 1, 51.0),
- },
- },
- {
- FromAssetID: ð,
- ToAssetID: &btc,
- Rate: 1 / 52.0,
- Utxo: &common.MovUtxo{
- SourceID: hashPtr(testutil.MustDecodeHash("05f24bb847db823075d81786aa270748e02602199cd009c0284f928503846a5a")),
- SourcePos: 0,
- Amount: 416,
- ControlProgram: mustCreateP2WMCProgram(btc, testutil.MustDecodeHexString("54"), 1, 52.0),
- },
- },
- {
- FromAssetID: ð,
- ToAssetID: &btc,
- Rate: 1 / 54.0,
- Utxo: &common.MovUtxo{
- SourceID: hashPtr(testutil.MustDecodeHash("119a02980796dc352cf6475457463aef5666f66622088de3551fa73a65f0d201")),
- SourcePos: 0,
- Amount: 810,
- ControlProgram: mustCreateP2WMCProgram(btc, testutil.MustDecodeHexString("55"), 1, 54.0),
- },
- },
- }
)
func TestGenerateMatchedTxs(t *testing.T) {
- btc2eth := &common.TradePair{FromAssetID: &btc, ToAssetID: ð}
- eth2btc := &common.TradePair{FromAssetID: ð, ToAssetID: &btc}
+ btc2eth := &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH}
+ eth2btc := &common.TradePair{FromAssetID: &mock.ETH, ToAssetID: &mock.BTC}
cases := []struct {
- desc string
- tradePair *common.TradePair
- storeOrderMap map[string][]*common.Order
- wantMatchedTxs []*types.TxData
+ desc string
+ tradePair *common.TradePair
+ initStoreOrders []*common.Order
+ wantMatchedTxs []*types.Tx
}{
{
desc: "full matched",
- tradePair: &common.TradePair{FromAssetID: &btc, ToAssetID: ð},
- storeOrderMap: map[string][]*common.Order{
- btc2eth.Key(): {orders[0], orders[1]},
- eth2btc.Key(): {orders[2], orders[3]},
+ tradePair: &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH},
+ initStoreOrders: []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
+ mock.Eth2BtcOrders[0],
},
- wantMatchedTxs: []*types.TxData{
- {
- Inputs: []*types.TxInput{
- types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *orders[0].Utxo.SourceID, *orders[0].FromAssetID, orders[0].Utxo.Amount, orders[0].Utxo.SourcePos, orders[0].Utxo.ControlProgram),
- types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *orders[2].Utxo.SourceID, *orders[2].FromAssetID, orders[2].Utxo.Amount, orders[2].Utxo.SourcePos, orders[2].Utxo.ControlProgram),
- },
- Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*orders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")),
- types.NewIntraChainOutput(*orders[2].ToAssetID, 10, testutil.MustDecodeHexString("53")),
- types.NewIntraChainOutput(*orders[0].ToAssetID, 10, []byte{0x51}),
- },
- },
+ wantMatchedTxs: []*types.Tx{
+ mock.MatchedTxs[1],
},
},
{
desc: "partial matched",
- tradePair: &common.TradePair{FromAssetID: &btc, ToAssetID: ð},
- storeOrderMap: map[string][]*common.Order{
- btc2eth.Key(): {orders[0], orders[1]},
- eth2btc.Key(): {orders[3]},
+ tradePair: &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH},
+ initStoreOrders: []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
+ mock.Eth2BtcOrders[1],
},
- wantMatchedTxs: []*types.TxData{
- {
- Inputs: []*types.TxInput{
- types.NewSpendInput([][]byte{vm.Int64Bytes(416), vm.Int64Bytes(0), vm.Int64Bytes(0)}, *orders[0].Utxo.SourceID, *orders[0].FromAssetID, orders[0].Utxo.Amount, orders[0].Utxo.SourcePos, orders[0].Utxo.ControlProgram),
- types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *orders[3].Utxo.SourceID, *orders[3].FromAssetID, orders[3].Utxo.Amount, orders[3].Utxo.SourcePos, orders[3].Utxo.ControlProgram),
- },
- Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*orders[0].ToAssetID, 416, testutil.MustDecodeHexString("51")),
- // re-order
- types.NewIntraChainOutput(*orders[0].FromAssetID, 2, orders[0].Utxo.ControlProgram),
- types.NewIntraChainOutput(*orders[3].ToAssetID, 8, testutil.MustDecodeHexString("54")),
- },
- },
+ wantMatchedTxs: []*types.Tx{
+ mock.MatchedTxs[0],
},
},
{
desc: "partial matched and continue to match",
- tradePair: &common.TradePair{FromAssetID: &btc, ToAssetID: ð},
- storeOrderMap: map[string][]*common.Order{
- btc2eth.Key(): {orders[0], orders[1]},
- eth2btc.Key(): {orders[4]},
+ tradePair: &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH},
+ initStoreOrders: []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
+ mock.Eth2BtcOrders[2],
},
- wantMatchedTxs: []*types.TxData{
- {
- Inputs: []*types.TxInput{
- types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *orders[0].Utxo.SourceID, *orders[0].FromAssetID, orders[0].Utxo.Amount, orders[0].Utxo.SourcePos, orders[0].Utxo.ControlProgram),
- types.NewSpendInput([][]byte{vm.Int64Bytes(10), vm.Int64Bytes(1), vm.Int64Bytes(0)}, *orders[4].Utxo.SourceID, *orders[4].FromAssetID, orders[4].Utxo.Amount, orders[4].Utxo.SourcePos, orders[4].Utxo.ControlProgram),
- },
- Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*orders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")),
- types.NewIntraChainOutput(*orders[4].ToAssetID, 10, testutil.MustDecodeHexString("55")),
- // re-order
- types.NewIntraChainOutput(*orders[4].FromAssetID, 270, orders[4].Utxo.ControlProgram),
- // fee
- types.NewIntraChainOutput(*orders[4].FromAssetID, 27, []byte{0x51}),
- // refund
- types.NewIntraChainOutput(*orders[4].FromAssetID, 6, testutil.MustDecodeHexString("51")),
- types.NewIntraChainOutput(*orders[4].FromAssetID, 7, testutil.MustDecodeHexString("55")),
- },
- },
- {
- Inputs: []*types.TxInput{
- types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(0)}, *orders[1].Utxo.SourceID, *orders[1].FromAssetID, orders[1].Utxo.Amount, orders[1].Utxo.SourcePos, orders[1].Utxo.ControlProgram),
- types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, testutil.MustDecodeHash("f47177c12d25f5316eb377ea006e77bf07e4f9646860e4641e313e004f9aa989"), *orders[4].FromAssetID, 270, 2, orders[4].Utxo.ControlProgram),
- },
- Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*orders[1].ToAssetID, 270, testutil.MustDecodeHexString("52")),
- // re-order
- types.NewIntraChainOutput(*orders[1].FromAssetID, 15, orders[1].Utxo.ControlProgram),
- types.NewIntraChainOutput(*orders[4].ToAssetID, 5, testutil.MustDecodeHexString("55")),
- },
- },
+ wantMatchedTxs: []*types.Tx{
+ mock.MatchedTxs[2],
+ mock.MatchedTxs[3],
},
},
+ {
+ desc: "unable to match",
+ tradePair: &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH},
+ initStoreOrders: []*common.Order{
+ mock.Btc2EthOrders[1],
+ mock.Eth2BtcOrders[0],
+ },
+ wantMatchedTxs: []*types.Tx{},
+ },
}
for i, c := range cases {
- movStore := &database.MockMovStore{OrderMap: c.storeOrderMap}
+ movStore := mock.NewMovStore([]*common.TradePair{btc2eth, eth2btc}, c.initStoreOrders)
matchEngine := NewEngine(NewOrderTable(movStore, nil, nil), 0.05, []byte{0x51})
var gotMatchedTxs []*types.Tx
for matchEngine.HasMatchedTx(c.tradePair, c.tradePair.Reverse()) {
}
c.wantMatchedTxs[i].SerializedSize = uint64(len(byteData))
- wantMatchedTx := types.NewTx(*c.wantMatchedTxs[i])
+ 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 mustCreateP2WMCProgram(requestAsset bc.AssetID, sellerProgram []byte, ratioMolecule, ratioDenominator int64) []byte {
- contractArgs := vmutil.MagneticContractArgs{
- RequestedAsset: requestAsset,
- RatioNumerator: ratioMolecule,
- RatioDenominator: ratioDenominator,
- SellerProgram: sellerProgram,
- SellerKey: testutil.MustDecodeHexString("ad79ec6bd3a6d6dbe4d0ee902afc99a12b9702fb63edce5f651db3081d868b75"),
- }
- program, err := vmutil.P2WMCProgram(contractArgs)
- if err != nil {
- panic(err)
- }
- return program
-}
-
-func hashPtr(hash bc.Hash) *bc.Hash {
- return &hash
-}
--- /dev/null
+package match
+
+import (
+ "testing"
+
+ "github.com/vapor/application/mov/mock"
+ "github.com/vapor/application/mov/common"
+ "github.com/vapor/application/mov/database"
+)
+
+var (
+ btc2eth = &common.TradePair{FromAssetID: &mock.BTC, ToAssetID: &mock.ETH}
+)
+
+func TestOrderTable(t *testing.T) {
+ cases := []struct {
+ desc string
+ initMovStore database.MovStore
+ initArrivalAddOrders []*common.Order
+ initArrivalDelOrders []*common.Order
+ addOrders []*common.Order
+ popOrders []*common.TradePair
+ wantPeekedOrders map[common.TradePair]*common.Order
+ }{
+ {
+ desc: "no arrival orders, no add order, no pop order",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+ },
+ ),
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[0],
+ },
+ },
+ {
+ desc: "no arrival orders, add lower price order, no pop order",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+ }),
+ addOrders: []*common.Order{mock.Btc2EthOrders[3]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[3],
+ },
+ },
+ {
+ desc: "no arrival orders, no add order, pop one order",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+ }),
+ popOrders: []*common.TradePair{btc2eth},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[2],
+ },
+ },
+ {
+ desc: "has arrival add orders, no add order, no pop order, the arrival add order is lower price",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+ }),
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[3]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[3],
+ },
+ },
+ {
+ desc: "has arrival add orders, no add order, no pop order, the db add order is lower price",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[0], mock.Btc2EthOrders[1],
+ }),
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[2]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[0],
+ },
+ },
+ {
+ desc: "has arrival add orders, no add order, pop one order, after pop the arrival order is lower price",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+ }),
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
+ popOrders: []*common.TradePair{btc2eth},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[0],
+ },
+ },
+ {
+ desc: "has arrival delete orders, no add order, no pop order",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[1], mock.Btc2EthOrders[2], mock.Btc2EthOrders[3],
+ }),
+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[2],
+ },
+ },
+ {
+ desc: "has arrival delete orders and arrival add orders, no add order, no pop order, the arrival order is lower price",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[3], mock.Btc2EthOrders[1], mock.Btc2EthOrders[2],
+ }),
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0]},
+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[0],
+ },
+ },
+ {
+ desc: "has arrival delete orders and arrival add orders, no add order, pop one order",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{
+ mock.Btc2EthOrders[3], mock.Btc2EthOrders[1],
+ }),
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[2]},
+ initArrivalDelOrders: []*common.Order{mock.Btc2EthOrders[3]},
+ popOrders: []*common.TradePair{btc2eth},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[2],
+ },
+ },
+ {
+ desc: "has arrival add orders, but db order is empty",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{}),
+ initArrivalAddOrders: []*common.Order{mock.Btc2EthOrders[0], mock.Btc2EthOrders[2]},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: mock.Btc2EthOrders[0],
+ },
+ },
+ {
+ desc: "no add orders, and db order is empty",
+ initMovStore: mock.NewMovStore(
+ []*common.TradePair{btc2eth},
+ []*common.Order{}),
+ initArrivalAddOrders: []*common.Order{},
+ wantPeekedOrders: map[common.TradePair]*common.Order{
+ *btc2eth: nil,
+ },
+ },
+ }
+
+ for i, c := range cases {
+ orderTable := NewOrderTable(c.initMovStore, c.initArrivalAddOrders, c.initArrivalDelOrders)
+ for _, order := range c.addOrders {
+ if err := orderTable.AddOrder(order); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ for _, tradePair := range c.popOrders {
+ orderTable.PopOrder(tradePair)
+ }
+
+ for tradePair, wantOrder := range c.wantPeekedOrders {
+ gotOrder := orderTable.PeekOrder(&tradePair)
+ if wantOrder == gotOrder && wantOrder == nil {
+ continue
+ }
+
+ if wantOrder != nil && gotOrder != nil && 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)
+ }
+ }
+ }
+}
--- /dev/null
+package mock
+
+import (
+ "github.com/vapor/application/mov/common"
+ "github.com/vapor/protocol/bc"
+ "github.com/vapor/protocol/bc/types"
+ "github.com/vapor/protocol/vm"
+ "github.com/vapor/protocol/vm/vmutil"
+ "github.com/vapor/testutil"
+)
+
+var (
+ BTC = bc.NewAssetID([32]byte{1})
+ ETH = bc.NewAssetID([32]byte{2})
+
+ Btc2EthOrders = []*common.Order{
+ {
+ FromAssetID: &BTC,
+ ToAssetID: Ð,
+ Rate: 50,
+ Utxo: &common.MovUtxo{
+ SourceID: hashPtr(testutil.MustDecodeHash("37b8edf656e45a7addf47f5626e114a8c394d918a36f61b5a2905675a09b40ae")),
+ SourcePos: 0,
+ Amount: 10,
+ ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("51"), 50, 1),
+ },
+ },
+ {
+ FromAssetID: &BTC,
+ ToAssetID: Ð,
+ Rate: 53,
+ Utxo: &common.MovUtxo{
+ SourceID: hashPtr(testutil.MustDecodeHash("3ec2bbfb499a8736d377b547eee5392bcddf7ec2b287e9ed20b5938c3d84e7cd")),
+ SourcePos: 0,
+ Amount: 20,
+ ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("52"), 53, 1),
+ },
+ },
+ {
+ FromAssetID: &BTC,
+ ToAssetID: Ð,
+ Rate: 52,
+ Utxo: &common.MovUtxo{
+ SourceID: hashPtr(testutil.MustDecodeHash("1232bbfb499a8736d377b547eee5392bcddf7ec2b287e9ed20b5938c3d84e7cd")),
+ SourcePos: 0,
+ Amount: 15,
+ ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("52"), 53, 1),
+ },
+ },
+ {
+ FromAssetID: &BTC,
+ ToAssetID: Ð,
+ Rate: 49,
+ Utxo: &common.MovUtxo{
+ SourceID: hashPtr(testutil.MustDecodeHash("7872bbfb499a8736d377b547eee5392bcddf7ec2b287e9ed20b5938c3d84e7cd")),
+ SourcePos: 0,
+ Amount: 17,
+ ControlProgram: MustCreateP2WMCProgram(ETH, testutil.MustDecodeHexString("52"), 53, 1),
+ },
+ },
+ }
+
+ Eth2BtcOrders = []*common.Order{
+ {
+ FromAssetID: Ð,
+ ToAssetID: &BTC,
+ Rate: 1 / 51.0,
+ Utxo: &common.MovUtxo{
+ SourceID: hashPtr(testutil.MustDecodeHash("fba43ff5155209cb1769e2ec0e1d4a33accf899c740865edfc6d1de39b873b29")),
+ SourcePos: 0,
+ Amount: 510,
+ ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("53"), 1, 51.0),
+ },
+ },
+ {
+ FromAssetID: Ð,
+ ToAssetID: &BTC,
+ Rate: 1 / 52.0,
+ Utxo: &common.MovUtxo{
+ SourceID: hashPtr(testutil.MustDecodeHash("05f24bb847db823075d81786aa270748e02602199cd009c0284f928503846a5a")),
+ SourcePos: 0,
+ Amount: 416,
+ ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("54"), 1, 52.0),
+ },
+ },
+ {
+ FromAssetID: Ð,
+ ToAssetID: &BTC,
+ Rate: 1 / 54.0,
+ Utxo: &common.MovUtxo{
+ SourceID: hashPtr(testutil.MustDecodeHash("119a02980796dc352cf6475457463aef5666f66622088de3551fa73a65f0d201")),
+ SourcePos: 0,
+ Amount: 810,
+ ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("55"), 1, 54.0),
+ },
+ },
+ }
+
+ Btc2EthMakerTxs = []*types.Tx {
+ // Btc2EthOrders[0]
+ types.NewTx(types.TxData{
+ Inputs: []*types.TxInput{types.NewSpendInput(nil, *Btc2EthOrders[0].Utxo.SourceID, *Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, Btc2EthOrders[0].Utxo.SourcePos, []byte{0x51})},
+ Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, Btc2EthOrders[0].Utxo.ControlProgram)},
+ }),
+ // Btc2EthOrders[1]
+ types.NewTx(types.TxData{
+ Inputs: []*types.TxInput{types.NewSpendInput(nil, *Btc2EthOrders[1].Utxo.SourceID, *Btc2EthOrders[1].FromAssetID, Btc2EthOrders[1].Utxo.Amount, Btc2EthOrders[1].Utxo.SourcePos, []byte{0x51})},
+ Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Btc2EthOrders[1].FromAssetID, Btc2EthOrders[1].Utxo.Amount, Btc2EthOrders[1].Utxo.ControlProgram)},
+ }),
+ // Btc2EthOrders[2]
+ types.NewTx(types.TxData{
+ Inputs: []*types.TxInput{types.NewSpendInput(nil, *Btc2EthOrders[2].Utxo.SourceID, *Btc2EthOrders[2].FromAssetID, Btc2EthOrders[2].Utxo.Amount, Btc2EthOrders[2].Utxo.SourcePos, []byte{0x51})},
+ Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Btc2EthOrders[2].FromAssetID, Btc2EthOrders[2].Utxo.Amount, Btc2EthOrders[2].Utxo.ControlProgram)},
+ }),
+ // Btc2EthOrders[3]
+ types.NewTx(types.TxData{
+ Inputs: []*types.TxInput{types.NewSpendInput(nil, *Btc2EthOrders[3].Utxo.SourceID, *Btc2EthOrders[3].FromAssetID, Btc2EthOrders[3].Utxo.Amount, Btc2EthOrders[3].Utxo.SourcePos, []byte{0x51})},
+ Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, Btc2EthOrders[3].Utxo.Amount, Btc2EthOrders[3].Utxo.ControlProgram)},
+ }),
+ }
+
+ Eth2BtcMakerTxs = []*types.Tx {
+ // Eth2Btc[0]
+ types.NewTx(types.TxData{
+ Inputs: []*types.TxInput{types.NewSpendInput(nil, *Eth2BtcOrders[0].Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, Eth2BtcOrders[0].Utxo.SourcePos, []byte{0x51})},
+ Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, Eth2BtcOrders[0].Utxo.ControlProgram)},
+ }),
+ // Eth2Btc[1]
+ types.NewTx(types.TxData{
+ Inputs: []*types.TxInput{types.NewSpendInput(nil, *Eth2BtcOrders[1].Utxo.SourceID, *Eth2BtcOrders[1].FromAssetID, Eth2BtcOrders[1].Utxo.Amount, Eth2BtcOrders[1].Utxo.SourcePos, []byte{0x51})},
+ Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Eth2BtcOrders[1].FromAssetID, Eth2BtcOrders[1].Utxo.Amount, Eth2BtcOrders[1].Utxo.ControlProgram)},
+ }),
+ // Eth2Btc[2]
+ types.NewTx(types.TxData{
+ Inputs: []*types.TxInput{types.NewSpendInput(nil, *Eth2BtcOrders[2].Utxo.SourceID, *Eth2BtcOrders[2].FromAssetID, Eth2BtcOrders[2].Utxo.Amount, Eth2BtcOrders[2].Utxo.SourcePos, []byte{0x51})},
+ Outputs: []*types.TxOutput{types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, Eth2BtcOrders[2].Utxo.Amount, Eth2BtcOrders[2].Utxo.ControlProgram)},
+ }),
+ }
+
+ MatchedTxs = []*types.Tx{
+ // partial matched transaction from Btc2EthOrders[0], Eth2BtcOrders[1]
+ types.NewTx(types.TxData{
+ Inputs: []*types.TxInput{
+ types.NewSpendInput([][]byte{vm.Int64Bytes(416), vm.Int64Bytes(0), vm.Int64Bytes(0)}, *Btc2EthOrders[0].Utxo.SourceID, *Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, Btc2EthOrders[0].Utxo.SourcePos, Btc2EthOrders[0].Utxo.ControlProgram),
+ types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *Eth2BtcOrders[1].Utxo.SourceID, *Eth2BtcOrders[1].FromAssetID, Eth2BtcOrders[1].Utxo.Amount, Eth2BtcOrders[1].Utxo.SourcePos, Eth2BtcOrders[1].Utxo.ControlProgram),
+ },
+ Outputs: []*types.TxOutput{
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 416, testutil.MustDecodeHexString("51")),
+ // re-order
+ types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 2, Btc2EthOrders[0].Utxo.ControlProgram),
+ types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 8, testutil.MustDecodeHexString("54")),
+ },
+ }),
+
+ // 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("51")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("53")),
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 10, []byte{0x51}),
+ },
+ }),
+
+ // 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("51")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 10, testutil.MustDecodeHexString("55")),
+ // re-order
+ types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 270, Eth2BtcOrders[2].Utxo.ControlProgram),
+ // fee
+ types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 27, []byte{0x51}),
+ // refund
+ types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 6, testutil.MustDecodeHexString("51")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 7, testutil.MustDecodeHexString("55")),
+ },
+ }),
+ 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("f47177c12d25f5316eb377ea006e77bf07e4f9646860e4641e313e004f9aa989"), *Eth2BtcOrders[2].FromAssetID, 270, 2, Eth2BtcOrders[2].Utxo.ControlProgram),
+ },
+ Outputs: []*types.TxOutput{
+ types.NewIntraChainOutput(*Btc2EthOrders[1].ToAssetID, 270, testutil.MustDecodeHexString("52")),
+ // re-order
+ types.NewIntraChainOutput(*Btc2EthOrders[1].FromAssetID, 15, Btc2EthOrders[1].Utxo.ControlProgram),
+ types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 5, testutil.MustDecodeHexString("55")),
+ },
+ }),
+
+ // 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("51")),
+ // re-order
+ types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 2, Btc2EthOrders[0].Utxo.ControlProgram),
+ types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 8, testutil.MustDecodeHexString("54")),
+ },
+ }),
+ }
+)
+
+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
+}
--- /dev/null
+package mock
+
+import (
+ "sort"
+
+ "github.com/vapor/application/mov/common"
+ "github.com/vapor/errors"
+ "github.com/vapor/protocol/bc"
+ "github.com/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
+}
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 matched tx must p2wmc script")
+ 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")
return state.Height, state.Hash, nil
}
-// ValidateBlock no need to verify the block header, becaure the first module has been verified.
+// 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)
--- /dev/null
+package mov
+
+import (
+ "math"
+ "os"
+ "testing"
+
+ "github.com/vapor/application/mov/common"
+ "github.com/vapor/application/mov/database"
+ "github.com/vapor/application/mov/mock"
+ "github.com/vapor/consensus"
+ dbm "github.com/vapor/database/leveldb"
+ "github.com/vapor/protocol/bc"
+ "github.com/vapor/protocol/bc/types"
+ "github.com/vapor/protocol/vm"
+ "github.com/vapor/testutil"
+)
+
+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("51"), 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("51"), 1, 0))},
+ }),
+ },
+ },
+ verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
+ wantError: errRatioOfTradeLessThanZero,
+ },
+ {
+ desc: "ratio numerator product input 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("51"), math.MaxInt64, 10))},
+ }),
+ },
+ },
+ verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
+ wantError: errNumeratorOfRatioIsOverflow,
+ },
+ }
+
+ 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
+}