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

     1  package db
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"regexp"
     7  	"strconv"
     8  
     9  	"github.com/gogo/protobuf/proto"
    10  	dbm "github.com/tendermint/tm-db"
    11  
    12  	tmsync "github.com/tendermint/tendermint/libs/sync"
    13  	"github.com/tendermint/tendermint/light/store"
    14  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    15  	"github.com/tendermint/tendermint/types"
    16  )
    17  
    18  var (
    19  	sizeKey = []byte("size")
    20  )
    21  
    22  type dbs struct {
    23  	db     dbm.DB
    24  	prefix string
    25  
    26  	mtx  tmsync.RWMutex
    27  	size uint16
    28  }
    29  
    30  // New returns a Store that wraps any DB (with an optional prefix in case you
    31  // want to use one DB with many light clients).
    32  func New(db dbm.DB, prefix string) store.Store {
    33  
    34  	size := uint16(0)
    35  	bz, err := db.Get(sizeKey)
    36  	if err == nil && len(bz) > 0 {
    37  		size = unmarshalSize(bz)
    38  	}
    39  
    40  	return &dbs{db: db, prefix: prefix, size: size}
    41  }
    42  
    43  // SaveSignedHeaderAndValidatorSet persists SignedHeader and ValidatorSet to
    44  // the db.
    45  //
    46  // Safe for concurrent use by multiple goroutines.
    47  func (s *dbs) SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error {
    48  	if sh.Height <= 0 {
    49  		panic("negative or zero height")
    50  	}
    51  
    52  	pbsh := sh.ToProto()
    53  
    54  	shBz, err := proto.Marshal(pbsh)
    55  	if err != nil {
    56  		return fmt.Errorf("marshalling SignedHeader: %w", err)
    57  	}
    58  
    59  	pbvs, err := valSet.ToProto()
    60  	if err != nil {
    61  		return fmt.Errorf("unable to transition validator set to protobuf: %w", err)
    62  	}
    63  
    64  	valSetBz, err := proto.Marshal(pbvs)
    65  	if err != nil {
    66  		return fmt.Errorf("marshalling validator set: %w", err)
    67  	}
    68  
    69  	s.mtx.Lock()
    70  	defer s.mtx.Unlock()
    71  
    72  	b := s.db.NewBatch()
    73  	defer b.Close()
    74  	if err = b.Set(s.shKey(sh.Height), shBz); err != nil {
    75  		return err
    76  	}
    77  	if err = b.Set(s.vsKey(sh.Height), valSetBz); err != nil {
    78  		return err
    79  	}
    80  	if err = b.Set(sizeKey, marshalSize(s.size+1)); err != nil {
    81  		return err
    82  	}
    83  	if err = b.WriteSync(); err != nil {
    84  		return err
    85  	}
    86  	s.size++
    87  
    88  	return nil
    89  }
    90  
    91  // DeleteSignedHeaderAndValidatorSet deletes SignedHeader and ValidatorSet from
    92  // the db.
    93  //
    94  // Safe for concurrent use by multiple goroutines.
    95  func (s *dbs) DeleteSignedHeaderAndValidatorSet(height int64) error {
    96  	if height <= 0 {
    97  		panic("negative or zero height")
    98  	}
    99  
   100  	s.mtx.Lock()
   101  	defer s.mtx.Unlock()
   102  
   103  	b := s.db.NewBatch()
   104  	defer b.Close()
   105  	if err := b.Delete(s.shKey(height)); err != nil {
   106  		return err
   107  	}
   108  	if err := b.Delete(s.vsKey(height)); err != nil {
   109  		return err
   110  	}
   111  	if err := b.Set(sizeKey, marshalSize(s.size-1)); err != nil {
   112  		return err
   113  	}
   114  	if err := b.WriteSync(); err != nil {
   115  		return err
   116  	}
   117  	s.size--
   118  
   119  	return nil
   120  }
   121  
   122  // SignedHeader loads SignedHeader at the given height.
   123  //
   124  // Safe for concurrent use by multiple goroutines.
   125  func (s *dbs) SignedHeader(height int64) (*types.SignedHeader, error) {
   126  	if height <= 0 {
   127  		panic("negative or zero height")
   128  	}
   129  
   130  	bz, err := s.db.Get(s.shKey(height))
   131  	if err != nil {
   132  		panic(err)
   133  	}
   134  	if len(bz) == 0 {
   135  		return nil, store.ErrSignedHeaderNotFound
   136  	}
   137  
   138  	var pbsh tmproto.SignedHeader
   139  	err = proto.Unmarshal(bz, &pbsh)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	signedHeader, err := types.SignedHeaderFromProto(&pbsh)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	return signedHeader, err
   150  }
   151  
   152  // ValidatorSet loads ValidatorSet at the given height.
   153  //
   154  // Safe for concurrent use by multiple goroutines.
   155  func (s *dbs) ValidatorSet(height int64) (*types.ValidatorSet, error) {
   156  	if height <= 0 {
   157  		panic("negative or zero height")
   158  	}
   159  
   160  	bz, err := s.db.Get(s.vsKey(height))
   161  	if err != nil {
   162  		panic(err)
   163  	}
   164  	if len(bz) == 0 {
   165  		return nil, store.ErrValidatorSetNotFound
   166  	}
   167  
   168  	var pbvs tmproto.ValidatorSet
   169  	err = proto.Unmarshal(bz, &pbvs)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	valSet, err := types.ValidatorSetFromProto(&pbvs)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	return valSet, err
   180  }
   181  
   182  // LastSignedHeaderHeight returns the last SignedHeader height stored.
   183  //
   184  // Safe for concurrent use by multiple goroutines.
   185  func (s *dbs) LastSignedHeaderHeight() (int64, error) {
   186  	itr, err := s.db.ReverseIterator(
   187  		s.shKey(1),
   188  		append(s.shKey(1<<63-1), byte(0x00)),
   189  	)
   190  	if err != nil {
   191  		panic(err)
   192  	}
   193  	defer itr.Close()
   194  
   195  	for itr.Valid() {
   196  		key := itr.Key()
   197  		_, height, ok := parseShKey(key)
   198  		if ok {
   199  			return height, nil
   200  		}
   201  		itr.Next()
   202  	}
   203  
   204  	return -1, itr.Error()
   205  }
   206  
   207  // FirstSignedHeaderHeight returns the first SignedHeader height stored.
   208  //
   209  // Safe for concurrent use by multiple goroutines.
   210  func (s *dbs) FirstSignedHeaderHeight() (int64, error) {
   211  	itr, err := s.db.Iterator(
   212  		s.shKey(1),
   213  		append(s.shKey(1<<63-1), byte(0x00)),
   214  	)
   215  	if err != nil {
   216  		panic(err)
   217  	}
   218  	defer itr.Close()
   219  
   220  	for itr.Valid() {
   221  		key := itr.Key()
   222  		_, height, ok := parseShKey(key)
   223  		if ok {
   224  			return height, nil
   225  		}
   226  		itr.Next()
   227  	}
   228  
   229  	return -1, itr.Error()
   230  }
   231  
   232  // SignedHeaderBefore iterates over headers until it finds a header before
   233  // the given height. It returns ErrSignedHeaderNotFound if no such header exists.
   234  //
   235  // Safe for concurrent use by multiple goroutines.
   236  func (s *dbs) SignedHeaderBefore(height int64) (*types.SignedHeader, error) {
   237  	if height <= 0 {
   238  		panic("negative or zero height")
   239  	}
   240  
   241  	itr, err := s.db.ReverseIterator(
   242  		s.shKey(1),
   243  		s.shKey(height),
   244  	)
   245  	if err != nil {
   246  		panic(err)
   247  	}
   248  	defer itr.Close()
   249  
   250  	for itr.Valid() {
   251  		key := itr.Key()
   252  		_, existingHeight, ok := parseShKey(key)
   253  		if ok {
   254  			return s.SignedHeader(existingHeight)
   255  		}
   256  		itr.Next()
   257  	}
   258  	if err = itr.Error(); err != nil {
   259  		return nil, err
   260  	}
   261  
   262  	return nil, store.ErrSignedHeaderNotFound
   263  }
   264  
   265  // Prune prunes header & validator set pairs until there are only size pairs
   266  // left.
   267  //
   268  // Safe for concurrent use by multiple goroutines.
   269  func (s *dbs) Prune(size uint16) error {
   270  	// 1) Check how many we need to prune.
   271  	s.mtx.RLock()
   272  	sSize := s.size
   273  	s.mtx.RUnlock()
   274  
   275  	if sSize <= size { // nothing to prune
   276  		return nil
   277  	}
   278  	numToPrune := sSize - size
   279  
   280  	// 2) Iterate over headers and perform a batch operation.
   281  	itr, err := s.db.Iterator(
   282  		s.shKey(1),
   283  		append(s.shKey(1<<63-1), byte(0x00)),
   284  	)
   285  	if err != nil {
   286  		return err
   287  	}
   288  	defer itr.Close()
   289  
   290  	b := s.db.NewBatch()
   291  	defer b.Close()
   292  
   293  	pruned := 0
   294  	for itr.Valid() && numToPrune > 0 {
   295  		key := itr.Key()
   296  		_, height, ok := parseShKey(key)
   297  		if ok {
   298  			if err = b.Delete(s.shKey(height)); err != nil {
   299  				return err
   300  			}
   301  			if err = b.Delete(s.vsKey(height)); err != nil {
   302  				return err
   303  			}
   304  		}
   305  		itr.Next()
   306  		numToPrune--
   307  		pruned++
   308  	}
   309  	if err = itr.Error(); err != nil {
   310  		return err
   311  	}
   312  
   313  	err = b.WriteSync()
   314  	if err != nil {
   315  		return err
   316  	}
   317  
   318  	// 3) Update size.
   319  	s.mtx.Lock()
   320  	defer s.mtx.Unlock()
   321  
   322  	s.size -= uint16(pruned)
   323  
   324  	if wErr := s.db.SetSync(sizeKey, marshalSize(s.size)); wErr != nil {
   325  		return fmt.Errorf("failed to persist size: %w", wErr)
   326  	}
   327  
   328  	return nil
   329  }
   330  
   331  // Size returns the number of header & validator set pairs.
   332  //
   333  // Safe for concurrent use by multiple goroutines.
   334  func (s *dbs) Size() uint16 {
   335  	s.mtx.RLock()
   336  	defer s.mtx.RUnlock()
   337  	return s.size
   338  }
   339  
   340  func (s *dbs) shKey(height int64) []byte {
   341  	return []byte(fmt.Sprintf("sh/%s/%020d", s.prefix, height))
   342  }
   343  
   344  func (s *dbs) vsKey(height int64) []byte {
   345  	return []byte(fmt.Sprintf("vs/%s/%020d", s.prefix, height))
   346  }
   347  
   348  var keyPattern = regexp.MustCompile(`^(sh|vs)/([^/]*)/([0-9]+)$`)
   349  
   350  func parseKey(key []byte) (part string, prefix string, height int64, ok bool) {
   351  	submatch := keyPattern.FindSubmatch(key)
   352  	if submatch == nil {
   353  		return "", "", 0, false
   354  	}
   355  	part = string(submatch[1])
   356  	prefix = string(submatch[2])
   357  	height, err := strconv.ParseInt(string(submatch[3]), 10, 64)
   358  	if err != nil {
   359  		return "", "", 0, false
   360  	}
   361  	ok = true // good!
   362  	return
   363  }
   364  
   365  func parseShKey(key []byte) (prefix string, height int64, ok bool) {
   366  	var part string
   367  	part, prefix, height, ok = parseKey(key)
   368  	if part != "sh" {
   369  		return "", 0, false
   370  	}
   371  	return
   372  }
   373  
   374  func marshalSize(size uint16) []byte {
   375  	bs := make([]byte, 2)
   376  	binary.LittleEndian.PutUint16(bs, size)
   377  	return bs
   378  }
   379  
   380  func unmarshalSize(bz []byte) uint16 {
   381  	return binary.LittleEndian.Uint16(bz)
   382  }