OSDN Git Service

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