8 "github.com/golang/protobuf/proto"
9 log "github.com/sirupsen/logrus"
10 "github.com/tendermint/tmlibs/common"
12 dbm "github.com/bytom/bytom/database/leveldb"
13 "github.com/bytom/bytom/database/storage"
14 "github.com/bytom/bytom/errors"
15 "github.com/bytom/bytom/protocol"
16 "github.com/bytom/bytom/protocol/bc"
17 "github.com/bytom/bytom/protocol/bc/types"
18 "github.com/bytom/bytom/protocol/state"
21 const logModule = "leveldb"
24 BlockStoreKey = []byte("blockStore")
25 BlockPrefix = []byte("B:")
26 BlockHeaderPrefix = []byte("BH:")
27 TxStatusPrefix = []byte("BTS:")
30 func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
31 bytes := db.Get(BlockStoreKey)
35 bsj := &protocol.BlockStoreState{}
36 if err := json.Unmarshal(bytes, bsj); err != nil {
37 common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes))
42 // A Store encapsulates storage for blockchain validation.
43 // It satisfies the interface protocol.Store, and provides additional
44 // methods for querying current data.
50 func CalcBlockKey(hash *bc.Hash) []byte {
51 return append(BlockPrefix, hash.Bytes()...)
54 func CalcBlockHeaderKey(height uint64, hash *bc.Hash) []byte {
56 binary.BigEndian.PutUint64(buf[:], height)
57 key := append(BlockHeaderPrefix, buf[:]...)
58 return append(key, hash.Bytes()...)
61 func CalcTxStatusKey(hash *bc.Hash) []byte {
62 return append(TxStatusPrefix, hash.Bytes()...)
65 // GetBlockHeader return the BlockHeader by given hash
66 func (s *Store) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) {
70 // GetBlock return the block by given hash
71 func GetBlock(db dbm.DB, hash *bc.Hash) (*types.Block, error) {
72 bytez := db.Get(CalcBlockKey(hash))
77 block := &types.Block{}
78 err := block.UnmarshalText(bytez)
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, error) {
85 return GetBlock(db, hash)
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)
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
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)
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)
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))
118 return nil, errors.New("can't find the transaction status by given hash")
121 ts := &bc.TransactionStatus{}
122 if err := proto.Unmarshal(data, ts); err != nil {
123 return nil, errors.Wrap(err, "unmarshaling transaction status")
128 // GetStoreStatus return the BlockStoreStateJSON
129 func (s *Store) GetStoreStatus() *protocol.BlockStoreState {
130 return loadBlockStoreStateJSON(s.db)
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()
139 var lastNode *state.BlockNode
141 bh := &types.BlockHeader{}
142 if err := bh.UnmarshalText(bhIter.Value()); err != nil {
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 {
152 var parent *state.BlockNode
153 if lastNode == nil || lastNode.Hash == bh.PreviousBlockHash {
156 parent = blockIndex.GetNode(&bh.PreviousBlockHash)
159 node, err := state.NewBlockNode(bh, parent)
164 blockIndex.AddNode(node)
168 log.WithFields(log.Fields{
170 "height": stateBestHeight,
171 "duration": time.Since(startTime),
172 }).Debug("initialize load history block index from database")
173 return blockIndex, nil
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()
181 return errors.Wrap(err, "Marshal block meta")
184 binaryBlockHeader, err := block.BlockHeader.MarshalText()
186 return errors.Wrap(err, "Marshal block header")
189 binaryTxStatus, err := proto.Marshal(ts)
191 return errors.Wrap(err, "marshal block transaction status")
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)
201 log.WithFields(log.Fields{
203 "height": block.Height,
204 "hash": blockHash.String(),
205 "duration": time.Since(startTime),
206 }).Info("block saved on disk")
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 {
217 bytes, err := json.Marshal(protocol.BlockStoreState{Height: node.Height, Hash: &node.Hash})
222 batch.Set(BlockStoreKey, bytes)
227 func (s *Store) GetCheckpoint(*bc.Hash) (*state.Checkpoint, error) {
231 // GetCheckpointsByHeight return all checkpoints of specified block height
232 func (s *Store) GetCheckpointsByHeight(uint64) ([]*state.Checkpoint, error) {