github.com/number571/tendermint@v0.34.11-gost/light/store/db/db.go (about)

     1  package db
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  
     7  	"github.com/google/orderedcode"
     8  	dbm "github.com/tendermint/tm-db"
     9  
    10  	tmsync "github.com/number571/tendermint/internal/libs/sync"
    11  	"github.com/number571/tendermint/light/store"
    12  	tmproto "github.com/number571/tendermint/proto/tendermint/types"
    13  	"github.com/number571/tendermint/types"
    14  )
    15  
    16  const (
    17  	prefixLightBlock = int64(11)
    18  	prefixSize       = int64(12)
    19  )
    20  
    21  type dbs struct {
    22  	db dbm.DB
    23  
    24  	mtx  tmsync.RWMutex
    25  	size uint16
    26  }
    27  
    28  // New returns a Store that wraps any DB
    29  // If you want to share one DB across many light clients consider using PrefixDB
    30  func New(db dbm.DB) store.Store {
    31  
    32  	lightStore := &dbs{db: db}
    33  
    34  	// retrieve the size of the db
    35  	size := uint16(0)
    36  	bz, err := lightStore.db.Get(lightStore.sizeKey())
    37  	if err == nil && len(bz) > 0 {
    38  		size = unmarshalSize(bz)
    39  	}
    40  	lightStore.size = size
    41  
    42  	return lightStore
    43  }
    44  
    45  // SaveLightBlock persists LightBlock to the db.
    46  //
    47  // Safe for concurrent use by multiple goroutines.
    48  func (s *dbs) SaveLightBlock(lb *types.LightBlock) error {
    49  	if lb.Height <= 0 {
    50  		panic("negative or zero height")
    51  	}
    52  
    53  	lbpb, err := lb.ToProto()
    54  	if err != nil {
    55  		return fmt.Errorf("unable to convert light block to protobuf: %w", err)
    56  	}
    57  
    58  	lbBz, err := lbpb.Marshal()
    59  	if err != nil {
    60  		return fmt.Errorf("marshaling LightBlock: %w", err)
    61  	}
    62  
    63  	s.mtx.Lock()
    64  	defer s.mtx.Unlock()
    65  
    66  	b := s.db.NewBatch()
    67  	defer b.Close()
    68  	if err = b.Set(s.lbKey(lb.Height), lbBz); err != nil {
    69  		return err
    70  	}
    71  	if err = b.Set(s.sizeKey(), marshalSize(s.size+1)); err != nil {
    72  		return err
    73  	}
    74  	if err = b.WriteSync(); err != nil {
    75  		return err
    76  	}
    77  	s.size++
    78  
    79  	return nil
    80  }
    81  
    82  // DeleteLightBlockAndValidatorSet deletes the LightBlock from
    83  // the db.
    84  //
    85  // Safe for concurrent use by multiple goroutines.
    86  func (s *dbs) DeleteLightBlock(height int64) error {
    87  	if height <= 0 {
    88  		panic("negative or zero height")
    89  	}
    90  
    91  	s.mtx.Lock()
    92  	defer s.mtx.Unlock()
    93  
    94  	b := s.db.NewBatch()
    95  	defer b.Close()
    96  	if err := b.Delete(s.lbKey(height)); err != nil {
    97  		return err
    98  	}
    99  	if err := b.Set(s.sizeKey(), marshalSize(s.size-1)); err != nil {
   100  		return err
   101  	}
   102  	if err := b.WriteSync(); err != nil {
   103  		return err
   104  	}
   105  	s.size--
   106  
   107  	return nil
   108  }
   109  
   110  // LightBlock retrieves the LightBlock at the given height.
   111  //
   112  // Safe for concurrent use by multiple goroutines.
   113  func (s *dbs) LightBlock(height int64) (*types.LightBlock, error) {
   114  	if height <= 0 {
   115  		panic("negative or zero height")
   116  	}
   117  
   118  	bz, err := s.db.Get(s.lbKey(height))
   119  	if err != nil {
   120  		panic(err)
   121  	}
   122  	if len(bz) == 0 {
   123  		return nil, store.ErrLightBlockNotFound
   124  	}
   125  
   126  	var lbpb tmproto.LightBlock
   127  	err = lbpb.Unmarshal(bz)
   128  	if err != nil {
   129  		return nil, fmt.Errorf("unmarshal error: %w", err)
   130  	}
   131  
   132  	lightBlock, err := types.LightBlockFromProto(&lbpb)
   133  	if err != nil {
   134  		return nil, fmt.Errorf("proto conversion error: %w", err)
   135  	}
   136  
   137  	return lightBlock, err
   138  }
   139  
   140  // LastLightBlockHeight returns the last LightBlock height stored.
   141  //
   142  // Safe for concurrent use by multiple goroutines.
   143  func (s *dbs) LastLightBlockHeight() (int64, error) {
   144  	itr, err := s.db.ReverseIterator(
   145  		s.lbKey(1),
   146  		append(s.lbKey(1<<63-1), byte(0x00)),
   147  	)
   148  	if err != nil {
   149  		panic(err)
   150  	}
   151  	defer itr.Close()
   152  
   153  	if itr.Valid() {
   154  		return s.decodeLbKey(itr.Key())
   155  	}
   156  
   157  	return -1, itr.Error()
   158  }
   159  
   160  // FirstLightBlockHeight returns the first LightBlock height stored.
   161  //
   162  // Safe for concurrent use by multiple goroutines.
   163  func (s *dbs) FirstLightBlockHeight() (int64, error) {
   164  	itr, err := s.db.Iterator(
   165  		s.lbKey(1),
   166  		append(s.lbKey(1<<63-1), byte(0x00)),
   167  	)
   168  	if err != nil {
   169  		panic(err)
   170  	}
   171  	defer itr.Close()
   172  
   173  	if itr.Valid() {
   174  		return s.decodeLbKey(itr.Key())
   175  	}
   176  
   177  	return -1, itr.Error()
   178  }
   179  
   180  // LightBlockBefore iterates over light blocks until it finds a block before
   181  // the given height. It returns ErrLightBlockNotFound if no such block exists.
   182  //
   183  // Safe for concurrent use by multiple goroutines.
   184  func (s *dbs) LightBlockBefore(height int64) (*types.LightBlock, error) {
   185  	if height <= 0 {
   186  		panic("negative or zero height")
   187  	}
   188  
   189  	itr, err := s.db.ReverseIterator(
   190  		s.lbKey(1),
   191  		s.lbKey(height),
   192  	)
   193  	if err != nil {
   194  		panic(err)
   195  	}
   196  	defer itr.Close()
   197  
   198  	if itr.Valid() {
   199  		var lbpb tmproto.LightBlock
   200  		err = lbpb.Unmarshal(itr.Value())
   201  		if err != nil {
   202  			return nil, fmt.Errorf("unmarshal error: %w", err)
   203  		}
   204  
   205  		lightBlock, err := types.LightBlockFromProto(&lbpb)
   206  		if err != nil {
   207  			return nil, fmt.Errorf("proto conversion error: %w", err)
   208  		}
   209  		return lightBlock, nil
   210  	}
   211  	if err = itr.Error(); err != nil {
   212  		return nil, err
   213  	}
   214  
   215  	return nil, store.ErrLightBlockNotFound
   216  }
   217  
   218  // Prune prunes header & validator set pairs until there are only size pairs
   219  // left.
   220  //
   221  // Safe for concurrent use by multiple goroutines.
   222  func (s *dbs) Prune(size uint16) error {
   223  	// 1) Check how many we need to prune.
   224  	s.mtx.Lock()
   225  	defer s.mtx.Unlock()
   226  	sSize := s.size
   227  
   228  	if sSize <= size { // nothing to prune
   229  		return nil
   230  	}
   231  	numToPrune := sSize - size
   232  
   233  	b := s.db.NewBatch()
   234  	defer b.Close()
   235  
   236  	// 2) use an iterator to batch together all the blocks that need to be deleted
   237  	if err := s.batchDelete(b, numToPrune); err != nil {
   238  		return err
   239  	}
   240  
   241  	// 3) // update size
   242  	s.size = size
   243  	if err := b.Set(s.sizeKey(), marshalSize(size)); err != nil {
   244  		return fmt.Errorf("failed to persist size: %w", err)
   245  	}
   246  
   247  	// 4) write batch deletion to disk
   248  	return b.WriteSync()
   249  }
   250  
   251  // Size returns the number of header & validator set pairs.
   252  //
   253  // Safe for concurrent use by multiple goroutines.
   254  func (s *dbs) Size() uint16 {
   255  	s.mtx.RLock()
   256  	defer s.mtx.RUnlock()
   257  	return s.size
   258  }
   259  
   260  func (s *dbs) batchDelete(batch dbm.Batch, numToPrune uint16) error {
   261  	itr, err := s.db.Iterator(
   262  		s.lbKey(1),
   263  		append(s.lbKey(1<<63-1), byte(0x00)),
   264  	)
   265  	if err != nil {
   266  		return err
   267  	}
   268  	defer itr.Close()
   269  
   270  	for itr.Valid() && numToPrune > 0 {
   271  		if err = batch.Delete(itr.Key()); err != nil {
   272  			return err
   273  		}
   274  		itr.Next()
   275  		numToPrune--
   276  	}
   277  
   278  	return itr.Error()
   279  }
   280  
   281  func (s *dbs) sizeKey() []byte {
   282  	key, err := orderedcode.Append(nil, prefixSize)
   283  	if err != nil {
   284  		panic(err)
   285  	}
   286  	return key
   287  }
   288  
   289  func (s *dbs) lbKey(height int64) []byte {
   290  	key, err := orderedcode.Append(nil, prefixLightBlock, height)
   291  	if err != nil {
   292  		panic(err)
   293  	}
   294  	return key
   295  }
   296  
   297  func (s *dbs) decodeLbKey(key []byte) (height int64, err error) {
   298  	var lightBlockPrefix int64
   299  	remaining, err := orderedcode.Parse(string(key), &lightBlockPrefix, &height)
   300  	if err != nil {
   301  		err = fmt.Errorf("failed to parse light block key: %w", err)
   302  	}
   303  	if len(remaining) != 0 {
   304  		err = fmt.Errorf("expected no remainder when parsing light block key but got: %s", remaining)
   305  	}
   306  	if lightBlockPrefix != prefixLightBlock {
   307  		err = fmt.Errorf("expected light block prefix but got: %d", lightBlockPrefix)
   308  	}
   309  	return
   310  }
   311  
   312  func marshalSize(size uint16) []byte {
   313  	bs := make([]byte, 2)
   314  	binary.LittleEndian.PutUint16(bs, size)
   315  	return bs
   316  }
   317  
   318  func unmarshalSize(bz []byte) uint16 {
   319  	return binary.LittleEndian.Uint16(bz)
   320  }