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"
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"
21 blockStoreKey = []byte("blockStore")
22 blockPrefix = []byte("B:")
23 blockHeaderPrefix = []byte("BH:")
24 txStatusPrefix = []byte("BTS:")
27 func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
28 bytes := db.Get(blockStoreKey)
32 bsj := &protocol.BlockStoreState{}
33 if err := json.Unmarshal(bytes, bsj); err != nil {
34 common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes))
39 // A Store encapsulates storage for blockchain validation.
40 // It satisfies the interface protocol.Store, and provides additional
41 // methods for querying current data.
47 func calcBlockKey(hash *bc.Hash) []byte {
48 return append(blockPrefix, hash.Bytes()...)
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 calcTxStatusKey(hash *bc.Hash) []byte {
59 return append(txStatusPrefix, hash.Bytes()...)
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))
69 block := &types.Block{}
70 block.UnmarshalText(bytez)
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)
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)
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
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)
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)
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))
110 return nil, errors.New("can't find the transaction status by given hash")
113 ts := &bc.TransactionStatus{}
114 if err := proto.Unmarshal(data, ts); err != nil {
115 return nil, errors.Wrap(err, "unmarshaling transaction status")
120 // GetStoreStatus return the BlockStoreStateJSON
121 func (s *Store) GetStoreStatus() *protocol.BlockStoreState {
122 return loadBlockStoreStateJSON(s.db)
125 func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error) {
126 blockIndex := state.NewBlockIndex()
127 bhIter := s.db.IteratorPrefix(blockHeaderPrefix)
128 defer bhIter.Release()
130 var lastNode *state.BlockNode
132 bh := &types.BlockHeader{}
133 if err := bh.UnmarshalText(bhIter.Value()); err != nil {
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 {
143 var parent *state.BlockNode
144 if lastNode == nil || lastNode.Hash == bh.PreviousBlockHash {
147 parent = blockIndex.GetNode(&bh.PreviousBlockHash)
150 node, err := state.NewBlockNode(bh, parent)
155 blockIndex.AddNode(node)
159 return blockIndex, nil
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()
166 return errors.Wrap(err, "Marshal block meta")
169 binaryBlockHeader, err := block.BlockHeader.MarshalText()
171 return errors.Wrap(err, "Marshal block header")
174 binaryTxStatus, err := proto.Marshal(ts)
176 return errors.Wrap(err, "marshal block transaction status")
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)
186 log.WithFields(log.Fields{"height": block.Height, "hash": blockHash.String()}).Info("block saved on disk")
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 {
197 bytes, err := json.Marshal(protocol.BlockStoreState{Height: node.Height, Hash: &node.Hash})
202 batch.Set(blockStoreKey, bytes)