9 "github.com/vapor/application/mov/common"
10 dbm "github.com/vapor/database/leveldb"
11 "github.com/vapor/protocol/bc"
12 "github.com/vapor/protocol/bc/types"
27 movStore = []byte("MOV:")
28 ordersPrefix = append(movStore, order)
29 tradePairsPrefix = append(movStore, tradePair)
30 bestMatchStore = append(movStore, matchStatus)
33 func calcOrderKey(fromAssetID, toAssetID *bc.AssetID, utxoHash *bc.Hash, rate float64) []byte {
34 buf := make([]byte, 8)
35 binary.BigEndian.PutUint64(buf, math.Float64bits(rate))
36 key := append(ordersPrefix, fromAssetID.Bytes()...)
37 key = append(key, toAssetID.Bytes()...)
38 key = append(key, buf...)
39 return append(key, utxoHash.Bytes()...)
42 func calcTradePairKey(fromAssetID, toAssetID *bc.AssetID) []byte {
43 key := append(tradePairsPrefix, fromAssetID.Bytes()...)
44 return append(key, toAssetID.Bytes()...)
47 func calcUTXOHash(order *common.Order) *bc.Hash {
48 prog := &bc.Program{VmVersion: 1, Code: order.Utxo.ControlProgram}
49 src := &bc.ValueSource{
50 Ref: order.Utxo.SourceID,
51 Value: &bc.AssetAmount{AssetId: order.FromAssetID, Amount: order.Utxo.Amount},
52 Position: order.Utxo.SourcePos,
54 hash := bc.EntryID(bc.NewIntraChainOutput(src, prog, 0))
58 func getAssetIDFromTradePairKey(key []byte, prefix []byte, posIndex int) *bc.AssetID {
60 pos := len(prefix) + assetIDLen*posIndex
61 copy(b[:], key[pos:pos+assetIDLen])
62 assetID := bc.NewAssetID(b)
66 func getRateFromOrderKey(key []byte, prefix []byte) float64 {
67 ratePos := len(prefix) + assetIDLen*2
68 return math.Float64frombits(binary.BigEndian.Uint64(key[ratePos : ratePos+rateByteLen]))
71 type tradePairData struct {
75 type MovStore struct {
79 func NewMovStore(db dbm.DB, height uint64, hash *bc.Hash) (*MovStore, error) {
80 if value := db.Get(bestMatchStore); value == nil {
81 state := &common.MovDatabaseState{Height: height, Hash: hash}
82 value, err := json.Marshal(state)
87 db.Set(bestMatchStore, value)
89 return &MovStore{db: db}, nil
92 func (m *MovStore) ListOrders(orderAfter *common.Order) ([]*common.Order, error) {
93 if orderAfter.FromAssetID == nil || orderAfter.ToAssetID == nil {
94 return nil, errors.New("assetID is nil")
97 orderPrefix := append(ordersPrefix, orderAfter.FromAssetID.Bytes()...)
98 orderPrefix = append(orderPrefix, orderAfter.ToAssetID.Bytes()...)
101 if orderAfter.Rate > 0 {
102 startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, calcUTXOHash(orderAfter), orderAfter.Rate)
105 itr := m.db.IteratorPrefixWithStart(orderPrefix, startKey, false)
108 var orders []*common.Order
109 for txNum := 0; txNum < ordersNum && itr.Next(); txNum++ {
110 movUtxo := &common.MovUtxo{}
111 if err := json.Unmarshal(itr.Value(), movUtxo); err != nil {
115 order := &common.Order{
116 FromAssetID: orderAfter.FromAssetID,
117 ToAssetID: orderAfter.ToAssetID,
118 Rate: getRateFromOrderKey(itr.Key(), ordersPrefix),
121 orders = append(orders, order)
126 func (m *MovStore) ProcessOrders(addOrders []*common.Order, delOreders []*common.Order, blockHeader *types.BlockHeader) error {
127 if err := m.checkMovDatabaseState(blockHeader); err != nil {
131 batch := m.db.NewBatch()
132 tradePairsCnt := make(map[common.TradePair]int)
133 if err := m.addOrders(batch, addOrders, tradePairsCnt); err != nil {
137 m.deleteOrders(batch, delOreders, tradePairsCnt)
139 if err := m.updateTradePairs(batch, tradePairsCnt); err != nil {
143 hash := blockHeader.Hash()
144 if err := m.saveMovDatabaseState(batch, &common.MovDatabaseState{Height: blockHeader.Height, Hash: &hash}); err != nil {
152 func (m *MovStore) addOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[common.TradePair]int) error {
153 for _, order := range orders {
154 data, err := json.Marshal(order.Utxo)
159 key := calcOrderKey(order.FromAssetID, order.ToAssetID, calcUTXOHash(order), order.Rate)
162 tradePair := common.TradePair{
163 FromAssetID: order.FromAssetID,
164 ToAssetID: order.ToAssetID,
166 tradePairsCnt[tradePair] += 1
171 func (m *MovStore) deleteOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[common.TradePair]int) {
172 for _, order := range orders {
173 key := calcOrderKey(order.FromAssetID, order.ToAssetID, calcUTXOHash(order), order.Rate)
176 tradePair := common.TradePair{
177 FromAssetID: order.FromAssetID,
178 ToAssetID: order.ToAssetID,
180 tradePairsCnt[tradePair] -= 1
184 func (m *MovStore) GetMovDatabaseState() (*common.MovDatabaseState, error) {
185 if value := m.db.Get(bestMatchStore); value != nil {
186 state := &common.MovDatabaseState{}
187 return state, json.Unmarshal(value, state)
190 return nil, errors.New("don't find state of mov-database")
193 func (m *MovStore) ListTradePairsWithStart(fromAssetIDAfter, toAssetIDAfter *bc.AssetID) ([]*common.TradePair, error) {
195 if fromAssetIDAfter != nil && toAssetIDAfter != nil {
196 startKey = calcTradePairKey(fromAssetIDAfter, toAssetIDAfter)
199 itr := m.db.IteratorPrefixWithStart(tradePairsPrefix, startKey, false)
202 var tradePairs []*common.TradePair
203 for txNum := 0; txNum < tradePairsNum && itr.Next(); txNum++ {
205 fromAssetID := getAssetIDFromTradePairKey(key, tradePairsPrefix, 0)
206 toAssetID := getAssetIDFromTradePairKey(key, tradePairsPrefix, 1)
208 tradePairData := &tradePairData{}
209 if err := json.Unmarshal(itr.Value(), tradePairData); err != nil {
213 tradePairs = append(tradePairs, &common.TradePair{FromAssetID: fromAssetID, ToAssetID: toAssetID, Count: tradePairData.Count})
215 return tradePairs, nil
218 func (m *MovStore) updateTradePairs(batch dbm.Batch, tradePairs map[common.TradePair]int) error {
219 for k, v := range tradePairs {
220 key := calcTradePairKey(k.FromAssetID, k.ToAssetID)
221 tradePairData := &tradePairData{}
222 if value := m.db.Get(key); value != nil {
223 if err := json.Unmarshal(value, tradePairData); err != nil {
227 return errors.New("don't find trade pair")
230 tradePairData.Count += v
231 if tradePairData.Count > 0 {
232 value, err := json.Marshal(tradePairData)
237 batch.Set(key, value)
245 func (m *MovStore) checkMovDatabaseState(header *types.BlockHeader) error {
246 state, err := m.GetMovDatabaseState()
251 blockHash := header.Hash()
252 if (state.Hash.String() == header.PreviousBlockHash.String() && (state.Height+1) == header.Height) || state.Hash.String() == blockHash.String() {
256 return errors.New("the status of the block is inconsistent with that of mov-database")
259 func (m *MovStore) saveMovDatabaseState(batch dbm.Batch, state *common.MovDatabaseState) error {
260 value, err := json.Marshal(state)
265 batch.Set(bestMatchStore, value)