8 "github.com/golang/protobuf/proto"
9 log "github.com/sirupsen/logrus"
10 "github.com/tendermint/tmlibs/common"
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"
21 const logModule = "leveldb"
24 blockStoreKey = []byte("blockStore")
25 blockHeaderPrefix = []byte("BH:")
26 blockTransactonsPrefix = []byte("BTXS:")
27 txStatusPrefix = []byte("BTS:")
28 voteResultPrefix = []byte("VR:")
31 func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
32 bytes := db.Get(blockStoreKey)
36 bsj := &protocol.BlockStoreState{}
37 if err := json.Unmarshal(bytes, bsj); err != nil {
38 common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes))
43 // A Store encapsulates storage for blockchain validation.
44 // It satisfies the interface protocol.Store, and provides additional
45 // methods for querying current data.
51 func calcBlockHeaderKey(height uint64, hash *bc.Hash) []byte {
53 binary.BigEndian.PutUint64(buf[:], height)
54 key := append(blockHeaderPrefix, buf[:]...)
55 return append(key, hash.Bytes()...)
58 func calcBlockTransactionsKey(hash *bc.Hash) []byte {
59 return append(blockTransactonsPrefix, hash.Bytes()...)
62 func calcTxStatusKey(hash *bc.Hash) []byte {
63 return append(txStatusPrefix, hash.Bytes()...)
66 func calcVoteResultKey(seq uint64) []byte {
68 binary.BigEndian.PutUint64(buf[:], seq)
69 return append(voteResultPrefix, buf[:]...)
72 // GetBlockHeader return the block header by given hash and height
73 func GetBlockHeader(db dbm.DB, hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
74 block := &types.Block{}
75 binaryBlockHeader := db.Get(calcBlockHeaderKey(height, hash))
76 if binaryBlockHeader == nil {
79 if err := block.UnmarshalText(binaryBlockHeader); err != nil {
83 return &block.BlockHeader, nil
86 // GetBlockTransactions return the block transactions by given hash
87 func GetBlockTransactions(db dbm.DB, hash *bc.Hash) ([]*types.Tx, error) {
88 block := &types.Block{}
89 binaryBlockTxs := db.Get(calcBlockTransactionsKey(hash))
90 if binaryBlockTxs == nil {
91 return nil, errors.New("The transactions in the block is empty")
94 if err := block.UnmarshalText(binaryBlockTxs); err != nil {
97 return block.Transactions, nil
100 // GetVoteResult return the vote result by given sequence
101 func GetVoteResult(db dbm.DB, seq uint64) (*state.VoteResult, error) {
102 data := db.Get(calcVoteResultKey(seq))
104 return nil, protocol.ErrNotFoundVoteResult
107 voteResult := new(state.VoteResult)
108 if err := json.Unmarshal(data, voteResult); err != nil {
109 return nil, errors.Wrap(err, "unmarshaling vote result")
111 return voteResult, nil
114 // NewStore creates and returns a new Store object.
115 func NewStore(db dbm.DB) *Store {
116 fillBlockHeaderFn := func(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
117 return GetBlockHeader(db, hash, height)
119 fillBlockTxsFn := func(hash *bc.Hash) ([]*types.Tx, error) {
120 return GetBlockTransactions(db, hash)
122 fillVoteResultFn := func(seq uint64) (*state.VoteResult, error) {
123 return GetVoteResult(db, seq)
125 bc := newCache(fillBlockHeaderFn, fillBlockTxsFn, fillVoteResultFn)
132 // GetUtxo will search the utxo in db
133 func (s *Store) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) {
134 return getUtxo(s.db, hash)
137 // BlockExist check if the block is stored in disk
138 func (s *Store) BlockExist(hash *bc.Hash, height uint64) bool {
139 blockHeader, err := s.cache.lookupBlockHeader(hash, height)
140 return err == nil && blockHeader != nil
143 // GetBlock return the block by given hash
144 func (s *Store) GetBlock(hash *bc.Hash, height uint64) (*types.Block, error) {
145 blockHeader, err := s.GetBlockHeader(hash, height)
150 txs, err := s.GetBlockTransactions(hash)
156 BlockHeader: *blockHeader,
161 // GetBlockHeader return the BlockHeader by given hash
162 func (s *Store) GetBlockHeader(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
163 blockHeader, err := s.cache.lookupBlockHeader(hash, height)
167 return blockHeader, nil
170 // GetBlockTransactions return the Block transactions by given hash
171 func (s *Store) GetBlockTransactions(hash *bc.Hash) ([]*types.Tx, error) {
172 txs, err := s.cache.lookupBlockTxs(hash)
179 // GetTransactionsUtxo will return all the utxo that related to the input txs
180 func (s *Store) GetTransactionsUtxo(view *state.UtxoViewpoint, txs []*bc.Tx) error {
181 return getTransactionsUtxo(s.db, view, txs)
184 // GetTransactionStatus will return the utxo that related to the block hash
185 func (s *Store) GetTransactionStatus(hash *bc.Hash) (*bc.TransactionStatus, error) {
186 data := s.db.Get(calcTxStatusKey(hash))
188 return nil, errors.New("can't find the transaction status by given hash")
191 ts := &bc.TransactionStatus{}
192 if err := proto.Unmarshal(data, ts); err != nil {
193 return nil, errors.Wrap(err, "unmarshaling transaction status")
198 // GetStoreStatus return the BlockStoreStateJSON
199 func (s *Store) GetStoreStatus() *protocol.BlockStoreState {
200 return loadBlockStoreStateJSON(s.db)
203 // GetVoteResult retrive the voting result in specified vote sequence
204 func (s *Store) GetVoteResult(seq uint64) (*state.VoteResult, error) {
205 return s.cache.lookupVoteResult(seq)
208 func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error) {
209 startTime := time.Now()
210 blockIndex := state.NewBlockIndex()
211 bhIter := s.db.IteratorPrefix(blockHeaderPrefix)
212 defer bhIter.Release()
214 var lastNode *state.BlockNode
216 bh := &types.BlockHeader{}
217 if err := bh.UnmarshalText(bhIter.Value()); err != nil {
221 // If a block with a height greater than the best height of state is added to the index,
222 // It may cause a bug that the new block cant not be process properly.
223 if bh.Height > stateBestHeight {
227 var parent *state.BlockNode
228 if lastNode == nil || lastNode.Hash == bh.PreviousBlockHash {
231 parent = blockIndex.GetNode(&bh.PreviousBlockHash)
234 node, err := state.NewBlockNode(bh, parent)
239 blockIndex.AddNode(node)
243 log.WithFields(log.Fields{
245 "height": stateBestHeight,
246 "duration": time.Since(startTime),
247 }).Debug("initialize load history block index from database")
248 return blockIndex, nil
251 // SaveBlock persists a new block in the protocol.
252 func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus) error {
253 startTime := time.Now()
255 binaryBlockHeader, err := block.MarshalTextForBlockHeader()
257 return errors.Wrap(err, "Marshal block header")
260 binaryBlockTxs, err := block.MarshalTextForTransactions()
262 return errors.Wrap(err, "Marshal block transactions")
265 binaryTxStatus, err := proto.Marshal(ts)
267 return errors.Wrap(err, "marshal block transaction status")
270 blockHash := block.Hash()
271 batch := s.db.NewBatch()
272 batch.Set(calcBlockHeaderKey(block.Height, &blockHash), binaryBlockHeader)
273 batch.Set(calcBlockTransactionsKey(&blockHash), binaryBlockTxs)
274 batch.Set(calcTxStatusKey(&blockHash), binaryTxStatus)
277 log.WithFields(log.Fields{
279 "height": block.Height,
280 "hash": blockHash.String(),
281 "duration": time.Since(startTime),
282 }).Info("block saved on disk")
286 // SaveBlockHeader persists a new block header in the protocol.
287 func (s *Store) SaveBlockHeader(blockHeader *types.BlockHeader) error {
288 binaryBlockHeader, err := blockHeader.MarshalText()
290 return errors.Wrap(err, "Marshal block header")
293 blockHash := blockHeader.Hash()
294 s.db.Set(calcBlockHeaderKey(blockHeader.Height, &blockHash), binaryBlockHeader)
296 // updata blockheader cache
297 if _, ok := s.cache.getBlockHeader(&blockHash); ok {
298 s.cache.addBlockHeader(blockHeader)
304 // SaveChainStatus save the core's newest status && delete old status
305 func (s *Store) SaveChainStatus(node, irreversibleNode *state.BlockNode, view *state.UtxoViewpoint, voteResults []*state.VoteResult) error {
306 batch := s.db.NewBatch()
307 if err := saveUtxoView(batch, view); err != nil {
311 for _, vote := range voteResults {
312 bytes, err := json.Marshal(vote)
317 batch.Set(calcVoteResultKey(vote.Seq), bytes)
318 if _, ok := s.cache.getVoteResult(vote.Seq); ok {
319 s.cache.addVoteResult(vote)
323 bytes, err := json.Marshal(protocol.BlockStoreState{
326 IrreversibleHeight: irreversibleNode.Height,
327 IrreversibleHash: &irreversibleNode.Hash,
333 batch.Set(blockStoreKey, bytes)