const logModule = "leveldb"
var (
- blockStoreKey = []byte("blockStore")
- blockPrefix = []byte("B:")
- blockHeaderPrefix = []byte("BH:")
- txStatusPrefix = []byte("BTS:")
+ blockStoreKey = []byte("blockStore")
+ blockHeaderPrefix = []byte("BH:")
+ blockTransactonsPrefix = []byte("BTXS:")
+ txStatusPrefix = []byte("BTS:")
+ voteResultPrefix = []byte("VR:")
)
func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
cache blockCache
}
-func calcBlockKey(hash *bc.Hash) []byte {
- return append(blockPrefix, hash.Bytes()...)
-}
-
func calcBlockHeaderKey(height uint64, hash *bc.Hash) []byte {
buf := [8]byte{}
binary.BigEndian.PutUint64(buf[:], height)
return append(key, hash.Bytes()...)
}
+func calcBlockTransactionsKey(hash *bc.Hash) []byte {
+ return append(blockTransactonsPrefix, hash.Bytes()...)
+}
+
func calcTxStatusKey(hash *bc.Hash) []byte {
return append(txStatusPrefix, hash.Bytes()...)
}
-// GetBlock return the block by given hash
-func GetBlock(db dbm.DB, hash *bc.Hash) (*types.Block, error) {
- bytez := db.Get(calcBlockKey(hash))
- if bytez == nil {
+func calcVoteResultKey(seq uint64) []byte {
+ buf := [8]byte{}
+ binary.BigEndian.PutUint64(buf[:], seq)
+ return append(voteResultPrefix, buf[:]...)
+}
+
+// GetBlockHeader return the block header by given hash and height
+func GetBlockHeader(db dbm.DB, hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
+ block := &types.Block{}
+ binaryBlockHeader := db.Get(calcBlockHeaderKey(height, hash))
+ if binaryBlockHeader == nil {
return nil, nil
}
+ if err := block.UnmarshalText(binaryBlockHeader); err != nil {
+ return nil, err
+ }
+
+ return &block.BlockHeader, nil
+}
+// GetBlockTransactions return the block transactions by given hash
+func GetBlockTransactions(db dbm.DB, hash *bc.Hash) ([]*types.Tx, error) {
block := &types.Block{}
- err := block.UnmarshalText(bytez)
- return block, err
+ binaryBlockTxs := db.Get(calcBlockTransactionsKey(hash))
+ if binaryBlockTxs == nil {
+ return nil, errors.New("The transactions in the block is empty")
+ }
+
+ if err := block.UnmarshalText(binaryBlockTxs); err != nil {
+ return nil, err
+ }
+ return block.Transactions, nil
+}
+
+// GetVoteResult return the vote result by given sequence
+func GetVoteResult(db dbm.DB, seq uint64) (*state.VoteResult, error) {
+ data := db.Get(calcVoteResultKey(seq))
+ if data == nil {
+ return nil, protocol.ErrNotFoundVoteResult
+ }
+
+ voteResult := new(state.VoteResult)
+ if err := json.Unmarshal(data, voteResult); err != nil {
+ return nil, errors.Wrap(err, "unmarshaling vote result")
+ }
+ return voteResult, nil
}
// NewStore creates and returns a new Store object.
func NewStore(db dbm.DB) *Store {
- cache := newBlockCache(func(hash *bc.Hash) (*types.Block, error) {
- return GetBlock(db, hash)
- })
+ fillBlockHeaderFn := func(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
+ return GetBlockHeader(db, hash, height)
+ }
+ fillBlockTxsFn := func(hash *bc.Hash) ([]*types.Tx, error) {
+ return GetBlockTransactions(db, hash)
+ }
+ fillVoteResultFn := func(seq uint64) (*state.VoteResult, error) {
+ return GetVoteResult(db, seq)
+ }
+ bc := newBlockCache(fillBlockHeaderFn, fillBlockTxsFn, fillVoteResultFn)
return &Store{
db: db,
- cache: cache,
+ cache: bc,
}
}
}
// BlockExist check if the block is stored in disk
-func (s *Store) BlockExist(hash *bc.Hash) bool {
- block, err := s.cache.lookup(hash)
- return err == nil && block != nil
+func (s *Store) BlockExist(hash *bc.Hash, height uint64) bool {
+ blockHeader, err := s.cache.lookupBlockHeader(hash, height)
+ return err == nil && blockHeader != nil
}
// GetBlock return the block by given hash
-func (s *Store) GetBlock(hash *bc.Hash) (*types.Block, error) {
- return s.cache.lookup(hash)
+func (s *Store) GetBlock(hash *bc.Hash, height uint64) (*types.Block, error) {
+ blockHeader, err := s.GetBlockHeader(hash, height)
+ if err != nil {
+ return nil, err
+ }
+
+ txs, err := s.GetBlockTransactions(hash)
+ if err != nil {
+ return nil, err
+ }
+
+ return &types.Block{
+ BlockHeader: *blockHeader,
+ Transactions: txs,
+ }, nil
+}
+
+// GetBlockHeader return the BlockHeader by given hash
+func (s *Store) GetBlockHeader(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
+ blockHeader, err := s.cache.lookupBlockHeader(hash, height)
+ if err != nil {
+ return nil, err
+ }
+ return blockHeader, nil
+}
+
+// GetBlockTransactions return the Block transactions by given hash
+func (s *Store) GetBlockTransactions(hash *bc.Hash) ([]*types.Tx, error) {
+ txs, err := s.cache.lookupBlockTxs(hash)
+ if err != nil {
+ return nil, err
+ }
+ return txs, nil
}
// GetTransactionsUtxo will return all the utxo that related to the input txs
return loadBlockStoreStateJSON(s.db)
}
+// GetVoteResult retrive the voting result in specified vote sequence
+func (s *Store) GetVoteResult(seq uint64) (*state.VoteResult, error) {
+ return s.cache.lookupVoteResult(seq)
+}
+
func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error) {
startTime := time.Now()
blockIndex := state.NewBlockIndex()
// SaveBlock persists a new block in the protocol.
func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus) error {
startTime := time.Now()
- binaryBlock, err := block.MarshalText()
+
+ binaryBlockHeader, err := block.MarshalTextForBlockHeader()
if err != nil {
- return errors.Wrap(err, "Marshal block meta")
+ return errors.Wrap(err, "Marshal block header")
}
- binaryBlockHeader, err := block.BlockHeader.MarshalText()
+ binaryBlockTxs, err := block.MarshalTextForTransactions()
if err != nil {
- return errors.Wrap(err, "Marshal block header")
+ return errors.Wrap(err, "Marshal block transactions")
}
binaryTxStatus, err := proto.Marshal(ts)
blockHash := block.Hash()
batch := s.db.NewBatch()
- batch.Set(calcBlockKey(&blockHash), binaryBlock)
batch.Set(calcBlockHeaderKey(block.Height, &blockHash), binaryBlockHeader)
+ batch.Set(calcBlockTransactionsKey(&blockHash), binaryBlockTxs)
batch.Set(calcTxStatusKey(&blockHash), binaryTxStatus)
batch.Write()
return nil
}
+// SaveBlockHeader persists a new block header in the protocol.
+func (s *Store) SaveBlockHeader(blockHeader *types.BlockHeader) error {
+ startTime := time.Now()
+
+ binaryBlockHeader, err := blockHeader.MarshalText()
+ if err != nil {
+ return errors.Wrap(err, "Marshal block header")
+ }
+
+ blockHash := blockHeader.Hash()
+ s.db.Set(calcBlockHeaderKey(blockHeader.Height, &blockHash), binaryBlockHeader)
+
+ // updata blockheader cache
+ if _, ok := s.cache.getBlockHeader(&blockHash); ok {
+ s.cache.addBlockHeader(blockHeader)
+ }
+
+ log.WithFields(log.Fields{
+ "module": logModule,
+ "height": blockHeader.Height,
+ "hash": blockHash.String(),
+ "duration": time.Since(startTime),
+ }).Info("blockHeader saved on disk")
+ return nil
+}
+
// SaveChainStatus save the core's newest status && delete old status
-func (s *Store) SaveChainStatus(node *state.BlockNode, view *state.UtxoViewpoint) error {
+func (s *Store) SaveChainStatus(node, irreversibleNode *state.BlockNode, view *state.UtxoViewpoint, voteResults []*state.VoteResult) error {
batch := s.db.NewBatch()
if err := saveUtxoView(batch, view); err != nil {
return err
}
- bytes, err := json.Marshal(protocol.BlockStoreState{Height: node.Height, Hash: &node.Hash})
+ for _, vote := range voteResults {
+ bytes, err := json.Marshal(vote)
+ if err != nil {
+ return err
+ }
+
+ batch.Set(calcVoteResultKey(vote.Seq), bytes)
+ if _, ok := s.cache.getVoteResult(vote.Seq); ok {
+ s.cache.addVoteResult(vote)
+ }
+ }
+
+ bytes, err := json.Marshal(protocol.BlockStoreState{
+ Height: node.Height,
+ Hash: &node.Hash,
+ IrreversibleHeight: irreversibleNode.Height,
+ IrreversibleHash: &irreversibleNode.Hash,
+ })
if err != nil {
return err
}