9 "github.com/bytom/vapor/application/mov/common"
10 dbm "github.com/bytom/vapor/database/leveldb"
11 "github.com/bytom/vapor/protocol/bc"
12 "github.com/bytom/vapor/protocol/bc/types"
15 // ErrNotInitDBState represent the database state of mov store is not initialized
16 var ErrNotInitDBState = errors.New("database state of mov store is not initialized")
18 // MovStore is the interface for mov's persistent storage
19 type MovStore interface {
21 GetMovDatabaseState() (*common.MovDatabaseState, error)
22 InitDBState(height uint64, hash *bc.Hash) error
23 ListOrders(orderAfter *common.Order) ([]*common.Order, error)
24 ListTradePairsWithStart(fromAssetIDAfter, toAssetIDAfter *bc.AssetID) ([]*common.TradePair, error)
25 ProcessOrders(addOrders []*common.Order, delOrders []*common.Order, blockHeader *types.BlockHeader) error
43 movStore = []byte("MOV:")
44 ordersPrefix = append(movStore, order)
45 tradePairsPrefix = append(movStore, tradePair)
46 bestMatchStore = append(movStore, matchStatus)
49 type orderData struct {
52 RatioDenominator int64
55 func calcOrderKey(fromAssetID, toAssetID *bc.AssetID, utxoHash *bc.Hash, rate float64) []byte {
56 buf := make([]byte, 8)
57 binary.BigEndian.PutUint64(buf, math.Float64bits(rate))
58 key := append(ordersPrefix, fromAssetID.Bytes()...)
59 key = append(key, toAssetID.Bytes()...)
60 key = append(key, buf...)
61 return append(key, utxoHash.Bytes()...)
64 func calcTradePairKey(fromAssetID, toAssetID *bc.AssetID) []byte {
65 key := append(tradePairsPrefix, fromAssetID.Bytes()...)
66 return append(key, toAssetID.Bytes()...)
69 func getAssetIDFromTradePairKey(key []byte, posIndex int) *bc.AssetID {
71 pos := len(tradePairsPrefix) + assetIDLen*posIndex
72 copy(b[:], key[pos:pos+assetIDLen])
73 assetID := bc.NewAssetID(b)
77 func getRateFromOrderKey(key []byte) float64 {
78 ratePos := len(ordersPrefix) + assetIDLen*2
79 return math.Float64frombits(binary.BigEndian.Uint64(key[ratePos : ratePos+rateByteLen]))
82 type tradePairData struct {
86 // LevelDBMovStore is the LevelDB implementation for MovStore
87 type LevelDBMovStore struct {
91 // NewLevelDBMovStore create a new LevelDBMovStore object
92 func NewLevelDBMovStore(db dbm.DB) *LevelDBMovStore {
93 return &LevelDBMovStore{db: db}
96 // Clear will clear all the data of store
97 func (m *LevelDBMovStore) Clear() {
98 batch := m.db.NewBatch()
100 iter := m.db.Iterator()
104 batch.Delete(iter.Key())
109 // GetMovDatabaseState return the current DB's image status
110 func (m *LevelDBMovStore) GetMovDatabaseState() (*common.MovDatabaseState, error) {
111 if value := m.db.Get(bestMatchStore); value != nil {
112 state := &common.MovDatabaseState{}
113 return state, json.Unmarshal(value, state)
116 return nil, ErrNotInitDBState
119 // InitDBState set the DB's image status
120 func (m *LevelDBMovStore) InitDBState(height uint64, hash *bc.Hash) error {
121 state := &common.MovDatabaseState{Height: height, Hash: hash}
122 value, err := json.Marshal(state)
127 m.db.Set(bestMatchStore, value)
131 // ListOrders return n orders after the input order
132 func (m *LevelDBMovStore) ListOrders(orderAfter *common.Order) ([]*common.Order, error) {
133 if orderAfter.FromAssetID == nil || orderAfter.ToAssetID == nil {
134 return nil, errors.New("assetID is nil")
137 orderPrefix := append(ordersPrefix, orderAfter.FromAssetID.Bytes()...)
138 orderPrefix = append(orderPrefix, orderAfter.ToAssetID.Bytes()...)
141 if orderAfter.Rate() > 0 {
142 startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, orderAfter.UTXOHash(), orderAfter.Rate())
145 itr := m.db.IteratorPrefixWithStart(orderPrefix, startKey, false)
148 var orders []*common.Order
149 for txNum := 0; txNum < ordersNum && itr.Next(); txNum++ {
150 orderData := &orderData{}
151 if err := json.Unmarshal(itr.Value(), orderData); err != nil {
155 orders = append(orders, &common.Order{
156 FromAssetID: orderAfter.FromAssetID,
157 ToAssetID: orderAfter.ToAssetID,
158 Utxo: orderData.Utxo,
159 RatioNumerator: orderData.RatioNumerator,
160 RatioDenominator: orderData.RatioDenominator,
166 // ListTradePairsWithStart return n trade pairs after the input trade pair
167 func (m *LevelDBMovStore) ListTradePairsWithStart(fromAssetIDAfter, toAssetIDAfter *bc.AssetID) ([]*common.TradePair, error) {
169 if fromAssetIDAfter != nil && toAssetIDAfter != nil {
170 startKey = calcTradePairKey(fromAssetIDAfter, toAssetIDAfter)
173 itr := m.db.IteratorPrefixWithStart(tradePairsPrefix, startKey, false)
176 var tradePairs []*common.TradePair
177 for txNum := 0; txNum < tradePairsNum && itr.Next(); txNum++ {
179 fromAssetID := getAssetIDFromTradePairKey(key, fromAssetIDPos)
180 toAssetID := getAssetIDFromTradePairKey(key, toAssetIDPos)
182 tradePairData := &tradePairData{}
183 if err := json.Unmarshal(itr.Value(), tradePairData); err != nil {
187 tradePairs = append(tradePairs, &common.TradePair{FromAssetID: fromAssetID, ToAssetID: toAssetID, Count: tradePairData.Count})
189 return tradePairs, nil
192 // ProcessOrders update the DB's image by add new orders, delete the used order
193 func (m *LevelDBMovStore) ProcessOrders(addOrders []*common.Order, delOrders []*common.Order, blockHeader *types.BlockHeader) error {
194 if err := m.checkMovDatabaseState(blockHeader); err != nil {
198 batch := m.db.NewBatch()
199 tradePairsCnt := make(map[string]*common.TradePair)
200 if err := m.addOrders(batch, addOrders, tradePairsCnt); err != nil {
204 m.deleteOrders(batch, delOrders, tradePairsCnt)
205 if err := m.updateTradePairs(batch, tradePairsCnt); err != nil {
209 state, err := m.calcNextDatabaseState(blockHeader)
214 if err := m.saveMovDatabaseState(batch, state); err != nil {
222 func (m *LevelDBMovStore) addOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[string]*common.TradePair) error {
223 for _, order := range orders {
224 orderData := &orderData{
226 RatioNumerator: order.RatioNumerator,
227 RatioDenominator: order.RatioDenominator,
229 data, err := json.Marshal(orderData)
234 key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate())
237 tradePair := &common.TradePair{
238 FromAssetID: order.FromAssetID,
239 ToAssetID: order.ToAssetID,
241 if _, ok := tradePairsCnt[tradePair.Key()]; !ok {
242 tradePairsCnt[tradePair.Key()] = tradePair
244 tradePairsCnt[tradePair.Key()].Count++
249 func (m *LevelDBMovStore) calcNextDatabaseState(blockHeader *types.BlockHeader) (*common.MovDatabaseState, error) {
250 hash := blockHeader.Hash()
251 height := blockHeader.Height
253 state, err := m.GetMovDatabaseState()
258 if *state.Hash == hash {
259 hash = blockHeader.PreviousBlockHash
260 height = blockHeader.Height - 1
263 return &common.MovDatabaseState{Height: height, Hash: &hash}, nil
266 func (m *LevelDBMovStore) checkMovDatabaseState(header *types.BlockHeader) error {
267 state, err := m.GetMovDatabaseState()
272 if (*state.Hash == header.PreviousBlockHash && (state.Height+1) == header.Height) || *state.Hash == header.Hash() {
276 return errors.New("the status of the block is inconsistent with that of mov-database")
279 func (m *LevelDBMovStore) deleteOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[string]*common.TradePair) {
280 for _, order := range orders {
281 key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate())
284 tradePair := &common.TradePair{
285 FromAssetID: order.FromAssetID,
286 ToAssetID: order.ToAssetID,
288 if _, ok := tradePairsCnt[tradePair.Key()]; !ok {
289 tradePairsCnt[tradePair.Key()] = tradePair
291 tradePairsCnt[tradePair.Key()].Count--
295 func (m *LevelDBMovStore) saveMovDatabaseState(batch dbm.Batch, state *common.MovDatabaseState) error {
296 value, err := json.Marshal(state)
301 batch.Set(bestMatchStore, value)
305 func (m *LevelDBMovStore) updateTradePairs(batch dbm.Batch, tradePairs map[string]*common.TradePair) error {
306 for _, v := range tradePairs {
307 key := calcTradePairKey(v.FromAssetID, v.ToAssetID)
308 tradePairData := &tradePairData{}
309 if value := m.db.Get(key); value != nil {
310 if err := json.Unmarshal(value, tradePairData); err != nil {
315 if tradePairData.Count += v.Count; tradePairData.Count < 0 {
316 return errors.New("negative trade count")
319 if tradePairData.Count > 0 {
320 value, err := json.Marshal(tradePairData)
325 batch.Set(key, value)