OSDN Git Service

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