github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/internal/store/store.go (about)

     1  package store
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"strconv"
     8  
     9  	"github.com/gogo/protobuf/proto"
    10  	"github.com/google/orderedcode"
    11  	dbm "github.com/tendermint/tm-db"
    12  
    13  	tmproto "github.com/ari-anchor/sei-tendermint/proto/tendermint/types"
    14  	"github.com/ari-anchor/sei-tendermint/types"
    15  )
    16  
    17  /*
    18  BlockStore is a simple low level store for blocks.
    19  
    20  There are three types of information stored:
    21    - BlockMeta:   Meta information about each block
    22    - Block part:  Parts of each block, aggregated w/ PartSet
    23    - Commit:      The commit part of each block, for gossiping precommit votes
    24  
    25  Currently the precommit signatures are duplicated in the Block parts as
    26  well as the Commit.  In the future this may change, perhaps by moving
    27  the Commit data outside the Block. (TODO)
    28  
    29  The store can be assumed to contain all contiguous blocks between base and height (inclusive).
    30  
    31  // NOTE: BlockStore methods will panic if they encounter errors
    32  // deserializing loaded data, indicating probable corruption on disk.
    33  */
    34  type BlockStore struct {
    35  	db dbm.DB
    36  }
    37  
    38  // NewBlockStore returns a new BlockStore with the given DB,
    39  // initialized to the last height that was committed to the DB.
    40  func NewBlockStore(db dbm.DB) *BlockStore {
    41  	return &BlockStore{db}
    42  }
    43  
    44  // Base returns the first known contiguous block height, or 0 for empty block stores.
    45  func (bs *BlockStore) Base() int64 {
    46  	iter, err := bs.db.Iterator(
    47  		blockMetaKey(1),
    48  		blockMetaKey(1<<63-1),
    49  	)
    50  	if err != nil {
    51  		panic(err)
    52  	}
    53  	defer iter.Close()
    54  
    55  	if iter.Valid() {
    56  		height, err := decodeBlockMetaKey(iter.Key())
    57  		if err == nil {
    58  			return height
    59  		}
    60  	}
    61  	if err := iter.Error(); err != nil {
    62  		panic(err)
    63  	}
    64  
    65  	return 0
    66  }
    67  
    68  // Height returns the last known contiguous block height, or 0 for empty block stores.
    69  func (bs *BlockStore) Height() int64 {
    70  	iter, err := bs.db.ReverseIterator(
    71  		blockMetaKey(1),
    72  		blockMetaKey(1<<63-1),
    73  	)
    74  
    75  	if err != nil {
    76  		panic(err)
    77  	}
    78  	defer iter.Close()
    79  
    80  	if iter.Valid() {
    81  		height, err := decodeBlockMetaKey(iter.Key())
    82  		if err == nil {
    83  			return height
    84  		}
    85  	}
    86  	if err := iter.Error(); err != nil {
    87  		panic(err)
    88  	}
    89  	return 0
    90  }
    91  
    92  // Size returns the number of blocks in the block store.
    93  func (bs *BlockStore) Size() int64 {
    94  	height := bs.Height()
    95  	if height == 0 {
    96  		return 0
    97  	}
    98  	return height + 1 - bs.Base()
    99  }
   100  
   101  // LoadBase atomically loads the base block meta, or returns nil if no base is found.
   102  func (bs *BlockStore) LoadBaseMeta() *types.BlockMeta {
   103  	iter, err := bs.db.Iterator(
   104  		blockMetaKey(1),
   105  		blockMetaKey(1<<63-1),
   106  	)
   107  	if err != nil {
   108  		return nil
   109  	}
   110  	defer iter.Close()
   111  
   112  	if iter.Valid() {
   113  		var pbbm = new(tmproto.BlockMeta)
   114  		err = proto.Unmarshal(iter.Value(), pbbm)
   115  		if err != nil {
   116  			panic(fmt.Errorf("unmarshal to tmproto.BlockMeta: %w", err))
   117  		}
   118  
   119  		blockMeta, err := types.BlockMetaFromProto(pbbm)
   120  		if err != nil {
   121  			panic(fmt.Errorf("error from proto blockMeta: %w", err))
   122  		}
   123  
   124  		return blockMeta
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  // LoadBlock returns the block with the given height.
   131  // If no block is found for that height, it returns nil.
   132  func (bs *BlockStore) LoadBlock(height int64) *types.Block {
   133  	var blockMeta = bs.LoadBlockMeta(height)
   134  	if blockMeta == nil {
   135  		return nil
   136  	}
   137  
   138  	pbb := new(tmproto.Block)
   139  	buf := []byte{}
   140  	for i := 0; i < int(blockMeta.BlockID.PartSetHeader.Total); i++ {
   141  		part := bs.LoadBlockPart(height, i)
   142  		// If the part is missing (e.g. since it has been deleted after we
   143  		// loaded the block meta) we consider the whole block to be missing.
   144  		if part == nil {
   145  			return nil
   146  		}
   147  		buf = append(buf, part.Bytes...)
   148  	}
   149  	err := proto.Unmarshal(buf, pbb)
   150  	if err != nil {
   151  		// NOTE: The existence of meta should imply the existence of the
   152  		// block. So, make sure meta is only saved after blocks are saved.
   153  		panic(fmt.Errorf("error reading block: %w", err))
   154  	}
   155  
   156  	block, err := types.BlockFromProto(pbb)
   157  	if err != nil {
   158  		panic(fmt.Errorf("error from proto block: %w", err))
   159  	}
   160  
   161  	return block
   162  }
   163  
   164  // LoadBlockByHash returns the block with the given hash.
   165  // If no block is found for that hash, it returns nil.
   166  // Panics if it fails to parse height associated with the given hash.
   167  func (bs *BlockStore) LoadBlockByHash(hash []byte) *types.Block {
   168  	bz, err := bs.db.Get(blockHashKey(hash))
   169  	if err != nil {
   170  		panic(err)
   171  	}
   172  	if len(bz) == 0 {
   173  		return nil
   174  	}
   175  
   176  	s := string(bz)
   177  	height, err := strconv.ParseInt(s, 10, 64)
   178  
   179  	if err != nil {
   180  		panic(fmt.Sprintf("failed to extract height from %s: %v", s, err))
   181  	}
   182  	return bs.LoadBlock(height)
   183  }
   184  
   185  // LoadBlockMetaByHash returns the blockmeta who's header corresponds to the given
   186  // hash. If none is found, returns nil.
   187  func (bs *BlockStore) LoadBlockMetaByHash(hash []byte) *types.BlockMeta {
   188  	bz, err := bs.db.Get(blockHashKey(hash))
   189  	if err != nil {
   190  		panic(err)
   191  	}
   192  	if len(bz) == 0 {
   193  		return nil
   194  	}
   195  
   196  	s := string(bz)
   197  	height, err := strconv.ParseInt(s, 10, 64)
   198  
   199  	if err != nil {
   200  		panic(fmt.Sprintf("failed to extract height from %s: %v", s, err))
   201  	}
   202  	return bs.LoadBlockMeta(height)
   203  }
   204  
   205  // LoadBlockPart returns the Part at the given index
   206  // from the block at the given height.
   207  // If no part is found for the given height and index, it returns nil.
   208  func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
   209  	var pbpart = new(tmproto.Part)
   210  
   211  	bz, err := bs.db.Get(blockPartKey(height, index))
   212  	if err != nil {
   213  		panic(err)
   214  	}
   215  	if len(bz) == 0 {
   216  		return nil
   217  	}
   218  
   219  	err = proto.Unmarshal(bz, pbpart)
   220  	if err != nil {
   221  		panic(fmt.Errorf("unmarshal to tmproto.Part failed: %w", err))
   222  	}
   223  	part, err := types.PartFromProto(pbpart)
   224  	if err != nil {
   225  		panic(fmt.Errorf("error reading block part: %w", err))
   226  	}
   227  
   228  	return part
   229  }
   230  
   231  // LoadBlockMeta returns the BlockMeta for the given height.
   232  // If no block is found for the given height, it returns nil.
   233  func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
   234  	var pbbm = new(tmproto.BlockMeta)
   235  	bz, err := bs.db.Get(blockMetaKey(height))
   236  
   237  	if err != nil {
   238  		panic(err)
   239  	}
   240  
   241  	if len(bz) == 0 {
   242  		return nil
   243  	}
   244  
   245  	err = proto.Unmarshal(bz, pbbm)
   246  	if err != nil {
   247  		panic(fmt.Errorf("unmarshal to tmproto.BlockMeta: %w", err))
   248  	}
   249  
   250  	blockMeta, err := types.BlockMetaFromProto(pbbm)
   251  	if err != nil {
   252  		panic(fmt.Errorf("error from proto blockMeta: %w", err))
   253  	}
   254  
   255  	return blockMeta
   256  }
   257  
   258  // LoadBlockCommit returns the Commit for the given height.
   259  // This commit consists of the +2/3 and other Precommit-votes for block at `height`,
   260  // and it comes from the block.LastCommit for `height+1`.
   261  // If no commit is found for the given height, it returns nil.
   262  func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit {
   263  	var pbc = new(tmproto.Commit)
   264  	bz, err := bs.db.Get(blockCommitKey(height))
   265  	if err != nil {
   266  		panic(err)
   267  	}
   268  	if len(bz) == 0 {
   269  		return nil
   270  	}
   271  	err = proto.Unmarshal(bz, pbc)
   272  	if err != nil {
   273  		panic(fmt.Errorf("error reading block commit: %w", err))
   274  	}
   275  	commit, err := types.CommitFromProto(pbc)
   276  	if err != nil {
   277  		panic(fmt.Errorf("converting commit to proto: %w", err))
   278  	}
   279  	return commit
   280  }
   281  
   282  // LoadExtendedCommit returns the ExtendedCommit for the given height.
   283  // The extended commit is not guaranteed to contain the same +2/3 precommits data
   284  // as the commit in the block.
   285  func (bs *BlockStore) LoadBlockExtendedCommit(height int64) *types.ExtendedCommit {
   286  	pbec := new(tmproto.ExtendedCommit)
   287  	bz, err := bs.db.Get(extCommitKey(height))
   288  	if err != nil {
   289  		panic(fmt.Errorf("fetching extended commit: %w", err))
   290  	}
   291  	if len(bz) == 0 {
   292  		return nil
   293  	}
   294  	err = proto.Unmarshal(bz, pbec)
   295  	if err != nil {
   296  		panic(fmt.Errorf("decoding extended commit: %w", err))
   297  	}
   298  	extCommit, err := types.ExtendedCommitFromProto(pbec)
   299  	if err != nil {
   300  		panic(fmt.Errorf("converting extended commit: %w", err))
   301  	}
   302  	return extCommit
   303  }
   304  
   305  // LoadSeenCommit returns the last locally seen Commit before being
   306  // cannonicalized. This is useful when we've seen a commit, but there
   307  // has not yet been a new block at `height + 1` that includes this
   308  // commit in its block.LastCommit.
   309  func (bs *BlockStore) LoadSeenCommit() *types.Commit {
   310  	var pbc = new(tmproto.Commit)
   311  	bz, err := bs.db.Get(seenCommitKey())
   312  	if err != nil {
   313  		panic(err)
   314  	}
   315  	if len(bz) == 0 {
   316  		return nil
   317  	}
   318  	err = proto.Unmarshal(bz, pbc)
   319  	if err != nil {
   320  		panic(fmt.Errorf("error reading block seen commit: %w", err))
   321  	}
   322  
   323  	commit, err := types.CommitFromProto(pbc)
   324  	if err != nil {
   325  		panic(fmt.Errorf("converting seen commit: %w", err))
   326  	}
   327  	return commit
   328  }
   329  
   330  // PruneBlocks removes block up to (but not including) a height. It returns the number of blocks pruned.
   331  func (bs *BlockStore) PruneBlocks(height int64) (uint64, error) {
   332  	if height <= 0 {
   333  		return 0, fmt.Errorf("height must be greater than 0")
   334  	}
   335  
   336  	if height > bs.Height() {
   337  		return 0, fmt.Errorf("height must be equal to or less than the latest height %d", bs.Height())
   338  	}
   339  
   340  	// when removing the block meta, use the hash to remove the hash key at the same time
   341  	removeBlockHash := func(key, value []byte, batch dbm.Batch) error {
   342  		// unmarshal block meta
   343  		var pbbm = new(tmproto.BlockMeta)
   344  		err := proto.Unmarshal(value, pbbm)
   345  		if err != nil {
   346  			return fmt.Errorf("unmarshal to tmproto.BlockMeta: %w", err)
   347  		}
   348  
   349  		blockMeta, err := types.BlockMetaFromProto(pbbm)
   350  		if err != nil {
   351  			return fmt.Errorf("error from proto blockMeta: %w", err)
   352  		}
   353  
   354  		// delete the hash key corresponding to the block meta's hash
   355  		if err := batch.Delete(blockHashKey(blockMeta.BlockID.Hash)); err != nil {
   356  			return fmt.Errorf("failed to delete hash key: %X: %w", blockHashKey(blockMeta.BlockID.Hash), err)
   357  		}
   358  
   359  		return nil
   360  	}
   361  
   362  	// remove block meta first as this is used to indicate whether the block exists.
   363  	// For this reason, we also use ony block meta as a measure of the amount of blocks pruned
   364  	pruned, err := bs.pruneRange(blockMetaKey(0), blockMetaKey(height), removeBlockHash)
   365  	if err != nil {
   366  		return pruned, err
   367  	}
   368  
   369  	if _, err := bs.pruneRange(blockPartKey(0, 0), blockPartKey(height, 0), nil); err != nil {
   370  		return pruned, err
   371  	}
   372  
   373  	if _, err := bs.pruneRange(blockCommitKey(0), blockCommitKey(height), nil); err != nil {
   374  		return pruned, err
   375  	}
   376  
   377  	return pruned, nil
   378  }
   379  
   380  // pruneRange is a generic function for deleting a range of values based on the lowest
   381  // height up to but excluding retainHeight. For each key/value pair, an optional hook can be
   382  // executed before the deletion itself is made. pruneRange will use batch delete to delete
   383  // keys in batches of at most 1000 keys.
   384  func (bs *BlockStore) pruneRange(
   385  	start []byte,
   386  	end []byte,
   387  	preDeletionHook func(key, value []byte, batch dbm.Batch) error,
   388  ) (uint64, error) {
   389  	var (
   390  		err         error
   391  		pruned      uint64
   392  		totalPruned uint64
   393  	)
   394  
   395  	batch := bs.db.NewBatch()
   396  	defer batch.Close()
   397  
   398  	pruned, start, err = bs.batchDelete(batch, start, end, preDeletionHook)
   399  	if err != nil {
   400  		return totalPruned, err
   401  	}
   402  
   403  	// loop until we have finished iterating over all the keys by writing, opening a new batch
   404  	// and incrementing through the next range of keys.
   405  	for !bytes.Equal(start, end) {
   406  		if err := batch.Write(); err != nil {
   407  			return totalPruned, err
   408  		}
   409  
   410  		totalPruned += pruned
   411  
   412  		if err := batch.Close(); err != nil {
   413  			return totalPruned, err
   414  		}
   415  
   416  		batch = bs.db.NewBatch()
   417  
   418  		pruned, start, err = bs.batchDelete(batch, start, end, preDeletionHook)
   419  		if err != nil {
   420  			return totalPruned, err
   421  		}
   422  	}
   423  
   424  	// once we looped over all keys we do a final flush to disk
   425  	if err := batch.WriteSync(); err != nil {
   426  		return totalPruned, err
   427  	}
   428  	totalPruned += pruned
   429  	return totalPruned, nil
   430  }
   431  
   432  // batchDelete runs an iterator over a set of keys, first preforming a pre deletion hook before adding it to the batch.
   433  // The function ends when either 1000 keys have been added to the batch or the iterator has reached the end.
   434  func (bs *BlockStore) batchDelete(
   435  	batch dbm.Batch,
   436  	start, end []byte,
   437  	preDeletionHook func(key, value []byte, batch dbm.Batch) error,
   438  ) (uint64, []byte, error) {
   439  	var pruned uint64
   440  	iter, err := bs.db.Iterator(start, end)
   441  	if err != nil {
   442  		return pruned, start, err
   443  	}
   444  	defer iter.Close()
   445  
   446  	for ; iter.Valid(); iter.Next() {
   447  		key := iter.Key()
   448  		if preDeletionHook != nil {
   449  			if err := preDeletionHook(key, iter.Value(), batch); err != nil {
   450  				return 0, start, fmt.Errorf("pruning error at key %X: %w", iter.Key(), err)
   451  			}
   452  		}
   453  
   454  		if err := batch.Delete(key); err != nil {
   455  			return 0, start, fmt.Errorf("pruning error at key %X: %w", iter.Key(), err)
   456  		}
   457  
   458  		pruned++
   459  		if pruned == 1000 {
   460  			return pruned, iter.Key(), iter.Error()
   461  		}
   462  	}
   463  
   464  	return pruned, end, iter.Error()
   465  }
   466  
   467  // SaveBlock persists the given block, blockParts, and seenCommit to the underlying db.
   468  // blockParts: Must be parts of the block
   469  // seenCommit: The +2/3 precommits that were seen which committed at height.
   470  //
   471  //	If all the nodes restart after committing a block,
   472  //	we need this to reload the precommits to catch-up nodes to the
   473  //	most recent height.  Otherwise they'd stall at H-1.
   474  func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
   475  	if block == nil {
   476  		panic("BlockStore can only save a non-nil block")
   477  	}
   478  	batch := bs.db.NewBatch()
   479  	if err := bs.saveBlockToBatch(batch, block, blockParts, seenCommit); err != nil {
   480  		panic(err)
   481  	}
   482  
   483  	if err := batch.WriteSync(); err != nil {
   484  		panic(err)
   485  	}
   486  
   487  	if err := batch.Close(); err != nil {
   488  		panic(err)
   489  	}
   490  }
   491  
   492  // SaveBlockWithExtendedCommit persists the given block, blockParts, and
   493  // seenExtendedCommit to the underlying db. seenExtendedCommit is stored under
   494  // two keys in the database: as the seenCommit and as the ExtendedCommit data for the
   495  // height. This allows the vote extension data to be persisted for all blocks
   496  // that are saved.
   497  func (bs *BlockStore) SaveBlockWithExtendedCommit(block *types.Block, blockParts *types.PartSet, seenExtendedCommit *types.ExtendedCommit) {
   498  	if block == nil {
   499  		panic("BlockStore can only save a non-nil block")
   500  	}
   501  	if err := seenExtendedCommit.EnsureExtensions(); err != nil {
   502  		panic(fmt.Errorf("saving block with extensions: %w", err))
   503  	}
   504  	batch := bs.db.NewBatch()
   505  	if err := bs.saveBlockToBatch(batch, block, blockParts, seenExtendedCommit.ToCommit()); err != nil {
   506  		panic(err)
   507  	}
   508  	height := block.Height
   509  
   510  	pbec := seenExtendedCommit.ToProto()
   511  	extCommitBytes := mustEncode(pbec)
   512  	if err := batch.Set(extCommitKey(height), extCommitBytes); err != nil {
   513  		panic(err)
   514  	}
   515  
   516  	if err := batch.WriteSync(); err != nil {
   517  		panic(err)
   518  	}
   519  
   520  	if err := batch.Close(); err != nil {
   521  		panic(err)
   522  	}
   523  }
   524  
   525  func (bs *BlockStore) saveBlockToBatch(batch dbm.Batch, block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) error {
   526  	if block == nil {
   527  		panic("BlockStore can only save a non-nil block")
   528  	}
   529  
   530  	height := block.Height
   531  	hash := block.Hash()
   532  
   533  	if g, w := height, bs.Height()+1; bs.Base() > 0 && g != w {
   534  		return fmt.Errorf("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g)
   535  	}
   536  	if !blockParts.IsComplete() {
   537  		return errors.New("BlockStore can only save complete block part sets")
   538  	}
   539  	if height != seenCommit.Height {
   540  		return fmt.Errorf("BlockStore cannot save seen commit of a different height (block: %d, commit: %d)", height, seenCommit.Height)
   541  	}
   542  
   543  	// Save block parts. This must be done before the block meta, since callers
   544  	// typically load the block meta first as an indication that the block exists
   545  	// and then go on to load block parts - we must make sure the block is
   546  	// complete as soon as the block meta is written.
   547  	for i := 0; i < int(blockParts.Total()); i++ {
   548  		part := blockParts.GetPart(i)
   549  		bs.saveBlockPart(height, i, part, batch)
   550  	}
   551  
   552  	blockMeta := types.NewBlockMeta(block, blockParts)
   553  	pbm := blockMeta.ToProto()
   554  	if pbm == nil {
   555  		return errors.New("nil blockmeta")
   556  	}
   557  
   558  	metaBytes := mustEncode(pbm)
   559  	if err := batch.Set(blockMetaKey(height), metaBytes); err != nil {
   560  		return err
   561  	}
   562  
   563  	if err := batch.Set(blockHashKey(hash), []byte(fmt.Sprintf("%d", height))); err != nil {
   564  		return err
   565  	}
   566  
   567  	pbc := block.LastCommit.ToProto()
   568  	blockCommitBytes := mustEncode(pbc)
   569  	if err := batch.Set(blockCommitKey(height-1), blockCommitBytes); err != nil {
   570  		return err
   571  	}
   572  
   573  	// Save seen commit (seen +2/3 precommits for block)
   574  	pbsc := seenCommit.ToProto()
   575  	seenCommitBytes := mustEncode(pbsc)
   576  	if err := batch.Set(seenCommitKey(), seenCommitBytes); err != nil {
   577  		return err
   578  	}
   579  
   580  	return nil
   581  }
   582  
   583  func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part, batch dbm.Batch) {
   584  	pbp, err := part.ToProto()
   585  	if err != nil {
   586  		panic(fmt.Errorf("unable to make part into proto: %w", err))
   587  	}
   588  	partBytes := mustEncode(pbp)
   589  	if err := batch.Set(blockPartKey(height, index), partBytes); err != nil {
   590  		panic(err)
   591  	}
   592  }
   593  
   594  // SaveSeenCommit saves a seen commit, used by e.g. the state sync reactor when bootstrapping node.
   595  func (bs *BlockStore) SaveSeenCommit(height int64, seenCommit *types.Commit) error {
   596  	pbc := seenCommit.ToProto()
   597  	seenCommitBytes, err := proto.Marshal(pbc)
   598  	if err != nil {
   599  		return fmt.Errorf("unable to marshal commit: %w", err)
   600  	}
   601  	return bs.db.Set(seenCommitKey(), seenCommitBytes)
   602  }
   603  
   604  func (bs *BlockStore) SaveSignedHeader(sh *types.SignedHeader, blockID types.BlockID) error {
   605  	// first check that the block store doesn't already have the block
   606  	bz, err := bs.db.Get(blockMetaKey(sh.Height))
   607  	if err != nil {
   608  		return err
   609  	}
   610  	if bz != nil {
   611  		return fmt.Errorf("block at height %d already saved", sh.Height)
   612  	}
   613  
   614  	// FIXME: saving signed headers although necessary for proving evidence,
   615  	// doesn't have complete parity with block meta's thus block size and num
   616  	// txs are filled with negative numbers. We should aim to find a solution to
   617  	// this.
   618  	blockMeta := &types.BlockMeta{
   619  		BlockID:   blockID,
   620  		BlockSize: -1,
   621  		Header:    *sh.Header,
   622  		NumTxs:    -1,
   623  	}
   624  
   625  	batch := bs.db.NewBatch()
   626  
   627  	pbm := blockMeta.ToProto()
   628  	metaBytes := mustEncode(pbm)
   629  	if err := batch.Set(blockMetaKey(sh.Height), metaBytes); err != nil {
   630  		return fmt.Errorf("unable to save block meta: %w", err)
   631  	}
   632  
   633  	pbc := sh.Commit.ToProto()
   634  	blockCommitBytes := mustEncode(pbc)
   635  	if err := batch.Set(blockCommitKey(sh.Height), blockCommitBytes); err != nil {
   636  		return fmt.Errorf("unable to save commit: %w", err)
   637  	}
   638  
   639  	if err := batch.WriteSync(); err != nil {
   640  		return err
   641  	}
   642  
   643  	return batch.Close()
   644  }
   645  
   646  func (bs *BlockStore) Close() error {
   647  	return bs.db.Close()
   648  }
   649  
   650  //---------------------------------- KEY ENCODING -----------------------------------------
   651  
   652  // key prefixes
   653  // NB: Before modifying these, cross-check them with those in
   654  // * internal/store/store.go    [0..4, 13]
   655  // * internal/state/store.go    [5..8, 14]
   656  // * internal/evidence/pool.go  [9..10]
   657  // * light/store/db/db.go       [11..12]
   658  // TODO(thane): Move all these to their own package.
   659  // TODO: what about these (they already collide):
   660  // * scripts/scmigrate/migrate.go [3] --> Looks OK, as it is also called "SeenCommit"
   661  // * internal/p2p/peermanager.go  [1]
   662  const (
   663  	// prefixes are unique across all tm db's
   664  	prefixBlockMeta   = int64(0)
   665  	prefixBlockPart   = int64(1)
   666  	prefixBlockCommit = int64(2)
   667  	prefixSeenCommit  = int64(3)
   668  	prefixBlockHash   = int64(4)
   669  	prefixExtCommit   = int64(13)
   670  )
   671  
   672  func blockMetaKey(height int64) []byte {
   673  	key, err := orderedcode.Append(nil, prefixBlockMeta, height)
   674  	if err != nil {
   675  		panic(err)
   676  	}
   677  	return key
   678  }
   679  
   680  func decodeBlockMetaKey(key []byte) (height int64, err error) {
   681  	var prefix int64
   682  	remaining, err := orderedcode.Parse(string(key), &prefix, &height)
   683  	if err != nil {
   684  		return
   685  	}
   686  	if len(remaining) != 0 {
   687  		return -1, fmt.Errorf("expected complete key but got remainder: %s", remaining)
   688  	}
   689  	if prefix != prefixBlockMeta {
   690  		return -1, fmt.Errorf("incorrect prefix. Expected %v, got %v", prefixBlockMeta, prefix)
   691  	}
   692  	return
   693  }
   694  
   695  func blockPartKey(height int64, partIndex int) []byte {
   696  	key, err := orderedcode.Append(nil, prefixBlockPart, height, int64(partIndex))
   697  	if err != nil {
   698  		panic(err)
   699  	}
   700  	return key
   701  }
   702  
   703  func blockCommitKey(height int64) []byte {
   704  	key, err := orderedcode.Append(nil, prefixBlockCommit, height)
   705  	if err != nil {
   706  		panic(err)
   707  	}
   708  	return key
   709  }
   710  
   711  func seenCommitKey() []byte {
   712  	key, err := orderedcode.Append(nil, prefixSeenCommit)
   713  	if err != nil {
   714  		panic(err)
   715  	}
   716  	return key
   717  }
   718  
   719  func extCommitKey(height int64) []byte {
   720  	key, err := orderedcode.Append(nil, prefixExtCommit, height)
   721  	if err != nil {
   722  		panic(err)
   723  	}
   724  	return key
   725  }
   726  
   727  func blockHashKey(hash []byte) []byte {
   728  	key, err := orderedcode.Append(nil, prefixBlockHash, string(hash))
   729  	if err != nil {
   730  		panic(err)
   731  	}
   732  	return key
   733  }
   734  
   735  //-----------------------------------------------------------------------------
   736  
   737  // mustEncode proto encodes a proto.message and panics if fails
   738  func mustEncode(pb proto.Message) []byte {
   739  	bz, err := proto.Marshal(pb)
   740  	if err != nil {
   741  		panic(fmt.Errorf("unable to marshal: %w", err))
   742  	}
   743  	return bz
   744  }
   745  
   746  //-----------------------------------------------------------------------------
   747  
   748  // DeleteLatestBlock removes the block pointed to by height,
   749  // lowering height by one.
   750  func (bs *BlockStore) DeleteLatestBlock() error {
   751  	targetHeight := bs.Height()
   752  	batch := bs.db.NewBatch()
   753  	fmt.Printf("Permanently deleting target height=%d from block store\n", targetHeight)
   754  	// delete what we can, skipping what's already missing, to ensure partial
   755  	// blocks get deleted fully.
   756  	if meta := bs.LoadBlockMeta(targetHeight); meta != nil {
   757  		if err := batch.Delete(blockHashKey(meta.BlockID.Hash)); err != nil {
   758  			return err
   759  		}
   760  		for p := 0; p < int(meta.BlockID.PartSetHeader.Total); p++ {
   761  			if err := batch.Delete(blockPartKey(targetHeight, p)); err != nil {
   762  				return err
   763  			}
   764  		}
   765  	}
   766  	if err := batch.Delete(blockCommitKey(targetHeight)); err != nil {
   767  		return err
   768  	}
   769  	if err := batch.Delete(seenCommitKey()); err != nil {
   770  		return err
   771  	}
   772  	// delete last, so as to not leave keys built on meta.BlockID dangling
   773  	if err := batch.Delete(blockMetaKey(targetHeight)); err != nil {
   774  		return err
   775  	}
   776  
   777  	err := batch.WriteSync()
   778  	if err != nil {
   779  		return fmt.Errorf("failed to delete height %v: %w", targetHeight, err)
   780  	}
   781  
   782  	if err := batch.Close(); err != nil {
   783  		panic(err)
   784  	}
   785  	return nil
   786  }