OSDN Git Service

add maker taker (#537)
[bytom/vapor.git] / application / mov / database / mov_store.go
1 package database
2
3 import (
4         "encoding/binary"
5         "encoding/json"
6         "errors"
7         "math"
8
9         "github.com/bytom/vapor/application/mov/common"
10         "github.com/bytom/vapor/consensus/segwit"
11         dbm "github.com/bytom/vapor/database/leveldb"
12         "github.com/bytom/vapor/protocol/bc"
13         "github.com/bytom/vapor/protocol/bc/types"
14 )
15
16 // ErrNotInitDBState represent the database state of mov store is not initialized
17 var ErrNotInitDBState = errors.New("database state of mov store is not initialized")
18
19 // MovStore is the interface for mov's persistent storage
20 type MovStore interface {
21         Clear()
22         GetMovDatabaseState() (*common.MovDatabaseState, error)
23         InitDBState(height uint64, hash *bc.Hash) error
24         ListOrders(orderAfter *common.Order) ([]*common.Order, error)
25         ListTradePairsWithStart(fromAssetIDAfter, toAssetIDAfter *bc.AssetID) ([]*common.TradePair, error)
26         ProcessOrders(addOrders []*common.Order, delOrders []*common.Order, blockHeader *types.BlockHeader) error
27 }
28
29 const (
30         order byte = iota + 1
31         tradePair
32         matchStatus
33
34         fromAssetIDPos = 0
35         toAssetIDPos   = 1
36         assetIDLen     = 32
37         rateByteLen    = 8
38
39         tradePairsNum = 32
40         ordersNum     = 128
41 )
42
43 var (
44         movStore         = []byte("MOV:")
45         ordersPrefix     = append(movStore, order)
46         tradePairsPrefix = append(movStore, tradePair)
47         bestMatchStore   = append(movStore, matchStatus)
48 )
49
50 type orderData struct {
51         Utxo             *common.MovUtxo
52         RatioNumerator   int64
53         RatioDenominator int64
54         BlockHeight      uint64
55         TxIndex          uint64
56 }
57
58 func calcOrderKey(fromAssetID, toAssetID *bc.AssetID, utxoHash *bc.Hash, rate float64) []byte {
59         buf := make([]byte, 8)
60         binary.BigEndian.PutUint64(buf, math.Float64bits(rate))
61         key := append(ordersPrefix, fromAssetID.Bytes()...)
62         key = append(key, toAssetID.Bytes()...)
63         key = append(key, buf...)
64         return append(key, utxoHash.Bytes()...)
65 }
66
67 func calcTradePairKey(fromAssetID, toAssetID *bc.AssetID) []byte {
68         key := append(tradePairsPrefix, fromAssetID.Bytes()...)
69         return append(key, toAssetID.Bytes()...)
70 }
71
72 func getAssetIDFromTradePairKey(key []byte, posIndex int) *bc.AssetID {
73         b := [32]byte{}
74         pos := len(tradePairsPrefix) + assetIDLen*posIndex
75         copy(b[:], key[pos:pos+assetIDLen])
76         assetID := bc.NewAssetID(b)
77         return &assetID
78 }
79
80 func getRateFromOrderKey(key []byte) float64 {
81         ratePos := len(ordersPrefix) + assetIDLen*2
82         return math.Float64frombits(binary.BigEndian.Uint64(key[ratePos : ratePos+rateByteLen]))
83 }
84
85 type tradePairData struct {
86         Count int
87 }
88
89 // LevelDBMovStore is the LevelDB implementation for MovStore
90 type LevelDBMovStore struct {
91         db dbm.DB
92 }
93
94 // NewLevelDBMovStore create a new LevelDBMovStore object
95 func NewLevelDBMovStore(db dbm.DB) *LevelDBMovStore {
96         return &LevelDBMovStore{db: db}
97 }
98
99 // Clear will clear all the data of store
100 func (m *LevelDBMovStore) Clear() {
101         batch := m.db.NewBatch()
102
103         iter := m.db.Iterator()
104         defer iter.Release()
105
106         for iter.Next() {
107                 batch.Delete(iter.Key())
108         }
109         batch.Write()
110 }
111
112 // GetMovDatabaseState return the current DB's image status
113 func (m *LevelDBMovStore) GetMovDatabaseState() (*common.MovDatabaseState, error) {
114         if value := m.db.Get(bestMatchStore); value != nil {
115                 state := &common.MovDatabaseState{}
116                 return state, json.Unmarshal(value, state)
117         }
118
119         return nil, ErrNotInitDBState
120 }
121
122 // InitDBState set the DB's image status
123 func (m *LevelDBMovStore) InitDBState(height uint64, hash *bc.Hash) error {
124         state := &common.MovDatabaseState{Height: height, Hash: hash}
125         value, err := json.Marshal(state)
126         if err != nil {
127                 return err
128         }
129
130         m.db.Set(bestMatchStore, value)
131         return nil
132 }
133
134 // ListOrders return n orders after the input order
135 func (m *LevelDBMovStore) ListOrders(orderAfter *common.Order) ([]*common.Order, error) {
136         if orderAfter.FromAssetID == nil || orderAfter.ToAssetID == nil {
137                 return nil, errors.New("assetID is nil")
138         }
139
140         orderPrefix := append(ordersPrefix, orderAfter.FromAssetID.Bytes()...)
141         orderPrefix = append(orderPrefix, orderAfter.ToAssetID.Bytes()...)
142
143         var startKey []byte
144         if orderAfter.Rate() > 0 {
145                 startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, orderAfter.UTXOHash(), orderAfter.Rate())
146         }
147
148         itr := m.db.IteratorPrefixWithStart(orderPrefix, startKey, false)
149         defer itr.Release()
150
151         var orders []*common.Order
152         for txNum := 0; txNum < ordersNum && itr.Next(); txNum++ {
153                 orderData := &orderData{}
154                 if err := json.Unmarshal(itr.Value(), orderData); err != nil {
155                         return nil, err
156                 }
157
158                 contractArgs, err := segwit.DecodeP2WMCProgram(orderData.Utxo.ControlProgram)
159                 if err != nil {
160                         return nil, err
161                 }
162
163                 orders = append(orders, &common.Order{
164                         FromAssetID:      orderAfter.FromAssetID,
165                         ToAssetID:        orderAfter.ToAssetID,
166                         Utxo:             orderData.Utxo,
167                         RatioNumerator:   orderData.RatioNumerator,
168                         RatioDenominator: orderData.RatioDenominator,
169                         BlockHeight:      orderData.BlockHeight,
170                         TxIndex:          orderData.TxIndex,
171                         SellerProgram:    contractArgs.SellerProgram,
172                 })
173         }
174         return orders, nil
175 }
176
177 // ListTradePairsWithStart return n trade pairs after the input trade pair
178 func (m *LevelDBMovStore) ListTradePairsWithStart(fromAssetIDAfter, toAssetIDAfter *bc.AssetID) ([]*common.TradePair, error) {
179         var startKey []byte
180         if fromAssetIDAfter != nil && toAssetIDAfter != nil {
181                 startKey = calcTradePairKey(fromAssetIDAfter, toAssetIDAfter)
182         }
183
184         itr := m.db.IteratorPrefixWithStart(tradePairsPrefix, startKey, false)
185         defer itr.Release()
186
187         var tradePairs []*common.TradePair
188         for txNum := 0; txNum < tradePairsNum && itr.Next(); txNum++ {
189                 key := itr.Key()
190                 fromAssetID := getAssetIDFromTradePairKey(key, fromAssetIDPos)
191                 toAssetID := getAssetIDFromTradePairKey(key, toAssetIDPos)
192
193                 tradePairData := &tradePairData{}
194                 if err := json.Unmarshal(itr.Value(), tradePairData); err != nil {
195                         return nil, err
196                 }
197
198                 tradePairs = append(tradePairs, &common.TradePair{FromAssetID: fromAssetID, ToAssetID: toAssetID, Count: tradePairData.Count})
199         }
200         return tradePairs, nil
201 }
202
203 // ProcessOrders update the DB's image by add new orders, delete the used order
204 func (m *LevelDBMovStore) ProcessOrders(addOrders []*common.Order, delOrders []*common.Order, blockHeader *types.BlockHeader) error {
205         if err := m.checkMovDatabaseState(blockHeader); err != nil {
206                 return err
207         }
208
209         batch := m.db.NewBatch()
210         tradePairsCnt := make(map[string]*common.TradePair)
211         if err := m.addOrders(batch, addOrders, tradePairsCnt); err != nil {
212                 return err
213         }
214
215         m.deleteOrders(batch, delOrders, tradePairsCnt)
216         if err := m.updateTradePairs(batch, tradePairsCnt); err != nil {
217                 return err
218         }
219
220         state, err := m.calcNextDatabaseState(blockHeader)
221         if err != nil {
222                 return err
223         }
224
225         if err := m.saveMovDatabaseState(batch, state); err != nil {
226                 return err
227         }
228
229         batch.Write()
230         return nil
231 }
232
233 func (m *LevelDBMovStore) addOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[string]*common.TradePair) error {
234         for _, order := range orders {
235                 orderData := &orderData{
236                         Utxo:             order.Utxo,
237                         RatioNumerator:   order.RatioNumerator,
238                         RatioDenominator: order.RatioDenominator,
239                         BlockHeight:      order.BlockHeight,
240                         TxIndex:          order.TxIndex,
241                 }
242                 data, err := json.Marshal(orderData)
243                 if err != nil {
244                         return err
245                 }
246
247                 key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate())
248                 batch.Set(key, data)
249
250                 tradePair := &common.TradePair{
251                         FromAssetID: order.FromAssetID,
252                         ToAssetID:   order.ToAssetID,
253                 }
254                 if _, ok := tradePairsCnt[tradePair.Key()]; !ok {
255                         tradePairsCnt[tradePair.Key()] = tradePair
256                 }
257                 tradePairsCnt[tradePair.Key()].Count++
258         }
259         return nil
260 }
261
262 func (m *LevelDBMovStore) calcNextDatabaseState(blockHeader *types.BlockHeader) (*common.MovDatabaseState, error) {
263         hash := blockHeader.Hash()
264         height := blockHeader.Height
265
266         state, err := m.GetMovDatabaseState()
267         if err != nil {
268                 return nil, err
269         }
270
271         if *state.Hash == hash {
272                 hash = blockHeader.PreviousBlockHash
273                 height = blockHeader.Height - 1
274         }
275
276         return &common.MovDatabaseState{Height: height, Hash: &hash}, nil
277 }
278
279 func (m *LevelDBMovStore) checkMovDatabaseState(header *types.BlockHeader) error {
280         state, err := m.GetMovDatabaseState()
281         if err != nil {
282                 return err
283         }
284
285         if (*state.Hash == header.PreviousBlockHash && (state.Height+1) == header.Height) || *state.Hash == header.Hash() {
286                 return nil
287         }
288
289         return errors.New("the status of the block is inconsistent with that of mov-database")
290 }
291
292 func (m *LevelDBMovStore) deleteOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[string]*common.TradePair) {
293         for _, order := range orders {
294                 key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate())
295                 batch.Delete(key)
296
297                 tradePair := &common.TradePair{
298                         FromAssetID: order.FromAssetID,
299                         ToAssetID:   order.ToAssetID,
300                 }
301                 if _, ok := tradePairsCnt[tradePair.Key()]; !ok {
302                         tradePairsCnt[tradePair.Key()] = tradePair
303                 }
304                 tradePairsCnt[tradePair.Key()].Count--
305         }
306 }
307
308 func (m *LevelDBMovStore) saveMovDatabaseState(batch dbm.Batch, state *common.MovDatabaseState) error {
309         value, err := json.Marshal(state)
310         if err != nil {
311                 return err
312         }
313
314         batch.Set(bestMatchStore, value)
315         return nil
316 }
317
318 func (m *LevelDBMovStore) updateTradePairs(batch dbm.Batch, tradePairs map[string]*common.TradePair) error {
319         for _, v := range tradePairs {
320                 key := calcTradePairKey(v.FromAssetID, v.ToAssetID)
321                 tradePairData := &tradePairData{}
322                 if value := m.db.Get(key); value != nil {
323                         if err := json.Unmarshal(value, tradePairData); err != nil {
324                                 return err
325                         }
326                 }
327
328                 if tradePairData.Count += v.Count; tradePairData.Count < 0 {
329                         return errors.New("negative trade count")
330                 }
331
332                 if tradePairData.Count > 0 {
333                         value, err := json.Marshal(tradePairData)
334                         if err != nil {
335                                 return err
336                         }
337
338                         batch.Set(key, value)
339                 } else {
340                         batch.Delete(key)
341                 }
342         }
343         return nil
344 }