"github.com/bytom/vapor/protocol/bc/types"
)
+// MovStore is the interface for mov's persistent storage
+type MovStore interface {
+ GetMovDatabaseState() (*common.MovDatabaseState, error)
+ InitDBState(height uint64, hash *bc.Hash) error
+ ListOrders(orderAfter *common.Order) ([]*common.Order, error)
+ ListTradePairsWithStart(fromAssetIDAfter, toAssetIDAfter *bc.AssetID) ([]*common.TradePair, error)
+ ProcessOrders(addOrders []*common.Order, delOrders []*common.Order, blockHeader *types.BlockHeader) error
+}
+
const (
- order byte = iota
+ order byte = iota + 1
tradePair
matchStatus
- tradePairsNum = 1024
- ordersNum = 10240
- assetIDLen = 32
- rateByteLen = 8
+ fromAssetIDPos = 0
+ toAssetIDPos = 1
+ assetIDLen = 32
+ rateByteLen = 8
+
+ tradePairsNum = 32
+ ordersNum = 128
)
var (
bestMatchStore = append(movStore, matchStatus)
)
+type orderData struct {
+ Utxo *common.MovUtxo
+ RatioNumerator int64
+ RatioDenominator int64
+}
+
func calcOrderKey(fromAssetID, toAssetID *bc.AssetID, utxoHash *bc.Hash, rate float64) []byte {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, math.Float64bits(rate))
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 {
+func getAssetIDFromTradePairKey(key []byte, posIndex int) *bc.AssetID {
b := [32]byte{}
- pos := len(prefix) + assetIDLen*posIndex
+ pos := len(tradePairsPrefix) + 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
+func getRateFromOrderKey(key []byte) float64 {
+ ratePos := len(ordersPrefix) + assetIDLen*2
return math.Float64frombits(binary.BigEndian.Uint64(key[ratePos : ratePos+rateByteLen]))
}
Count int
}
-type MovStore struct {
+// LevelDBMovStore is the LevelDB implementation for MovStore
+type LevelDBMovStore 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
- }
+// NewLevelDBMovStore create a new LevelDBMovStore object
+func NewLevelDBMovStore(db dbm.DB) *LevelDBMovStore {
+ return &LevelDBMovStore{db: db}
+}
+
+// GetMovDatabaseState return the current DB's image status
+func (m *LevelDBMovStore) 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")
+}
- db.Set(bestMatchStore, value)
+// InitDBState set the DB's image status
+func (m *LevelDBMovStore) InitDBState(height uint64, hash *bc.Hash) error {
+ state := &common.MovDatabaseState{Height: height, Hash: hash}
+ value, err := json.Marshal(state)
+ if err != nil {
+ return err
}
- return &MovStore{db: db}, nil
+
+ m.db.Set(bestMatchStore, value)
+ return nil
}
-func (m *MovStore) ListOrders(orderAfter *common.Order) ([]*common.Order, error) {
+// ListOrders return n orders after the input order
+func (m *LevelDBMovStore) ListOrders(orderAfter *common.Order) ([]*common.Order, error) {
if orderAfter.FromAssetID == nil || orderAfter.ToAssetID == nil {
return nil, errors.New("assetID is nil")
}
orderPrefix = append(orderPrefix, orderAfter.ToAssetID.Bytes()...)
var startKey []byte
- if orderAfter.Rate > 0 {
- startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, calcUTXOHash(orderAfter), orderAfter.Rate)
+ if orderAfter.Rate() > 0 {
+ startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, orderAfter.UTXOHash(), orderAfter.Rate())
}
itr := m.db.IteratorPrefixWithStart(orderPrefix, startKey, false)
var orders []*common.Order
for txNum := 0; txNum < ordersNum && itr.Next(); txNum++ {
- movUtxo := &common.MovUtxo{}
- if err := json.Unmarshal(itr.Value(), movUtxo); err != nil {
+ orderData := &orderData{}
+ if err := json.Unmarshal(itr.Value(), orderData); 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)
+ orders = append(orders, &common.Order{
+ FromAssetID: orderAfter.FromAssetID,
+ ToAssetID: orderAfter.ToAssetID,
+ Utxo: orderData.Utxo,
+ RatioNumerator: orderData.RatioNumerator,
+ RatioDenominator: orderData.RatioDenominator,
+ })
}
return orders, nil
}
-func (m *MovStore) ProcessOrders(addOrders []*common.Order, delOreders []*common.Order, blockHeader *types.BlockHeader) error {
+// ListTradePairsWithStart return n trade pairs after the input trade pair
+func (m *LevelDBMovStore) 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, fromAssetIDPos)
+ toAssetID := getAssetIDFromTradePairKey(key, toAssetIDPos)
+
+ 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
+}
+
+// ProcessOrders update the DB's image by add new orders, delete the used order
+func (m *LevelDBMovStore) ProcessOrders(addOrders []*common.Order, delOrders []*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)
+ tradePairsCnt := make(map[string]*common.TradePair)
if err := m.addOrders(batch, addOrders, tradePairsCnt); err != nil {
return err
}
- m.deleteOrders(batch, delOreders, tradePairsCnt)
-
+ m.deleteOrders(batch, delOrders, 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 {
+ state, err := m.calcNextDatabaseState(blockHeader)
+ if err != nil {
+ return err
+ }
+
+ if err := m.saveMovDatabaseState(batch, state); err != nil {
return err
}
return nil
}
-func (m *MovStore) addOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[common.TradePair]int) error {
+func (m *LevelDBMovStore) addOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[string]*common.TradePair) error {
for _, order := range orders {
- data, err := json.Marshal(order.Utxo)
+ orderData := &orderData{
+ Utxo: order.Utxo,
+ RatioNumerator: order.RatioNumerator,
+ RatioDenominator: order.RatioDenominator,
+ }
+ data, err := json.Marshal(orderData)
if err != nil {
return err
}
- key := calcOrderKey(order.FromAssetID, order.ToAssetID, calcUTXOHash(order), order.Rate)
+ key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate())
batch.Set(key, data)
- tradePair := common.TradePair{
+ tradePair := &common.TradePair{
FromAssetID: order.FromAssetID,
ToAssetID: order.ToAssetID,
}
- tradePairsCnt[tradePair] += 1
+ if _, ok := tradePairsCnt[tradePair.Key()]; !ok {
+ tradePairsCnt[tradePair.Key()] = tradePair
+ }
+ tradePairsCnt[tradePair.Key()].Count++
}
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)
+func (m *LevelDBMovStore) calcNextDatabaseState(blockHeader *types.BlockHeader) (*common.MovDatabaseState, error) {
+ hash := blockHeader.Hash()
+ height := blockHeader.Height
- tradePair := common.TradePair{
- FromAssetID: order.FromAssetID,
- ToAssetID: order.ToAssetID,
- }
- tradePairsCnt[tradePair] -= 1
+ state, err := m.GetMovDatabaseState()
+ if err != nil {
+ return nil, err
}
-}
-func (m *MovStore) GetMovDatabaseState() (*common.MovDatabaseState, error) {
- if value := m.db.Get(bestMatchStore); value != nil {
- state := &common.MovDatabaseState{}
- return state, json.Unmarshal(value, state)
+ if *state.Hash == hash {
+ hash = blockHeader.PreviousBlockHash
+ height = blockHeader.Height - 1
}
- return nil, errors.New("don't find state of mov-database")
+ return &common.MovDatabaseState{Height: height, Hash: &hash}, nil
}
-func (m *MovStore) ListTradePairsWithStart(fromAssetIDAfter, toAssetIDAfter *bc.AssetID) ([]*common.TradePair, error) {
- var startKey []byte
- if fromAssetIDAfter != nil && toAssetIDAfter != nil {
- startKey = calcTradePairKey(fromAssetIDAfter, toAssetIDAfter)
+func (m *LevelDBMovStore) checkMovDatabaseState(header *types.BlockHeader) error {
+ state, err := m.GetMovDatabaseState()
+ if err != nil {
+ return err
}
- itr := m.db.IteratorPrefixWithStart(tradePairsPrefix, startKey, false)
- defer itr.Release()
+ if (*state.Hash == header.PreviousBlockHash && (state.Height+1) == header.Height) || *state.Hash == header.Hash() {
+ return nil
+ }
- 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)
+ return errors.New("the status of the block is inconsistent with that of mov-database")
+}
- tradePairData := &tradePairData{}
- if err := json.Unmarshal(itr.Value(), tradePairData); err != nil {
- return nil, err
+func (m *LevelDBMovStore) deleteOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[string]*common.TradePair) {
+ for _, order := range orders {
+ key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate())
+ batch.Delete(key)
+
+ tradePair := &common.TradePair{
+ FromAssetID: order.FromAssetID,
+ ToAssetID: order.ToAssetID,
+ }
+ if _, ok := tradePairsCnt[tradePair.Key()]; !ok {
+ tradePairsCnt[tradePair.Key()] = tradePair
}
+ tradePairsCnt[tradePair.Key()].Count--
+ }
+}
- tradePairs = append(tradePairs, &common.TradePair{FromAssetID: fromAssetID, ToAssetID: toAssetID, Count: tradePairData.Count})
+func (m *LevelDBMovStore) saveMovDatabaseState(batch dbm.Batch, state *common.MovDatabaseState) error {
+ value, err := json.Marshal(state)
+ if err != nil {
+ return err
}
- return tradePairs, nil
+
+ batch.Set(bestMatchStore, value)
+ return 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)
+func (m *LevelDBMovStore) updateTradePairs(batch dbm.Batch, tradePairs map[string]*common.TradePair) error {
+ for _, v := range tradePairs {
+ key := calcTradePairKey(v.FromAssetID, v.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 += v.Count; tradePairData.Count < 0 {
+ return errors.New("negative trade count")
+ }
+
if tradePairData.Count > 0 {
value, err := json.Marshal(tradePairData)
if err != nil {
}
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
-}