github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/store/store.go (about)

     1  package store
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  
     7  	"github.com/gogo/protobuf/proto"
     8  	dbm "github.com/tendermint/tm-db"
     9  
    10  	tmsync "github.com/tendermint/tendermint/libs/sync"
    11  	tmstore "github.com/tendermint/tendermint/proto/tendermint/store"
    12  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    13  	"github.com/tendermint/tendermint/types"
    14  )
    15  
    16  /*
    17  BlockStore is a simple low level store for blocks.
    18  
    19  There are three types of information stored:
    20   - BlockMeta:   Meta information about each block
    21   - Block part:  Parts of each block, aggregated w/ PartSet
    22   - Commit:      The commit part of each block, for gossiping precommit votes
    23  
    24  Currently the precommit signatures are duplicated in the Block parts as
    25  well as the Commit.  In the future this may change, perhaps by moving
    26  the Commit data outside the Block. (TODO)
    27  
    28  The store can be assumed to contain all contiguous blocks between base and height (inclusive).
    29  
    30  // NOTE: BlockStore methods will panic if they encounter errors
    31  // deserializing loaded data, indicating probable corruption on disk.
    32  */
    33  type BlockStore struct {
    34  	db dbm.DB
    35  
    36  	mtx    tmsync.RWMutex
    37  	base   int64
    38  	height int64
    39  }
    40  
    41  // NewBlockStore returns a new BlockStore with the given DB,
    42  // initialized to the last height that was committed to the DB.
    43  func NewBlockStore(db dbm.DB) *BlockStore {
    44  	bs := LoadBlockStoreState(db)
    45  	return &BlockStore{
    46  		base:   bs.Base,
    47  		height: bs.Height,
    48  		db:     db,
    49  	}
    50  }
    51  
    52  // Base returns the first known contiguous block height, or 0 for empty block stores.
    53  func (bs *BlockStore) Base() int64 {
    54  	bs.mtx.RLock()
    55  	defer bs.mtx.RUnlock()
    56  	return bs.base
    57  }
    58  
    59  // Height returns the last known contiguous block height, or 0 for empty block stores.
    60  func (bs *BlockStore) Height() int64 {
    61  	bs.mtx.RLock()
    62  	defer bs.mtx.RUnlock()
    63  	return bs.height
    64  }
    65  
    66  // Size returns the number of blocks in the block store.
    67  func (bs *BlockStore) Size() int64 {
    68  	bs.mtx.RLock()
    69  	defer bs.mtx.RUnlock()
    70  	if bs.height == 0 {
    71  		return 0
    72  	}
    73  	return bs.height - bs.base + 1
    74  }
    75  
    76  // LoadBlock returns the block with the given height.
    77  // If no block is found for that height, it returns nil.
    78  func (bs *BlockStore) LoadBlock(height int64) *types.Block {
    79  	var blockMeta = bs.LoadBlockMeta(height)
    80  	if blockMeta == nil {
    81  		return nil
    82  	}
    83  
    84  	pbb := new(tmproto.Block)
    85  	buf := []byte{}
    86  	for i := 0; i < int(blockMeta.BlockID.PartSetHeader.Total); i++ {
    87  		part := bs.LoadBlockPart(height, i)
    88  		buf = append(buf, part.Bytes...)
    89  	}
    90  	err := proto.Unmarshal(buf, pbb)
    91  	if err != nil {
    92  		// NOTE: The existence of meta should imply the existence of the
    93  		// block. So, make sure meta is only saved after blocks are saved.
    94  		panic(fmt.Sprintf("Error reading block: %v", err))
    95  	}
    96  
    97  	block, err := types.BlockFromProto(pbb)
    98  	if err != nil {
    99  		panic(fmt.Errorf("error from proto block: %w", err))
   100  	}
   101  
   102  	return block
   103  }
   104  
   105  // LoadBlockByHash returns the block with the given hash.
   106  // If no block is found for that hash, it returns nil.
   107  // Panics if it fails to parse height associated with the given hash.
   108  func (bs *BlockStore) LoadBlockByHash(hash []byte) *types.Block {
   109  	bz, err := bs.db.Get(calcBlockHashKey(hash))
   110  	if err != nil {
   111  		panic(err)
   112  	}
   113  	if len(bz) == 0 {
   114  		return nil
   115  	}
   116  
   117  	s := string(bz)
   118  	height, err := strconv.ParseInt(s, 10, 64)
   119  
   120  	if err != nil {
   121  		panic(fmt.Sprintf("failed to extract height from %s: %v", s, err))
   122  	}
   123  	return bs.LoadBlock(height)
   124  }
   125  
   126  // LoadBlockPart returns the Part at the given index
   127  // from the block at the given height.
   128  // If no part is found for the given height and index, it returns nil.
   129  func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
   130  	var pbpart = new(tmproto.Part)
   131  
   132  	bz, err := bs.db.Get(calcBlockPartKey(height, index))
   133  	if err != nil {
   134  		panic(err)
   135  	}
   136  	if len(bz) == 0 {
   137  		return nil
   138  	}
   139  
   140  	err = proto.Unmarshal(bz, pbpart)
   141  	if err != nil {
   142  		panic(fmt.Errorf("unmarshal to tmproto.Part failed: %w", err))
   143  	}
   144  	part, err := types.PartFromProto(pbpart)
   145  	if err != nil {
   146  		panic(fmt.Sprintf("Error reading block part: %v", err))
   147  	}
   148  
   149  	return part
   150  }
   151  
   152  // LoadBlockMeta returns the BlockMeta for the given height.
   153  // If no block is found for the given height, it returns nil.
   154  func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
   155  	var pbbm = new(tmproto.BlockMeta)
   156  	bz, err := bs.db.Get(calcBlockMetaKey(height))
   157  
   158  	if err != nil {
   159  		panic(err)
   160  	}
   161  
   162  	if len(bz) == 0 {
   163  		return nil
   164  	}
   165  
   166  	err = proto.Unmarshal(bz, pbbm)
   167  	if err != nil {
   168  		panic(fmt.Errorf("unmarshal to tmproto.BlockMeta: %w", err))
   169  	}
   170  
   171  	blockMeta, err := types.BlockMetaFromProto(pbbm)
   172  	if err != nil {
   173  		panic(fmt.Errorf("error from proto blockMeta: %w", err))
   174  	}
   175  
   176  	return blockMeta
   177  }
   178  
   179  // LoadBlockCommit returns the Commit for the given height.
   180  // This commit consists of the +2/3 and other Precommit-votes for block at `height`,
   181  // and it comes from the block.LastCommit for `height+1`.
   182  // If no commit is found for the given height, it returns nil.
   183  func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit {
   184  	var pbc = new(tmproto.Commit)
   185  	bz, err := bs.db.Get(calcBlockCommitKey(height))
   186  	if err != nil {
   187  		panic(err)
   188  	}
   189  	if len(bz) == 0 {
   190  		return nil
   191  	}
   192  	err = proto.Unmarshal(bz, pbc)
   193  	if err != nil {
   194  		panic(fmt.Errorf("error reading block commit: %w", err))
   195  	}
   196  	commit, err := types.CommitFromProto(pbc)
   197  	if err != nil {
   198  		panic(fmt.Sprintf("Error reading block commit: %v", err))
   199  	}
   200  	return commit
   201  }
   202  
   203  // LoadSeenCommit returns the locally seen Commit for the given height.
   204  // This is useful when we've seen a commit, but there has not yet been
   205  // a new block at `height + 1` that includes this commit in its block.LastCommit.
   206  func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit {
   207  	var pbc = new(tmproto.Commit)
   208  	bz, err := bs.db.Get(calcSeenCommitKey(height))
   209  	if err != nil {
   210  		panic(err)
   211  	}
   212  	if len(bz) == 0 {
   213  		return nil
   214  	}
   215  	err = proto.Unmarshal(bz, pbc)
   216  	if err != nil {
   217  		panic(fmt.Sprintf("error reading block seen commit: %v", err))
   218  	}
   219  
   220  	commit, err := types.CommitFromProto(pbc)
   221  	if err != nil {
   222  		panic(fmt.Errorf("error from proto commit: %w", err))
   223  	}
   224  	return commit
   225  }
   226  
   227  // PruneBlocks removes block up to (but not including) a height. It returns number of blocks pruned.
   228  func (bs *BlockStore) PruneBlocks(height int64) (uint64, error) {
   229  	if height <= 0 {
   230  		return 0, fmt.Errorf("height must be greater than 0")
   231  	}
   232  	bs.mtx.RLock()
   233  	if height > bs.height {
   234  		bs.mtx.RUnlock()
   235  		return 0, fmt.Errorf("cannot prune beyond the latest height %v", bs.height)
   236  	}
   237  	base := bs.base
   238  	bs.mtx.RUnlock()
   239  	if height < base {
   240  		return 0, fmt.Errorf("cannot prune to height %v, it is lower than base height %v",
   241  			height, base)
   242  	}
   243  
   244  	pruned := uint64(0)
   245  	batch := bs.db.NewBatch()
   246  	defer batch.Close()
   247  	flush := func(batch dbm.Batch, base int64) error {
   248  		// We can't trust batches to be atomic, so update base first to make sure noone
   249  		// tries to access missing blocks.
   250  		bs.mtx.Lock()
   251  		bs.base = base
   252  		bs.mtx.Unlock()
   253  		bs.saveState()
   254  
   255  		err := batch.WriteSync()
   256  		if err != nil {
   257  			return fmt.Errorf("failed to prune up to height %v: %w", base, err)
   258  		}
   259  		batch.Close()
   260  		return nil
   261  	}
   262  
   263  	for h := base; h < height; h++ {
   264  		meta := bs.LoadBlockMeta(h)
   265  		if meta == nil { // assume already deleted
   266  			continue
   267  		}
   268  		if err := batch.Delete(calcBlockMetaKey(h)); err != nil {
   269  			return 0, err
   270  		}
   271  		if err := batch.Delete(calcBlockHashKey(meta.BlockID.Hash)); err != nil {
   272  			return 0, err
   273  		}
   274  		if err := batch.Delete(calcBlockCommitKey(h)); err != nil {
   275  			return 0, err
   276  		}
   277  		if err := batch.Delete(calcSeenCommitKey(h)); err != nil {
   278  			return 0, err
   279  		}
   280  		for p := 0; p < int(meta.BlockID.PartSetHeader.Total); p++ {
   281  			if err := batch.Delete(calcBlockPartKey(h, p)); err != nil {
   282  				return 0, err
   283  			}
   284  		}
   285  		pruned++
   286  
   287  		// flush every 1000 blocks to avoid batches becoming too large
   288  		if pruned%1000 == 0 && pruned > 0 {
   289  			err := flush(batch, h)
   290  			if err != nil {
   291  				return 0, err
   292  			}
   293  			batch = bs.db.NewBatch()
   294  			defer batch.Close()
   295  		}
   296  	}
   297  
   298  	err := flush(batch, height)
   299  	if err != nil {
   300  		return 0, err
   301  	}
   302  	return pruned, nil
   303  }
   304  
   305  // SaveBlock persists the given block, blockParts, and seenCommit to the underlying db.
   306  // blockParts: Must be parts of the block
   307  // seenCommit: The +2/3 precommits that were seen which committed at height.
   308  //             If all the nodes restart after committing a block,
   309  //             we need this to reload the precommits to catch-up nodes to the
   310  //             most recent height.  Otherwise they'd stall at H-1.
   311  func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
   312  	if block == nil {
   313  		panic("BlockStore can only save a non-nil block")
   314  	}
   315  
   316  	height := block.Height
   317  	hash := block.Hash()
   318  
   319  	if g, w := height, bs.Height()+1; bs.Base() > 0 && g != w {
   320  		panic(fmt.Sprintf("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g))
   321  	}
   322  	if !blockParts.IsComplete() {
   323  		panic("BlockStore can only save complete block part sets")
   324  	}
   325  
   326  	// Save block meta
   327  	blockMeta := types.NewBlockMeta(block, blockParts)
   328  	pbm := blockMeta.ToProto()
   329  	if pbm == nil {
   330  		panic("nil blockmeta")
   331  	}
   332  	metaBytes := mustEncode(pbm)
   333  	if err := bs.db.Set(calcBlockMetaKey(height), metaBytes); err != nil {
   334  		panic(err)
   335  	}
   336  	if err := bs.db.Set(calcBlockHashKey(hash), []byte(fmt.Sprintf("%d", height))); err != nil {
   337  		panic(err)
   338  	}
   339  
   340  	// Save block parts
   341  	for i := 0; i < int(blockParts.Total()); i++ {
   342  		part := blockParts.GetPart(i)
   343  		bs.saveBlockPart(height, i, part)
   344  	}
   345  
   346  	// Save block commit (duplicate and separate from the Block)
   347  	pbc := block.LastCommit.ToProto()
   348  	blockCommitBytes := mustEncode(pbc)
   349  	if err := bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes); err != nil {
   350  		panic(err)
   351  	}
   352  
   353  	// Save seen commit (seen +2/3 precommits for block)
   354  	// NOTE: we can delete this at a later height
   355  	pbsc := seenCommit.ToProto()
   356  	seenCommitBytes := mustEncode(pbsc)
   357  	if err := bs.db.Set(calcSeenCommitKey(height), seenCommitBytes); err != nil {
   358  		panic(err)
   359  	}
   360  
   361  	// Done!
   362  	bs.mtx.Lock()
   363  	bs.height = height
   364  	if bs.base == 0 {
   365  		bs.base = height
   366  	}
   367  	bs.mtx.Unlock()
   368  
   369  	// Save new BlockStoreState descriptor. This also flushes the database.
   370  	bs.saveState()
   371  }
   372  
   373  func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part) {
   374  	pbp, err := part.ToProto()
   375  	if err != nil {
   376  		panic(fmt.Errorf("unable to make part into proto: %w", err))
   377  	}
   378  	partBytes := mustEncode(pbp)
   379  	if err := bs.db.Set(calcBlockPartKey(height, index), partBytes); err != nil {
   380  		panic(err)
   381  	}
   382  }
   383  
   384  func (bs *BlockStore) saveState() {
   385  	bs.mtx.RLock()
   386  	bss := tmstore.BlockStoreState{
   387  		Base:   bs.base,
   388  		Height: bs.height,
   389  	}
   390  	bs.mtx.RUnlock()
   391  	SaveBlockStoreState(&bss, bs.db)
   392  }
   393  
   394  // SaveSeenCommit saves a seen commit, used by e.g. the state sync reactor when bootstrapping node.
   395  func (bs *BlockStore) SaveSeenCommit(height int64, seenCommit *types.Commit) error {
   396  	pbc := seenCommit.ToProto()
   397  	seenCommitBytes, err := proto.Marshal(pbc)
   398  	if err != nil {
   399  		return fmt.Errorf("unable to marshal commit: %w", err)
   400  	}
   401  	return bs.db.Set(calcSeenCommitKey(height), seenCommitBytes)
   402  }
   403  
   404  //-----------------------------------------------------------------------------
   405  
   406  func calcBlockMetaKey(height int64) []byte {
   407  	return []byte(fmt.Sprintf("H:%v", height))
   408  }
   409  
   410  func calcBlockPartKey(height int64, partIndex int) []byte {
   411  	return []byte(fmt.Sprintf("P:%v:%v", height, partIndex))
   412  }
   413  
   414  func calcBlockCommitKey(height int64) []byte {
   415  	return []byte(fmt.Sprintf("C:%v", height))
   416  }
   417  
   418  func calcSeenCommitKey(height int64) []byte {
   419  	return []byte(fmt.Sprintf("SC:%v", height))
   420  }
   421  
   422  func calcBlockHashKey(hash []byte) []byte {
   423  	return []byte(fmt.Sprintf("BH:%x", hash))
   424  }
   425  
   426  //-----------------------------------------------------------------------------
   427  
   428  var blockStoreKey = []byte("blockStore")
   429  
   430  // SaveBlockStoreState persists the blockStore state to the database.
   431  func SaveBlockStoreState(bsj *tmstore.BlockStoreState, db dbm.DB) {
   432  	bytes, err := proto.Marshal(bsj)
   433  	if err != nil {
   434  		panic(fmt.Sprintf("Could not marshal state bytes: %v", err))
   435  	}
   436  	if err := db.SetSync(blockStoreKey, bytes); err != nil {
   437  		panic(err)
   438  	}
   439  }
   440  
   441  // LoadBlockStoreState returns the BlockStoreState as loaded from disk.
   442  // If no BlockStoreState was previously persisted, it returns the zero value.
   443  func LoadBlockStoreState(db dbm.DB) tmstore.BlockStoreState {
   444  	bytes, err := db.Get(blockStoreKey)
   445  	if err != nil {
   446  		panic(err)
   447  	}
   448  
   449  	if len(bytes) == 0 {
   450  		return tmstore.BlockStoreState{
   451  			Base:   0,
   452  			Height: 0,
   453  		}
   454  	}
   455  
   456  	var bsj tmstore.BlockStoreState
   457  	if err := proto.Unmarshal(bytes, &bsj); err != nil {
   458  		panic(fmt.Sprintf("Could not unmarshal bytes: %X", bytes))
   459  	}
   460  
   461  	// Backwards compatibility with persisted data from before Base existed.
   462  	if bsj.Height > 0 && bsj.Base == 0 {
   463  		bsj.Base = 1
   464  	}
   465  	return bsj
   466  }
   467  
   468  //mustEncode proto encodes a proto.message and panics if fails
   469  func mustEncode(pb proto.Message) []byte {
   470  	bz, err := proto.Marshal(pb)
   471  	if err != nil {
   472  		panic(fmt.Errorf("unable to marshal: %w", err))
   473  	}
   474  	return bz
   475  }