OSDN Git Service

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