From: wz Date: Thu, 10 Oct 2019 08:21:31 +0000 (+0800) Subject: Dex database logic implementation (#404) X-Git-Tag: v1.0.5~39 X-Git-Url: http://git.osdn.net/view?p=bytom%2Fvapor.git;a=commitdiff_plain;h=8703d015edb1a0f6533bbcb5655eedaa54b43334 Dex database logic implementation (#404) * add logic * Add logic implementation * fix name * fix * fix * fix * fix and add test * fix test * fix * fix * fix space line --- diff --git a/application/dex/database/dex_store.go b/application/dex/database/dex_store.go deleted file mode 100644 index 9035ac01..00000000 --- a/application/dex/database/dex_store.go +++ /dev/null @@ -1,78 +0,0 @@ -package database - -import ( - "encoding/binary" - "math" - - "github.com/vapor/application/dex/common" - dbm "github.com/vapor/database/leveldb" - "github.com/vapor/protocol/bc" -) - -const ( - order byte = iota - tradePair - matchStatus -) - -var ( - dexStore = []byte("DEX:") - ordersPreFix = append(dexStore, order) - tradePairPreFix = append(dexStore, tradePair) - bestMatchStore = append(dexStore, matchStatus) -) - -func calcOrdersPrefix(fromAssetID, toAssetID *bc.AssetID, utxoHash *bc.Hash, rate float64) []byte { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, math.Float64bits(rate)) - key := append(ordersPreFix, fromAssetID.Bytes()...) - key = append(key, toAssetID.Bytes()...) - key = append(key, buf...) - return append(key, utxoHash.Bytes()...) -} - -func calcTradePairPreFix(fromAssetID, toAssetID *bc.Hash) []byte { - key := append(ordersPreFix, fromAssetID.Bytes()...) - return append(key, toAssetID.Bytes()...) -} - -type DexTradeOrderDB struct { - db dbm.DB -} - -func (d *DexTradeOrderDB) GetTradePairsWithStart(start []byte) ([]common.TradePair, error) { - return nil, nil -} - -func (d *DexTradeOrderDB) addTradePair() error { - return nil -} - -func (d *DexTradeOrderDB) deleteTradePair() error { - return nil -} - -func (d *DexTradeOrderDB) ProcessOrders(addOrders []*common.Order, delOreders []*common.Order, height uint64, blockHash *bc.Hash) error { - - return nil -} - -func (d *DexTradeOrderDB) addOrders(orders []*common.Order) error { - return nil -} - -func (d *DexTradeOrderDB) deleteOrder(orders []*common.Order) error { - return nil -} - -func (d *DexTradeOrderDB) ListOrders(fromAssetID, toAssetID string, rateAfter float64) ([]*common.Order, error) { - return nil, nil -} - -func (d *DexTradeOrderDB) GetDexDatabaseState() (*common.DexDatabaseState, error) { - return nil, nil -} - -func (d *DexTradeOrderDB) saveDexDatabaseState(state *common.DexDatabaseState) error { - return nil -} diff --git a/application/dex/database/dex_store_test.go b/application/dex/database/dex_store_test.go deleted file mode 100644 index c72e6322..00000000 --- a/application/dex/database/dex_store_test.go +++ /dev/null @@ -1,323 +0,0 @@ -package database - -import ( - "encoding/binary" - "encoding/json" - "io/ioutil" - "math" - "os" - "testing" - - "github.com/stretchr/testify/require" - "golang.org/x/crypto/sha3" - - "github.com/vapor/application/dex/common" - "github.com/vapor/database/leveldb" - "github.com/vapor/protocol/bc" - "github.com/vapor/testutil" -) - -func TestSortOrderKey(t *testing.T) { - dirname, err := ioutil.TempDir("", "db_common_test") - require.Nil(t, err) - - db, err := leveldb.NewGoLevelDB("testdb", dirname) - if err != nil { - t.Fatal(err) - } - - defer func() { - db.Close() - os.RemoveAll(dirname) - }() - - type expectedData struct { - rate float64 - utxoHash string - } - - cases := []struct { - orders []common.Order - want []expectedData - }{ - { - orders: []common.Order{ - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 1.00090, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 21}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00090, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 22}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00097, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 23}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00098, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 13}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00098, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 24}, - Amount: 10, - SourcePos: 1, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00099, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 24}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00096, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 25}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00095, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 26}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00091, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 26}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00092, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 27}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00093, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 28}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00094, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 29}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00077, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 30}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 0.00088, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 31}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 999999.9521, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 32}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - common.Order{ - FromAssetID: &bc.AssetID{V0: 1}, - ToAssetID: &bc.AssetID{V0: 0}, - Rate: 888888.7954, - Utxo: &common.DexUtxo{ - SourceID: &bc.Hash{V0: 33}, - Amount: 1, - SourcePos: 0, - ControlProgram: []byte("aa"), - }, - }, - }, - want: []expectedData{ - expectedData{ - rate: 0.00077, - utxoHash: "7967bb8c4cca951749553e9c7787255d35a032d9e1acecefe4011c8095dc8e6f", - }, - expectedData{ - rate: 0.00088, - utxoHash: "215a6e7e3a5006151bd0b81c54fcccda0381f3a22e7b6646ed201c35f9fa6c5a", - }, - expectedData{ - rate: 0.00090, - utxoHash: "cb373d3a383d30eb2863317ea2cfb5b4b269772fbc0fb8413a2be7d7b69ec2b9", - }, - expectedData{ - rate: 0.00091, - utxoHash: "298c39d327cb4b0dcefcf701aa8d1b559f1de0148e9bcbe14da48cfa268c01ea", - }, - expectedData{ - rate: 0.00092, - utxoHash: "b2c59190fb0d948c9545c146a69b1f17503b2d280b2f3f45ecc0a7b7e2cd1784", - }, - expectedData{ - rate: 0.00093, - utxoHash: "80b44aae2b2cf57bd2cf77cb88f0d8363066f5f16a17a3e85224ecbbc6387d8b", - }, - expectedData{ - rate: 0.00094, - utxoHash: "4843adc8c4a50672a022e5f377dfd2ac11119364dc0a547be45b4a5edacef33b", - }, - expectedData{ - rate: 0.00095, - utxoHash: "298c39d327cb4b0dcefcf701aa8d1b559f1de0148e9bcbe14da48cfa268c01ea", - }, - expectedData{ - rate: 0.00096, - utxoHash: "d8d1a85303e9ac738e675b874b866e5ffbfa10e05201113404dde544055a18b9", - }, - expectedData{ - rate: 0.00097, - utxoHash: "2305be66ab9648b713a58e3807fa1cba1f84e5d11359b316e967d98e9a7667da", - }, - expectedData{ - rate: 0.00098, - utxoHash: "030dc8a868a3e534799d465ebc8209eb32d9465985dc8c35e731b124bf3ffbcf", - }, - expectedData{ - rate: 0.00098, - utxoHash: "8222a9a43b3951f247612ddce2fe36f96cd843bc0dfef86c7d0ef5335331f11f", - }, - expectedData{ - rate: 0.00099, - utxoHash: "a40bd183cd2ff2b52faac5ebc2cfc1e36104cbc92bcebac011b45792b39e380e", - }, - expectedData{ - rate: 1.0009, - utxoHash: "118b2c40848887614d99b0e7eb6c88a10b47196e6aca3ff2eeab452bfdb9cfcb", - }, - expectedData{ - rate: 888888.7954, - utxoHash: "545a5c6f7ff9be19ed07a7246277c67d661f9cc7d8956bb81ce9a4045fba3720", - }, - expectedData{ - rate: 999999.9521, - utxoHash: "d9f7725d908510268c7bdecd29cb2031baab93b9bfa69108eb0a926ba7ae18f9", - }, - }, - }, - } - - for i, c := range cases { - for _, order := range c.orders { - data, err := json.Marshal(order.Utxo) - if err != nil { - t.Fatal(err) - } - utxoHash := bc.NewHash(sha3.Sum256(data)) - key := calcOrdersPrefix(order.FromAssetID, order.ToAssetID, &utxoHash, order.Rate) - db.SetSync(key, data) - } - - got := []expectedData{} - - itr := db.IteratorPrefixWithStart(nil, nil, false) - for itr.Next() { - key := itr.Key() - pos := len(ordersPreFix) + 32*2 - b := [32]byte{} - copy(b[:], key[pos+8:]) - utxoHash := bc.NewHash(b) - got = append(got, expectedData{ - rate: math.Float64frombits(binary.BigEndian.Uint64(key[pos : pos+8])), - utxoHash: utxoHash.String(), - }) - } - itr.Release() - - if !testutil.DeepEqual(c.want, got) { - t.Errorf("case %v: got recovery status, got: %v, want: %v.", i, got, c.want) - } - - } -} diff --git a/application/dex/common/type.go b/application/mov/common/type.go similarity index 78% rename from application/dex/common/type.go rename to application/mov/common/type.go index 63baf2b8..031e3781 100644 --- a/application/dex/common/type.go +++ b/application/mov/common/type.go @@ -2,7 +2,7 @@ package common import "github.com/vapor/protocol/bc" -type DexUtxo struct { +type MovUtxo struct { SourceID *bc.Hash SourcePos uint64 Amount uint64 @@ -12,17 +12,17 @@ type DexUtxo struct { type Order struct { FromAssetID *bc.AssetID ToAssetID *bc.AssetID - Utxo *DexUtxo + Utxo *MovUtxo Rate float64 } type TradePair struct { FromAssetID *bc.AssetID ToAssetID *bc.AssetID - Count uint64 + Count int } -type DexDatabaseState struct { +type MovDatabaseState struct { Height uint64 Hash *bc.Hash } diff --git a/application/mov/database/mov_store.go b/application/mov/database/mov_store.go new file mode 100644 index 00000000..4aa831a3 --- /dev/null +++ b/application/mov/database/mov_store.go @@ -0,0 +1,267 @@ +package database + +import ( + "encoding/binary" + "encoding/json" + "errors" + "math" + + "github.com/vapor/application/mov/common" + dbm "github.com/vapor/database/leveldb" + "github.com/vapor/protocol/bc" + "github.com/vapor/protocol/bc/types" +) + +const ( + order byte = iota + tradePair + matchStatus + + tradePairsNum = 1024 + ordersNum = 10240 + assetIDLen = 32 + rateByteLen = 8 +) + +var ( + movStore = []byte("MOV:") + ordersPrefix = append(movStore, order) + tradePairsPrefix = append(movStore, tradePair) + bestMatchStore = append(movStore, matchStatus) +) + +func calcOrderKey(fromAssetID, toAssetID *bc.AssetID, utxoHash *bc.Hash, rate float64) []byte { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, math.Float64bits(rate)) + key := append(ordersPrefix, fromAssetID.Bytes()...) + key = append(key, toAssetID.Bytes()...) + key = append(key, buf...) + return append(key, utxoHash.Bytes()...) +} + +func calcTradePairKey(fromAssetID, toAssetID *bc.AssetID) []byte { + key := append(tradePairsPrefix, fromAssetID.Bytes()...) + 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 + copy(b[:], key[pos:pos+assetIDLen]) + assetID := bc.NewAssetID(b) + return &assetID +} + +func getRateFromOrderKey(key []byte, prefix []byte) float64 { + ratePos := len(prefix) + assetIDLen*2 + return math.Float64frombits(binary.BigEndian.Uint64(key[ratePos : ratePos+rateByteLen])) +} + +type tradePairData struct { + Count int +} + +type MovStore 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 + } + + db.Set(bestMatchStore, value) + } + return &MovStore{db: db}, nil +} + +func (m *MovStore) ListOrders(orderAfter *common.Order) ([]*common.Order, error) { + if orderAfter.FromAssetID == nil || orderAfter.ToAssetID == nil { + return nil, errors.New("assetID is nil") + } + + orderPrefix := append(ordersPrefix, orderAfter.FromAssetID.Bytes()...) + orderPrefix = append(orderPrefix, orderAfter.ToAssetID.Bytes()...) + + var startKey []byte + if orderAfter.Rate > 0 { + startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, calcUTXOHash(orderAfter), orderAfter.Rate) + } + + itr := m.db.IteratorPrefixWithStart(orderPrefix, startKey, false) + defer itr.Release() + + var orders []*common.Order + for txNum := 0; txNum < ordersNum && itr.Next(); txNum++ { + movUtxo := &common.MovUtxo{} + if err := json.Unmarshal(itr.Value(), movUtxo); 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) + } + return orders, nil +} + +func (m *MovStore) ProcessOrders(addOrders []*common.Order, delOreders []*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) + if err := m.addOrders(batch, addOrders, tradePairsCnt); err != nil { + return err + } + + m.deleteOrders(batch, delOreders, 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 { + return err + } + + batch.Write() + return nil +} + +func (m *MovStore) addOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[common.TradePair]int) error { + for _, order := range orders { + data, err := json.Marshal(order.Utxo) + if err != nil { + return err + } + + key := calcOrderKey(order.FromAssetID, order.ToAssetID, calcUTXOHash(order), order.Rate) + batch.Set(key, data) + + tradePair := common.TradePair{ + FromAssetID: order.FromAssetID, + ToAssetID: order.ToAssetID, + } + tradePairsCnt[tradePair] += 1 + } + 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) + + tradePair := common.TradePair{ + FromAssetID: order.FromAssetID, + ToAssetID: order.ToAssetID, + } + tradePairsCnt[tradePair] -= 1 + } +} + +func (m *MovStore) 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") +} + +func (m *MovStore) 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, tradePairsPrefix, 0) + toAssetID := getAssetIDFromTradePairKey(key, tradePairsPrefix, 1) + + 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 +} + +func (m *MovStore) updateTradePairs(batch dbm.Batch, tradePairs map[common.TradePair]int) error { + for k, v := range tradePairs { + key := calcTradePairKey(k.FromAssetID, k.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 > 0 { + value, err := json.Marshal(tradePairData) + if err != nil { + return err + } + + batch.Set(key, value) + } else { + batch.Delete(key) + } + } + 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 +} diff --git a/application/mov/database/mov_store_test.go b/application/mov/database/mov_store_test.go new file mode 100644 index 00000000..7f17fc70 --- /dev/null +++ b/application/mov/database/mov_store_test.go @@ -0,0 +1,3015 @@ +package database + +import ( + "encoding/json" + "errors" + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/vapor/application/mov/common" + "github.com/vapor/consensus" + "github.com/vapor/database/leveldb" + dbm "github.com/vapor/database/leveldb" + chainjson "github.com/vapor/encoding/json" + "github.com/vapor/protocol/bc" + "github.com/vapor/protocol/bc/types" + "github.com/vapor/testutil" +) + +var ( + assetID1 = &bc.AssetID{V0: 1} + assetID2 = &bc.AssetID{V0: 2} + assetID3 = &bc.AssetID{V0: 3} + assetID4 = &bc.AssetID{V0: 4} + assetID5 = &bc.AssetID{V0: 5} + 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[:], + }, + } + + hash := calcUTXOHash(order) + if hash.String() != wantHash { + t.Fatal("The function is incorrect") + } + +} + +func TestSortOrderKey(t *testing.T) { + dirname, err := ioutil.TempDir("", "db_common_test") + require.Nil(t, err) + + db, err := leveldb.NewGoLevelDB("testdb", dirname) + if err != nil { + t.Fatal(err) + } + + defer func() { + db.Close() + os.RemoveAll(dirname) + }() + + type expectedData struct { + rate float64 + utxoHash string + } + + cases := []struct { + orders []common.Order + want []expectedData + }{ + { + orders: []common.Order{ + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 1.00090, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 21}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00090, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 22}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00097, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 23}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00098, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 13}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00098, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 24}, + Amount: 10, + SourcePos: 1, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00099, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 24}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00096, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 25}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00095, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 26}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00091, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 26}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00092, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 27}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00093, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 28}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00094, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 29}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00077, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 30}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 0.00088, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 31}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 999999.9521, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 32}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + common.Order{ + FromAssetID: &bc.AssetID{V0: 1}, + ToAssetID: &bc.AssetID{V0: 0}, + Rate: 888888.7954, + Utxo: &common.MovUtxo{ + SourceID: &bc.Hash{V0: 33}, + Amount: 1, + SourcePos: 0, + ControlProgram: []byte("aa"), + }, + }, + }, + want: []expectedData{ + expectedData{ + rate: 0.00077, + utxoHash: "f1b85307cf1f4eb6b193b6fc289413fdbb12bc362ced399762589b016e54dd02", + }, + expectedData{ + rate: 0.00088, + utxoHash: "49ef60af0f24962ed129a73142048ed0cb589041c629353932e3c3e0a4e822ba", + }, + expectedData{ + rate: 0.00090, + utxoHash: "67b2ac6ea71b271e72836e162811866f291ed2fab106c43519ca0c94ef8a5dce", + }, + expectedData{ + rate: 0.00091, + utxoHash: "47ff45b7b530512142981c2cee82faad63d6c9e7ffed0e72c3e42668f13b296f", + }, + expectedData{ + rate: 0.00092, + utxoHash: "b750d0b95f38043362c8335f242f97cfd3e1cada8fd171b914471a16cc0f14c6", + }, + expectedData{ + rate: 0.00093, + utxoHash: "04386ef57f0ca1be0a9be46c413900adbc0ab1e90e773959924aa73ca62edf64", + }, + expectedData{ + rate: 0.00094, + utxoHash: "c0fe6227c50da350a5e7b4ff85c18e9c901c323521067b9142acd128cf13ae82", + }, + expectedData{ + rate: 0.00095, + utxoHash: "47ff45b7b530512142981c2cee82faad63d6c9e7ffed0e72c3e42668f13b296f", + }, + expectedData{ + rate: 0.00096, + utxoHash: "bc92df1cbd20c98b0d18c9d93422a770849235867522a08e492196d16ed0a422", + }, + expectedData{ + rate: 0.00097, + utxoHash: "0cc0ded6fb337a3c5e6e4d008d6167dc58bdede43713898e914d65cda3b8499a", + }, + expectedData{ + rate: 0.00098, + utxoHash: "1fa9fae83d0a5401a4e92f80636966486e763eecca588aa11dff02b415320602", + }, + expectedData{ + rate: 0.00098, + utxoHash: "a4bc534c267d35a9eafc25cd66e0cb270a2537a51186605b7f7591bc567ab4c6", + }, + expectedData{ + rate: 0.00099, + utxoHash: "fdedf4117def659e07cc8a8ca318d21ae577a05e1a0197844b54d493bdae5854", + }, + expectedData{ + rate: 1.0009, + utxoHash: "20be3bd2d406bb7fe6627b32768fb2073e997b962a4badfa4384210fed2ab9c6", + }, + expectedData{ + rate: 888888.7954, + utxoHash: "72192f56b9525c74c6a9f0419563bc0da76b0f3d6e89d9decdb6e67786ac3909", + }, + expectedData{ + rate: 999999.9521, + utxoHash: "7886844334659b4feffc41528cf81192925d3aa4a5ccb3652200b9073b7d47c3", + }, + }, + }, + } + + for i, c := range cases { + for _, order := range c.orders { + key := calcOrderKey(order.FromAssetID, order.ToAssetID, calcUTXOHash(&order), order.Rate) + data, err := json.Marshal(order.Utxo) + if err != nil { + t.Fatal(err) + } + + db.SetSync(key, data) + } + + got := []expectedData{} + + itr := db.IteratorPrefixWithStart(nil, nil, false) + for itr.Next() { + key := itr.Key() + pos := len(ordersPrefix) + assetIDLen*2 + b := [32]byte{} + copy(b[:], key[pos+8:]) + utxoHash := bc.NewHash(b) + + rate := getRateFromOrderKey(key, ordersPrefix) + got = append(got, expectedData{ + rate: rate, + utxoHash: utxoHash.String(), + }) + } + itr.Release() + + if !testutil.DeepEqual(c.want, got) { + t.Errorf("case %v: got recovery status, got: %v, want: %v.", i, got, c.want) + } + + } +} + +func TestMovStore(t *testing.T) { + cases := []struct { + desc string + beforeOrders []*common.Order + beforeTradePairs []*common.TradePair + beforeDBStatus *common.MovDatabaseState + addOrders []*common.Order + delOrders []*common.Order + blockHeader *types.BlockHeader + wantOrders []*common.Order + wantTradePairs []*common.TradePair + wantDBState *common.MovDatabaseState + }{ + { + 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{ + 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: 1, PreviousBlockHash: bc.Hash{V0: 524821139490765641, V1: 2484214155808702787, V2: 9108473449351508820, V3: 7972721253564512122}}, + 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"), + }, + }, + }, + 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, + }, + }, + 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"), + }, + }, + }, + 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"), + }, + }, + }, + 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{ + &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{ + 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.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: 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"), + }, + }, + }, + }, + { + 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"), + }, + }, + }, + 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"), + }, + }, + }, + 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"), + }, + }, + }, + }, + } + + 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) + movStore.updateTradePairs(batch, tradePairsCnt) + batch.Write() + + tradePairsCnt = make(map[common.TradePair]int) + movStore.addOrders(batch, c.addOrders, tradePairsCnt) + batch.Write() + + gotOrders, err := movStore.ListOrders(&common.Order{FromAssetID: assetID1, ToAssetID: assetID2}) + 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 TestDelOrders(t *testing.T) { + cases := []struct { + desc string + beforeOrders []*common.Order + delOrders []*common.Order + wantOrders []*common.Order + err error + }{ + { + 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"), + }, + }, + }, + wantOrders: []*common.Order{}, + err: errors.New("don't find trade pair"), + }, + { + 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"), + }, + }, + }, + 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"), + }, + }, + }, + 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"), + }, + }, + }, + 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"), + }, + }, + }, + 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"), + }, + }, + }, + wantOrders: []*common.Order{}, + err: nil, + }, + } + + 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) + movStore.updateTradePairs(batch, tradePairsCnt) + batch.Write() + + tradePairsCnt = make(map[common.TradePair]int) + movStore.deleteOrders(batch, c.delOrders, tradePairsCnt) + movStore.updateTradePairs(batch, tradePairsCnt) + batch.Write() + + gotOrders, err := movStore.ListOrders(&common.Order{FromAssetID: assetID1, ToAssetID: assetID2}) + 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 TestListTradePairsWithStart(t *testing.T) { + cases := []struct { + desc string + storeTradePairs map[common.TradePair]int + query *common.TradePair + wantTradePairs []*common.TradePair + }{ + { + desc: "empty", + query: &common.TradePair{}, + wantTradePairs: []*common.TradePair{}, + }, + { + 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, + }, + query: &common.TradePair{}, + wantTradePairs: []*common.TradePair{ + &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2, Count: 1}, + &common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3, Count: 2}, + &common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4, Count: 3}, + &common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5, Count: 4}, + &common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6, Count: 5}, + &common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7, Count: 6}, + }, + }, + { + 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, + }, + query: &common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4, Count: 3}, + wantTradePairs: []*common.TradePair{ + &common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5, Count: 4}, + &common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6, Count: 5}, + &common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7, Count: 6}, + &common.TradePair{FromAssetID: assetID6, ToAssetID: assetID8, Count: 7}, + }, + }, + } + + 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() + movStore.updateTradePairs(batch, c.storeTradePairs) + batch.Write() + + gotTradePairs, err := movStore.ListTradePairsWithStart(c.query.FromAssetID, c.query.ToAssetID) + 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) + } + + testDB.Close() + os.RemoveAll("temp") + } +} + +func TestUpdateTradePairs(t *testing.T) { + cases := []struct { + desc string + beforeTradePairs map[common.TradePair]int + addTradePairs map[common.TradePair]int + delTradePairs map[common.TradePair]int + 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, + }, + wantTradePairs: []*common.TradePair{ + &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2, Count: 1}, + &common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3, Count: 2}, + &common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4, Count: 3}, + &common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5, Count: 4}, + &common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6, Count: 5}, + &common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7, Count: 6}, + }, + }, + { + 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, + }, + 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, + }, + wantTradePairs: []*common.TradePair{ + &common.TradePair{FromAssetID: assetID1, ToAssetID: assetID2, Count: 1}, + &common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3, Count: 2}, + &common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4, Count: 3}, + &common.TradePair{FromAssetID: assetID4, ToAssetID: assetID5, Count: 4}, + &common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6, Count: 5}, + &common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7, Count: 6}, + }, + }, + { + 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, + }, + wantTradePairs: []*common.TradePair{ + &common.TradePair{FromAssetID: assetID2, ToAssetID: assetID3, Count: 2}, + &common.TradePair{FromAssetID: assetID3, ToAssetID: assetID4, Count: 3}, + &common.TradePair{FromAssetID: assetID4, ToAssetID: assetID6, Count: 3}, + &common.TradePair{FromAssetID: assetID5, ToAssetID: assetID7, Count: 2}, + }, + }, + } + + 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() + movStore.updateTradePairs(batch, c.beforeTradePairs) + batch.Write() + + movStore.updateTradePairs(batch, c.addTradePairs) + movStore.updateTradePairs(batch, c.delTradePairs) + batch.Write() + + 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) + } + + testDB.Close() + os.RemoveAll("temp") + } +} + +func TestCheckMovDatabaseState(t *testing.T) { + cases := []struct { + desc string + beforeDBStatus *common.MovDatabaseState + blockHeader *types.BlockHeader + err error + }{ + { + desc: "attach Block", + beforeDBStatus: &common.MovDatabaseState{Height: 1, Hash: &bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}}, + blockHeader: &types.BlockHeader{Height: 2, PreviousBlockHash: bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}}, + err: nil, + }, + { + desc: "error attach Block", + beforeDBStatus: &common.MovDatabaseState{Height: 1, Hash: &bc.Hash{V0: 14213576368347360351, V1: 16287398171800437029, V2: 9513543230620030445, V3: 8534035697182508177}}, + blockHeader: &types.BlockHeader{Height: 2, PreviousBlockHash: bc.Hash{}}, + err: errors.New("the status of the block is inconsistent with that of mov-database"), + }, + + { + desc: "detach Block", + beforeDBStatus: &common.MovDatabaseState{Height: 5, Hash: &bc.Hash{V0: 3724755213446347384, V1: 158878632373345042, V2: 18283800951484248781, V3: 7520797730449067221}}, + blockHeader: &types.BlockHeader{Height: 4}, + err: nil, + }, + { + desc: "error detach Block", + beforeDBStatus: &common.MovDatabaseState{Height: 5, Hash: &bc.Hash{V0: 3724755213446347384, V1: 158878632373345042, V2: 18283800951484248781, V3: 7520797730449067221}}, + blockHeader: &types.BlockHeader{Height: 3}, + err: errors.New("the status of the block is inconsistent with that of mov-database"), + }, + } + + 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() + movStore.saveMovDatabaseState(batch, c.beforeDBStatus) + batch.Write() + + if err := movStore.checkMovDatabaseState(c.blockHeader); c.err != nil && c.err.Error() != err.Error() { + t.Fatalf("case %d: checkMovDatabaseState error %v.", i, err) + } + + testDB.Close() + os.RemoveAll("temp") + } + +}