github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/database/store.go (about)

     1  package database
     2  
     3  import (
     4  	"encoding/binary"
     5  	"encoding/json"
     6  	"time"
     7  
     8  	"github.com/golang/protobuf/proto"
     9  	log "github.com/sirupsen/logrus"
    10  	"github.com/tendermint/tmlibs/common"
    11  
    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"
    18  	dbm "github.com/bytom/bytom/database/leveldb"
    19  )
    20  
    21  const logModule = "leveldb"
    22  
    23  var (
    24  	BlockStoreKey     = []byte("blockStore")
    25  	BlockPrefix       = []byte("B:")
    26  	BlockHeaderPrefix = []byte("BH:")
    27  	TxStatusPrefix    = []byte("BTS:")
    28  )
    29  
    30  func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
    31  	bytes := db.Get(BlockStoreKey)
    32  	if bytes == nil {
    33  		return nil
    34  	}
    35  	bsj := &protocol.BlockStoreState{}
    36  	if err := json.Unmarshal(bytes, bsj); err != nil {
    37  		common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes))
    38  	}
    39  	return bsj
    40  }
    41  
    42  // A Store encapsulates storage for blockchain validation.
    43  // It satisfies the interface protocol.Store, and provides additional
    44  // methods for querying current data.
    45  type Store struct {
    46  	db    dbm.DB
    47  	cache blockCache
    48  }
    49  
    50  func CalcBlockKey(hash *bc.Hash) []byte {
    51  	return append(BlockPrefix, hash.Bytes()...)
    52  }
    53  
    54  func CalcBlockHeaderKey(height uint64, hash *bc.Hash) []byte {
    55  	buf := [8]byte{}
    56  	binary.BigEndian.PutUint64(buf[:], height)
    57  	key := append(BlockHeaderPrefix, buf[:]...)
    58  	return append(key, hash.Bytes()...)
    59  }
    60  
    61  func CalcTxStatusKey(hash *bc.Hash) []byte {
    62  	return append(TxStatusPrefix, hash.Bytes()...)
    63  }
    64  
    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))
    68  	if bytez == nil {
    69  		return nil, nil
    70  	}
    71  
    72  	block := &types.Block{}
    73  	err := block.UnmarshalText(bytez)
    74  	return block, err
    75  }
    76  
    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)
    81  	})
    82  	return &Store{
    83  		db:    db,
    84  		cache: cache,
    85  	}
    86  }
    87  
    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)
    91  }
    92  
    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
    97  }
    98  
    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)
   102  }
   103  
   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)
   107  }
   108  
   109  // GetTransactionStatus will return the utxo that related to the block hash
   110  func (s *Store) GetTransactionStatus(hash *bc.Hash) (*bc.TransactionStatus, error) {
   111  	data := s.db.Get(CalcTxStatusKey(hash))
   112  	if data == nil {
   113  		return nil, errors.New("can't find the transaction status by given hash")
   114  	}
   115  
   116  	ts := &bc.TransactionStatus{}
   117  	if err := proto.Unmarshal(data, ts); err != nil {
   118  		return nil, errors.Wrap(err, "unmarshaling transaction status")
   119  	}
   120  	return ts, nil
   121  }
   122  
   123  // GetStoreStatus return the BlockStoreStateJSON
   124  func (s *Store) GetStoreStatus() *protocol.BlockStoreState {
   125  	return loadBlockStoreStateJSON(s.db)
   126  }
   127  
   128  func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error) {
   129  	startTime := time.Now()
   130  	blockIndex := state.NewBlockIndex()
   131  	bhIter := s.db.IteratorPrefix(BlockHeaderPrefix)
   132  	defer bhIter.Release()
   133  
   134  	var lastNode *state.BlockNode
   135  	for bhIter.Next() {
   136  		bh := &types.BlockHeader{}
   137  		if err := bh.UnmarshalText(bhIter.Value()); err != nil {
   138  			return nil, err
   139  		}
   140  
   141  		// If a block with a height greater than the best height of state is added to the index,
   142  		// It may cause a bug that the new block cant not be process properly.
   143  		if bh.Height > stateBestHeight {
   144  			break
   145  		}
   146  
   147  		var parent *state.BlockNode
   148  		if lastNode == nil || lastNode.Hash == bh.PreviousBlockHash {
   149  			parent = lastNode
   150  		} else {
   151  			parent = blockIndex.GetNode(&bh.PreviousBlockHash)
   152  		}
   153  
   154  		node, err := state.NewBlockNode(bh, parent)
   155  		if err != nil {
   156  			return nil, err
   157  		}
   158  
   159  		blockIndex.AddNode(node)
   160  		lastNode = node
   161  	}
   162  
   163  	log.WithFields(log.Fields{
   164  		"module":   logModule,
   165  		"height":   stateBestHeight,
   166  		"duration": time.Since(startTime),
   167  	}).Debug("initialize load history block index from database")
   168  	return blockIndex, nil
   169  }
   170  
   171  // SaveBlock persists a new block in the protocol.
   172  func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus) error {
   173  	startTime := time.Now()
   174  	binaryBlock, err := block.MarshalText()
   175  	if err != nil {
   176  		return errors.Wrap(err, "Marshal block meta")
   177  	}
   178  
   179  	binaryBlockHeader, err := block.BlockHeader.MarshalText()
   180  	if err != nil {
   181  		return errors.Wrap(err, "Marshal block header")
   182  	}
   183  
   184  	binaryTxStatus, err := proto.Marshal(ts)
   185  	if err != nil {
   186  		return errors.Wrap(err, "marshal block transaction status")
   187  	}
   188  
   189  	blockHash := block.Hash()
   190  	batch := s.db.NewBatch()
   191  	batch.Set(CalcBlockKey(&blockHash), binaryBlock)
   192  	batch.Set(CalcBlockHeaderKey(block.Height, &blockHash), binaryBlockHeader)
   193  	batch.Set(CalcTxStatusKey(&blockHash), binaryTxStatus)
   194  	batch.Write()
   195  
   196  	log.WithFields(log.Fields{
   197  		"module":   logModule,
   198  		"height":   block.Height,
   199  		"hash":     blockHash.String(),
   200  		"duration": time.Since(startTime),
   201  	}).Info("block saved on disk")
   202  	return nil
   203  }
   204  
   205  // SaveChainStatus save the core's newest status && delete old status
   206  func (s *Store) SaveChainStatus(node *state.BlockNode, view *state.UtxoViewpoint) error {
   207  	batch := s.db.NewBatch()
   208  	if err := saveUtxoView(batch, view); err != nil {
   209  		return err
   210  	}
   211  
   212  	bytes, err := json.Marshal(protocol.BlockStoreState{Height: node.Height, Hash: &node.Hash})
   213  	if err != nil {
   214  		return err
   215  	}
   216  
   217  	batch.Set(BlockStoreKey, bytes)
   218  	batch.Write()
   219  	return nil
   220  }