OSDN Git Service

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