github.com/prysmaticlabs/prysm@v1.4.4/slasher/db/kv/highest_attestation.go (about)

     1  package kv
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  
     7  	"github.com/pkg/errors"
     8  	slashpb "github.com/prysmaticlabs/prysm/proto/slashing"
     9  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    10  	bolt "go.etcd.io/bbolt"
    11  	"go.opencensus.io/trace"
    12  )
    13  
    14  func persistHighestAttestationCacheOnEviction(db *Store) func(key interface{}, value interface{}) {
    15  	// We use a closure here so we can access the database itself
    16  	// on the eviction of a span map from the cache. The function has the signature
    17  	// required by the ristretto cache OnEvict method.
    18  	// See https://godoc.org/github.com/dgraph-io/ristretto#Config.
    19  	return func(key interface{}, value interface{}) {
    20  		log.Tracef("Evicting highest attestation for validator: %d", key.(uint64))
    21  		err := db.update(func(tx *bolt.Tx) error {
    22  			enc, err := json.Marshal(value.(map[uint64]*slashpb.HighestAttestation))
    23  			if err != nil {
    24  				return errors.Wrap(err, "failed to marshal")
    25  			}
    26  			dbKey := highestAttSetkeyBytes(key.(uint64))
    27  			bucket := tx.Bucket(highestAttestationBucket)
    28  			if err := bucket.Put(dbKey, enc); err != nil {
    29  				return errors.Wrap(err, "failed to add highest attestation to slasher db.")
    30  			}
    31  			return nil
    32  		})
    33  		if err != nil {
    34  			log.Errorf("Failed to save highest attestation to db on cache eviction: %v", err)
    35  		}
    36  	}
    37  }
    38  
    39  // EnableHighestAttestationCache used to enable or disable highest attestation cache in tests.
    40  func (s *Store) EnableHighestAttestationCache(enable bool) {
    41  	s.highestAttCacheEnabled = enable
    42  }
    43  
    44  // HighestAttestation returns the highest calculated attestation for a validatorID
    45  func (s *Store) HighestAttestation(ctx context.Context, validatorID uint64) (*slashpb.HighestAttestation, error) {
    46  	ctx, span := trace.StartSpan(ctx, "SlasherDB.HighestAttestation")
    47  	defer span.End()
    48  
    49  	if s.highestAttCacheEnabled {
    50  		h, ok := s.highestAttestationCache.Get(highestAttSetkey(validatorID))
    51  		if ok && h[validatorID] != nil {
    52  			return h[validatorID], nil
    53  		}
    54  	}
    55  
    56  	key := highestAttSetkeyBytes(validatorID)
    57  	var highestAtt *slashpb.HighestAttestation
    58  	err := s.view(func(tx *bolt.Tx) error {
    59  		b := tx.Bucket(highestAttestationBucket)
    60  		if enc := b.Get(key); enc != nil {
    61  			set := map[uint64]*slashpb.HighestAttestation{}
    62  			err := json.Unmarshal(enc, &set)
    63  			if err != nil {
    64  				return err
    65  			}
    66  			highestAtt = set[validatorID]
    67  		}
    68  		return nil
    69  	})
    70  
    71  	return highestAtt, err
    72  }
    73  
    74  // SaveHighestAttestation saves highest attestation for a validatorID.
    75  func (s *Store) SaveHighestAttestation(ctx context.Context, highest *slashpb.HighestAttestation) error {
    76  	ctx, span := trace.StartSpan(ctx, "SlasherDB.SaveHighestAttestation")
    77  	defer span.End()
    78  
    79  	if s.highestAttCacheEnabled {
    80  		s.highestAttestationCache.Set(highestAttSetkey(highest.ValidatorId), highest)
    81  		return nil
    82  	}
    83  
    84  	key := highestAttSetkeyBytes(highest.ValidatorId)
    85  	set := map[uint64]*slashpb.HighestAttestation{}
    86  	err := s.view(func(tx *bolt.Tx) error {
    87  		b := tx.Bucket(highestAttestationBucket)
    88  		if enc := b.Get(key); enc != nil {
    89  			err := json.Unmarshal(enc, &set)
    90  			if err != nil {
    91  				return err
    92  			}
    93  		}
    94  		return nil
    95  	})
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	set[highest.ValidatorId] = highest
   101  	enc, err := json.Marshal(set)
   102  	if err != nil {
   103  		return errors.Wrap(err, "failed to marshal")
   104  	}
   105  	err = s.update(func(tx *bolt.Tx) error {
   106  		bucket := tx.Bucket(highestAttestationBucket)
   107  		if err := bucket.Put(key, enc); err != nil {
   108  			return errors.Wrap(err, "failed to add highest attestation to slasher s.")
   109  		}
   110  		return nil
   111  	})
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	return nil
   117  }
   118  
   119  func highestAttSetkeyBytes(validatorID uint64) []byte {
   120  	return bytesutil.Uint64ToBytesBigEndian(highestAttSetkey(validatorID))
   121  }
   122  
   123  // divide validators by id into 1k-ish buckets (0-1000,1001-1999, etc).
   124  func highestAttSetkey(validatorID uint64) uint64 {
   125  	return validatorID / 1000
   126  }