8 log "github.com/sirupsen/logrus"
9 "github.com/tendermint/tmlibs/common"
11 "github.com/bytom/bytom/consensus"
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 // CheckpointPrefix represent the namespace of checkpoints in db
25 CheckpointPrefix = []byte("CP:")
26 // BlockStoreKey block store key
27 BlockStoreKey = []byte("blockStore")
28 // BlockHeaderIndexPrefix block header index with height
29 BlockHeaderIndexPrefix = []byte("BH:")
32 func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
33 bytes := db.Get(BlockStoreKey)
37 bsj := &protocol.BlockStoreState{}
38 if err := json.Unmarshal(bytes, bsj); err != nil {
39 common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes))
44 // A Store encapsulates storage for blockchain validation.
45 // It satisfies the interface protocol.Store, and provides additional
46 // methods for querying current data.
52 // NewStore creates and returns a new Store object.
53 func NewStore(db dbm.DB) *Store {
54 fillBlockHeaderFn := func(hash *bc.Hash) (*types.BlockHeader, error) {
55 return GetBlockHeader(db, hash)
58 fillBlockTxsFn := func(hash *bc.Hash) ([]*types.Tx, error) {
59 return GetBlockTransactions(db, hash)
62 fillBlockHashesFn := func(height uint64) ([]*bc.Hash, error) {
63 return GetBlockHashesByHeight(db, height)
66 fillMainChainHashFn := func(height uint64) (*bc.Hash, error) {
67 return GetMainChainHash(db, height)
70 cache := newCache(fillBlockHeaderFn, fillBlockTxsFn, fillBlockHashesFn, fillMainChainHashFn)
77 // GetBlockHeader return the BlockHeader by given hash
78 func (s *Store) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) {
79 return s.cache.lookupBlockHeader(hash)
82 // GetUtxo will search the utxo in db
83 func (s *Store) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) {
84 return getUtxo(s.db, hash)
87 func (s *Store) GetContract(hash [32]byte) ([]byte, error) {
88 return getContract(s.db, hash)
91 // BlockExist check if the block is stored in disk
92 func (s *Store) BlockExist(hash *bc.Hash) bool {
93 _, err := s.cache.lookupBlockHeader(hash)
97 // SaveBlockHeader persists a new block header in the protocol.
98 func (s *Store) SaveBlockHeader(blockHeader *types.BlockHeader) error {
99 binaryBlockHeader, err := blockHeader.MarshalText()
101 return errors.Wrap(err, "Marshal block header")
104 blockHash := blockHeader.Hash()
105 s.db.Set(CalcBlockHeaderKey(&blockHash), binaryBlockHeader)
106 s.cache.removeBlockHeader(blockHeader)
110 // GetBlockHashesByHeight return the block hash by the specified height
111 func (s *Store) GetBlockHashesByHeight(height uint64) ([]*bc.Hash, error) {
112 return s.cache.lookupBlockHashesByHeight(height)
115 // GetMainChainHash return the block hash by the specified height
116 func (s *Store) GetMainChainHash(height uint64) (*bc.Hash, error) {
117 return s.cache.lookupMainChainHash(height)
120 // SaveBlock persists a new block in the protocol.
121 func (s *Store) SaveBlock(block *types.Block) error {
122 startTime := time.Now()
123 binaryBlockHeader, err := block.MarshalTextForBlockHeader()
125 return errors.Wrap(err, "Marshal block header")
128 binaryBlockTxs, err := block.MarshalTextForTransactions()
130 return errors.Wrap(err, "Marshal block transactions")
133 blockHashes := []*bc.Hash{}
134 hashes, err := s.GetBlockHashesByHeight(block.Height)
139 blockHashes = append(blockHashes, hashes...)
140 blockHash := block.Hash()
141 blockHashes = append(blockHashes, &blockHash)
142 binaryBlockHashes, err := json.Marshal(blockHashes)
144 return errors.Wrap(err, "Marshal block hashes")
147 batch := s.db.NewBatch()
148 batch.Set(CalcBlockHashesKey(block.Height), binaryBlockHashes)
149 batch.Set(CalcBlockHeaderKey(&blockHash), binaryBlockHeader)
150 batch.Set(CalcBlockTransactionsKey(&blockHash), binaryBlockTxs)
151 batch.Set(CalcBlockHeaderIndexKey(block.Height, &blockHash), binaryBlockHeader)
154 s.cache.removeBlockHashes(block.Height)
155 log.WithFields(log.Fields{
157 "height": block.Height,
158 "hash": blockHash.String(),
159 "duration": time.Since(startTime),
160 }).Info("block saved on disk")
164 // GetBlockTransactions return the Block transactions by given hash
165 func (s *Store) GetBlockTransactions(hash *bc.Hash) ([]*types.Tx, error) {
166 return s.cache.lookupBlockTxs(hash)
169 // GetBlock return the block by given hash
170 func (s *Store) GetBlock(hash *bc.Hash) (*types.Block, error) {
171 blockHeader, err := s.GetBlockHeader(hash)
176 txs, err := s.GetBlockTransactions(hash)
182 BlockHeader: *blockHeader,
187 // GetTransactionsUtxo will return all the utxo that related to the input txs
188 func (s *Store) GetTransactionsUtxo(view *state.UtxoViewpoint, txs []*bc.Tx) error {
189 return getTransactionsUtxo(s.db, view, txs)
192 // GetStoreStatus return the BlockStoreStateJSON
193 func (s *Store) GetStoreStatus() *protocol.BlockStoreState {
194 return loadBlockStoreStateJSON(s.db)
197 // SaveChainStatus save the core's newest status && delete old status
198 func (s *Store) SaveChainStatus(blockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, contractView *state.ContractViewpoint, finalizedHeight uint64, finalizedHash *bc.Hash) error {
199 batch := s.db.NewBatch()
200 if err := saveUtxoView(batch, view); err != nil {
204 if err := deleteContractView(s.db, batch, contractView); err != nil {
208 if err := saveContractView(s.db, batch, contractView); err != nil {
212 blockHeaderHash := blockHeader.Hash()
213 bytes, err := json.Marshal(
214 protocol.BlockStoreState{
215 Height: blockHeader.Height,
216 Hash: &blockHeaderHash,
217 FinalizedHeight: finalizedHeight,
218 FinalizedHash: finalizedHash,
224 batch.Set(BlockStoreKey, bytes)
226 var clearCacheFuncs []func()
227 // save main chain blockHeaders
228 for _, blockHeader := range mainBlockHeaders {
230 blockHash := bh.Hash()
231 binaryBlockHash, err := blockHash.MarshalText()
233 return errors.Wrap(err, "Marshal block hash")
236 batch.Set(calcMainChainIndexPrefix(bh.Height), binaryBlockHash)
237 clearCacheFuncs = append(clearCacheFuncs, func() {
238 s.cache.removeMainChainHash(bh.Height)
242 for _, clearCacheFunc := range clearCacheFuncs {
249 func calcCheckpointKey(height uint64, hash *bc.Hash) []byte {
250 buf := make([]byte, 8)
251 binary.BigEndian.PutUint64(buf, height)
252 key := append(CheckpointPrefix, buf...)
254 key = append(key, hash.Bytes()...)
259 func (s *Store) GetCheckpoint(hash *bc.Hash) (*state.Checkpoint, error) {
260 header, err := s.GetBlockHeader(hash)
265 data := s.db.Get(calcCheckpointKey(header.Height, hash))
266 checkpoint := &state.Checkpoint{}
267 if err := json.Unmarshal(data, checkpoint); err != nil {
271 checkpoint.SupLinks = append(checkpoint.SupLinks, header.SupLinks...)
272 return checkpoint, nil
275 // GetCheckpointsByHeight return all checkpoints of specified block height
276 func (s *Store) GetCheckpointsByHeight(height uint64) ([]*state.Checkpoint, error) {
277 iter := s.db.IteratorPrefix(calcCheckpointKey(height, nil))
279 return s.loadCheckpointsFromIter(iter)
282 // CheckpointsFromNode return all checkpoints from specified block height and hash
283 func (s *Store) CheckpointsFromNode(height uint64, hash *bc.Hash) ([]*state.Checkpoint, error) {
284 startKey := calcCheckpointKey(height, hash)
285 iter := s.db.IteratorPrefixWithStart(CheckpointPrefix, startKey, false)
287 firstCheckpoint := &state.Checkpoint{}
288 if err := json.Unmarshal(iter.Value(), firstCheckpoint); err != nil {
292 checkpoints := []*state.Checkpoint{firstCheckpoint}
293 subs, err := s.loadCheckpointsFromIter(iter)
298 checkpoints = append(checkpoints, subs...)
299 return checkpoints, nil
302 func (s *Store) loadCheckpointsFromIter(iter dbm.Iterator) ([]*state.Checkpoint, error) {
303 var checkpoints []*state.Checkpoint
306 checkpoint := &state.Checkpoint{}
307 if err := json.Unmarshal(iter.Value(), checkpoint); err != nil {
311 header, err := s.GetBlockHeader(&checkpoint.Hash)
316 checkpoint.SupLinks = append(checkpoint.SupLinks, header.SupLinks...)
317 checkpoints = append(checkpoints, checkpoint)
319 return checkpoints, nil
322 // SaveCheckpoints bulk save multiple checkpoint
323 func (s *Store) SaveCheckpoints(checkpoints []*state.Checkpoint) error {
324 batch := s.db.NewBatch()
326 if err := s.saveCheckpoints(batch, checkpoints); err != nil {
334 func (s *Store) saveCheckpoints(batch dbm.Batch, checkpoints []*state.Checkpoint) error {
335 for _, checkpoint := range checkpoints {
336 startTime := time.Now()
337 data, err := json.Marshal(checkpoint)
342 if checkpoint.Height%consensus.ActiveNetParams.BlocksOfEpoch != 1 {
343 header, err := s.GetBlockHeader(&checkpoint.Hash)
348 batch.Delete(calcCheckpointKey(header.Height-1, &header.PreviousBlockHash))
351 batch.Set(calcCheckpointKey(checkpoint.Height, &checkpoint.Hash), data)
352 log.WithFields(log.Fields{
354 "height": checkpoint.Height,
355 "hash": checkpoint.Hash.String(),
356 "status": checkpoint.Status,
357 "duration": time.Since(startTime),
358 }).Info("checkpoint saved on disk")