OSDN Git Service

3e5d4cb63fde43806f29251a8730beb2c3fac46b
[bytom/vapor.git] / database / leveldb / store.go
1 package leveldb
2
3 import (
4         "encoding/binary"
5         "encoding/json"
6         "time"
7
8         "github.com/golang/protobuf/proto"
9         log "github.com/sirupsen/logrus"
10         "github.com/tendermint/tmlibs/common"
11         dbm "github.com/tendermint/tmlibs/db"
12
13         "github.com/vapor/database/storage"
14         "github.com/vapor/errors"
15         "github.com/vapor/protocol"
16         "github.com/vapor/protocol/bc"
17         "github.com/vapor/protocol/bc/types"
18         "github.com/vapor/protocol/state"
19 )
20
21 const logModule = "leveldb"
22
23 var (
24         blockStoreKey     = []byte("blockStore")
25         blockPrefix       = []byte("B:")
26         blockHeaderPrefix = []byte("BH:")
27         txStatusPrefix    = []byte("BTS:")
28         cliamTxPreFix     = []byte("Claim:")
29         dposPreFix        = []byte("dpos:")
30 )
31
32 func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
33         bytes := db.Get(blockStoreKey)
34         if bytes == nil {
35                 return nil
36         }
37         bsj := &protocol.BlockStoreState{}
38         if err := json.Unmarshal(bytes, bsj); err != nil {
39                 common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes))
40         }
41         return bsj
42 }
43
44 // A Store encapsulates storage for blockchain validation.
45 // It satisfies the interface protocol.Store, and provides additional
46 // methods for querying current data.
47 type Store struct {
48         db    dbm.DB
49         cache blockCache
50 }
51
52 func calcBlockKey(hash *bc.Hash) []byte {
53         return append(blockPrefix, hash.Bytes()...)
54 }
55
56 func calcBlockHeaderKey(height uint64, hash *bc.Hash) []byte {
57         buf := [8]byte{}
58         binary.BigEndian.PutUint64(buf[:], height)
59         key := append(blockHeaderPrefix, buf[:]...)
60         return append(key, hash.Bytes()...)
61 }
62
63 func calcTxStatusKey(hash *bc.Hash) []byte {
64         return append(txStatusPrefix, hash.Bytes()...)
65 }
66
67 func calcClaimTxKey(hash *bc.Hash) []byte {
68         return append(cliamTxPreFix, hash.Bytes()...)
69 }
70
71 func calcDPosKey(hash *bc.Hash) []byte {
72         return append(dposPreFix, hash.Bytes()...)
73 }
74
75 // GetBlock return the block by given hash
76 func GetBlock(db dbm.DB, hash *bc.Hash) *types.Block {
77         bytez := db.Get(calcBlockKey(hash))
78         if bytez == nil {
79                 return nil
80         }
81
82         block := &types.Block{}
83         block.UnmarshalText(bytez)
84         return block
85 }
86
87 // NewStore creates and returns a new Store object.
88 func NewStore(db dbm.DB) *Store {
89         cache := newBlockCache(func(hash *bc.Hash) *types.Block {
90                 return GetBlock(db, hash)
91         })
92         return &Store{
93                 db:    db,
94                 cache: cache,
95         }
96 }
97
98 // GetUtxo will search the utxo in db
99 func (s *Store) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) {
100         return getUtxo(s.db, hash)
101 }
102
103 // BlockExist check if the block is stored in disk
104 func (s *Store) BlockExist(hash *bc.Hash) bool {
105         block, err := s.cache.lookup(hash)
106         return err == nil && block != nil
107 }
108
109 // GetBlock return the block by given hash
110 func (s *Store) GetBlock(hash *bc.Hash) (*types.Block, error) {
111         return s.cache.lookup(hash)
112 }
113
114 // GetTransactionsUtxo will return all the utxo that related to the input txs
115 func (s *Store) GetTransactionsUtxo(view *state.UtxoViewpoint, txs []*bc.Tx) error {
116         return getTransactionsUtxo(s.db, view, txs)
117 }
118
119 // GetTransactionStatus will return the utxo that related to the block hash
120 func (s *Store) GetTransactionStatus(hash *bc.Hash) (*bc.TransactionStatus, error) {
121         data := s.db.Get(calcTxStatusKey(hash))
122         if data == nil {
123                 return nil, errors.New("can't find the transaction status by given hash")
124         }
125
126         ts := &bc.TransactionStatus{}
127         if err := proto.Unmarshal(data, ts); err != nil {
128                 return nil, errors.Wrap(err, "unmarshaling transaction status")
129         }
130         return ts, nil
131 }
132
133 // GetStoreStatus return the BlockStoreStateJSON
134 func (s *Store) GetStoreStatus() *protocol.BlockStoreState {
135         return loadBlockStoreStateJSON(s.db)
136 }
137
138 func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error) {
139         startTime := time.Now()
140         blockIndex := state.NewBlockIndex()
141         bhIter := s.db.IteratorPrefix(blockHeaderPrefix)
142         defer bhIter.Release()
143
144         var lastNode *state.BlockNode
145         for bhIter.Next() {
146                 bh := &types.BlockHeader{}
147                 if err := bh.UnmarshalText(bhIter.Value()); err != nil {
148                         return nil, err
149                 }
150
151                 // If a block with a height greater than the best height of state is added to the index,
152                 // It may cause a bug that the new block cant not be process properly.
153                 if bh.Height > stateBestHeight {
154                         break
155                 }
156
157                 var parent *state.BlockNode
158                 if lastNode == nil || lastNode.Hash == bh.PreviousBlockHash {
159                         parent = lastNode
160                 } else {
161                         parent = blockIndex.GetNode(&bh.PreviousBlockHash)
162                 }
163
164                 node, err := state.NewBlockNode(bh, parent)
165                 if err != nil {
166                         return nil, err
167                 }
168
169                 blockIndex.AddNode(node)
170                 lastNode = node
171         }
172
173         log.WithFields(log.Fields{
174                 "module":   logModule,
175                 "height":   stateBestHeight,
176                 "duration": time.Since(startTime),
177         }).Debug("initialize load history block index from database")
178         return blockIndex, nil
179 }
180
181 // SaveBlock persists a new block in the protocol.
182 func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus) error {
183         startTime := time.Now()
184         binaryBlock, err := block.MarshalText()
185         if err != nil {
186                 return errors.Wrap(err, "Marshal block meta")
187         }
188
189         binaryBlockHeader, err := block.BlockHeader.MarshalText()
190         if err != nil {
191                 return errors.Wrap(err, "Marshal block header")
192         }
193
194         binaryTxStatus, err := proto.Marshal(ts)
195         if err != nil {
196                 return errors.Wrap(err, "marshal block transaction status")
197         }
198
199         blockHash := block.Hash()
200         batch := s.db.NewBatch()
201         batch.Set(calcBlockKey(&blockHash), binaryBlock)
202         batch.Set(calcBlockHeaderKey(block.Height, &blockHash), binaryBlockHeader)
203         batch.Set(calcTxStatusKey(&blockHash), binaryTxStatus)
204         batch.Write()
205
206         log.WithFields(log.Fields{
207                 "module":   logModule,
208                 "height":   block.Height,
209                 "hash":     blockHash.String(),
210                 "duration": time.Since(startTime),
211         }).Info("block saved on disk")
212         return nil
213 }
214
215 // SaveChainStatus save the core's newest status && delete old status
216 func (s *Store) SaveChainStatus(node *state.BlockNode, view *state.UtxoViewpoint) error {
217         batch := s.db.NewBatch()
218         if err := saveUtxoView(batch, view); err != nil {
219                 return err
220         }
221
222         bytes, err := json.Marshal(protocol.BlockStoreState{Height: node.Height, Hash: &node.Hash})
223         if err != nil {
224                 return err
225         }
226
227         batch.Set(blockStoreKey, bytes)
228         batch.Write()
229         return nil
230 }
231
232 func (s *Store) IsWithdrawSpent(hash *bc.Hash) bool {
233         data := s.db.Get(calcClaimTxKey(hash))
234         if data != nil {
235                 return true
236         }
237         return false
238 }
239
240 func (s *Store) SetWithdrawSpent(hash *bc.Hash) {
241         batch := s.db.NewBatch()
242         batch.Set(calcClaimTxKey(hash), []byte("1"))
243         batch.Write()
244 }
245
246 func (s *Store) Set(hash *bc.Hash, data []byte) error {
247         batch := s.db.NewBatch()
248         batch.Set(calcDPosKey(hash), data)
249         batch.Write()
250         return nil
251 }
252
253 func (s *Store) Get(hash *bc.Hash) ([]byte, error) {
254         data := s.db.Get(calcDPosKey(hash))
255         if data == nil {
256                 return nil, errors.New("can't find the snapshot by given hash")
257         }
258         return data, nil
259 }