8 log "github.com/sirupsen/logrus"
9 "github.com/tendermint/tmlibs/common"
11 dbm "github.com/bytom/bytom/database/leveldb"
12 "github.com/bytom/bytom/database/storage"
13 "github.com/bytom/bytom/errors"
14 "github.com/bytom/bytom/protocol"
15 "github.com/bytom/bytom/protocol/bc"
16 "github.com/bytom/bytom/protocol/bc/types"
17 "github.com/bytom/bytom/protocol/state"
20 const logModule = "leveldb"
23 BlockStoreKey = []byte("blockStore")
24 BlockPrefix = []byte("B:")
25 BlockHeaderPrefix = []byte("BH:")
26 TxStatusPrefix = []byte("BTS:")
29 func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
30 bytes := db.Get(BlockStoreKey)
34 bsj := &protocol.BlockStoreState{}
35 if err := json.Unmarshal(bytes, bsj); err != nil {
36 common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes))
41 // A Store encapsulates storage for blockchain validation.
42 // It satisfies the interface protocol.Store, and provides additional
43 // methods for querying current data.
49 func CalcBlockKey(hash *bc.Hash) []byte {
50 return append(BlockPrefix, hash.Bytes()...)
53 func CalcBlockHeaderKey(height uint64, hash *bc.Hash) []byte {
55 binary.BigEndian.PutUint64(buf[:], height)
56 key := append(BlockHeaderPrefix, buf[:]...)
57 return append(key, hash.Bytes()...)
60 // GetBlockHeader return the BlockHeader by given hash
61 func (s *Store) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) {
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))
72 block := &types.Block{}
73 err := block.UnmarshalText(bytez)
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)
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)
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
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)
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)
109 // GetStoreStatus return the BlockStoreStateJSON
110 func (s *Store) GetStoreStatus() *protocol.BlockStoreState {
111 return loadBlockStoreStateJSON(s.db)
114 func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error) {
115 startTime := time.Now()
116 blockIndex := state.NewBlockIndex()
117 bhIter := s.db.IteratorPrefix(BlockHeaderPrefix)
118 defer bhIter.Release()
120 var lastNode *state.BlockNode
122 bh := &types.BlockHeader{}
123 if err := bh.UnmarshalText(bhIter.Value()); err != nil {
127 // If a block with a height greater than the best height of state is added to the index,
128 // It may cause a bug that the new block cant not be process properly.
129 if bh.Height > stateBestHeight {
133 var parent *state.BlockNode
134 if lastNode == nil || lastNode.Hash == bh.PreviousBlockHash {
137 parent = blockIndex.GetNode(&bh.PreviousBlockHash)
140 node, err := state.NewBlockNode(bh, parent)
145 blockIndex.AddNode(node)
149 log.WithFields(log.Fields{
151 "height": stateBestHeight,
152 "duration": time.Since(startTime),
153 }).Debug("initialize load history block index from database")
154 return blockIndex, nil
157 // SaveBlock persists a new block in the protocol.
158 func (s *Store) SaveBlock(block *types.Block) error {
159 startTime := time.Now()
160 binaryBlock, err := block.MarshalText()
162 return errors.Wrap(err, "Marshal block meta")
165 binaryBlockHeader, err := block.BlockHeader.MarshalText()
167 return errors.Wrap(err, "Marshal block header")
170 blockHash := block.Hash()
171 batch := s.db.NewBatch()
172 batch.Set(CalcBlockKey(&blockHash), binaryBlock)
173 batch.Set(CalcBlockHeaderKey(block.Height, &blockHash), binaryBlockHeader)
176 log.WithFields(log.Fields{
178 "height": block.Height,
179 "hash": blockHash.String(),
180 "duration": time.Since(startTime),
181 }).Info("block saved on disk")
185 // SaveChainStatus save the core's newest status && delete old status
186 func (s *Store) SaveChainStatus(node *state.BlockNode, view *state.UtxoViewpoint, contractView *state.ContractViewpoint) error {
187 batch := s.db.NewBatch()
188 if err := saveUtxoView(batch, view); err != nil {
192 if err := deleteContractView(s.db, batch, contractView); err != nil {
196 if err := saveContractView(s.db, batch, contractView); err != nil {
200 bytes, err := json.Marshal(protocol.BlockStoreState{Height: node.Height, Hash: &node.Hash})
205 batch.Set(BlockStoreKey, bytes)
210 func (s *Store) GetCheckpoint(*bc.Hash) (*state.Checkpoint, error) {
214 // GetCheckpointsByHeight return all checkpoints of specified block height
215 func (s *Store) GetCheckpointsByHeight(uint64) ([]*state.Checkpoint, error) {
219 // SaveCheckpoints bulk save multiple checkpoint
220 func (s *Store) SaveCheckpoints(...*state.Checkpoint) error {