github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/operations/slashings/service.go (about)

     1  package slashings
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sort"
     7  
     8  	"github.com/pkg/errors"
     9  	types "github.com/prysmaticlabs/eth2-types"
    10  	"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
    11  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    12  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
    13  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    14  	"github.com/prysmaticlabs/prysm/shared/params"
    15  	"github.com/prysmaticlabs/prysm/shared/sliceutil"
    16  	"github.com/trailofbits/go-mutexasserts"
    17  	"go.opencensus.io/trace"
    18  )
    19  
    20  // NewPool returns an initialized attester slashing and proposer slashing pool.
    21  func NewPool() *Pool {
    22  	return &Pool{
    23  		pendingProposerSlashing: make([]*ethpb.ProposerSlashing, 0),
    24  		pendingAttesterSlashing: make([]*PendingAttesterSlashing, 0),
    25  		included:                make(map[types.ValidatorIndex]bool),
    26  	}
    27  }
    28  
    29  // PendingAttesterSlashings returns attester slashings that are able to be included into a block.
    30  // This method will return the amount of pending attester slashings for a block transition unless parameter `noLimit` is true
    31  // to indicate the request is for noLimit pending items.
    32  func (p *Pool) PendingAttesterSlashings(ctx context.Context, state iface.ReadOnlyBeaconState, noLimit bool) []*ethpb.AttesterSlashing {
    33  	p.lock.Lock()
    34  	defer p.lock.Unlock()
    35  	ctx, span := trace.StartSpan(ctx, "operations.PendingAttesterSlashing")
    36  	defer span.End()
    37  
    38  	// Update prom metric.
    39  	numPendingAttesterSlashings.Set(float64(len(p.pendingAttesterSlashing)))
    40  
    41  	included := make(map[types.ValidatorIndex]bool)
    42  
    43  	// Allocate pending slice with a capacity of maxAttesterSlashings or len(p.pendingAttesterSlashing)) depending on the request.
    44  	maxSlashings := params.BeaconConfig().MaxAttesterSlashings
    45  	if noLimit {
    46  		maxSlashings = uint64(len(p.pendingAttesterSlashing))
    47  	}
    48  	pending := make([]*ethpb.AttesterSlashing, 0, maxSlashings)
    49  	for i := 0; i < len(p.pendingAttesterSlashing); i++ {
    50  		if uint64(len(pending)) >= maxSlashings {
    51  			break
    52  		}
    53  		slashing := p.pendingAttesterSlashing[i]
    54  		valid, err := p.validatorSlashingPreconditionCheck(state, slashing.validatorToSlash)
    55  		if err != nil {
    56  			log.WithError(err).Error("could not validate attester slashing")
    57  			continue
    58  		}
    59  		if included[slashing.validatorToSlash] || !valid {
    60  			p.pendingAttesterSlashing = append(p.pendingAttesterSlashing[:i], p.pendingAttesterSlashing[i+1:]...)
    61  			i--
    62  			continue
    63  		}
    64  		attSlashing := slashing.attesterSlashing
    65  		slashedVal := sliceutil.IntersectionUint64(attSlashing.Attestation_1.AttestingIndices, attSlashing.Attestation_2.AttestingIndices)
    66  		for _, idx := range slashedVal {
    67  			included[types.ValidatorIndex(idx)] = true
    68  		}
    69  
    70  		pending = append(pending, attSlashing)
    71  	}
    72  
    73  	return pending
    74  }
    75  
    76  // PendingProposerSlashings returns proposer slashings that are able to be included into a block.
    77  // This method will return the amount of pending proposer slashings for a block transition unless the `noLimit` parameter
    78  // is set to true to indicate the request is for noLimit pending items.
    79  func (p *Pool) PendingProposerSlashings(ctx context.Context, state iface.ReadOnlyBeaconState, noLimit bool) []*ethpb.ProposerSlashing {
    80  	p.lock.Lock()
    81  	defer p.lock.Unlock()
    82  	ctx, span := trace.StartSpan(ctx, "operations.PendingProposerSlashing")
    83  	defer span.End()
    84  
    85  	// Update prom metric.
    86  	numPendingProposerSlashings.Set(float64(len(p.pendingProposerSlashing)))
    87  
    88  	// Allocate pending slice with a capacity of len(p.pendingProposerSlashing) or maxProposerSlashings depending on the request.
    89  	maxSlashings := params.BeaconConfig().MaxProposerSlashings
    90  	if noLimit {
    91  		maxSlashings = uint64(len(p.pendingProposerSlashing))
    92  	}
    93  	pending := make([]*ethpb.ProposerSlashing, 0, maxSlashings)
    94  	for i := 0; i < len(p.pendingProposerSlashing); i++ {
    95  		if uint64(len(pending)) >= maxSlashings {
    96  			break
    97  		}
    98  		slashing := p.pendingProposerSlashing[i]
    99  		valid, err := p.validatorSlashingPreconditionCheck(state, slashing.Header_1.Header.ProposerIndex)
   100  		if err != nil {
   101  			log.WithError(err).Error("could not validate proposer slashing")
   102  			continue
   103  		}
   104  		if !valid {
   105  			p.pendingProposerSlashing = append(p.pendingProposerSlashing[:i], p.pendingProposerSlashing[i+1:]...)
   106  			i--
   107  			continue
   108  		}
   109  
   110  		pending = append(pending, slashing)
   111  	}
   112  	return pending
   113  }
   114  
   115  // InsertAttesterSlashing into the pool. This method is a no-op if the attester slashing already exists in the pool,
   116  // has been included into a block recently, or the validator is already exited.
   117  func (p *Pool) InsertAttesterSlashing(
   118  	ctx context.Context,
   119  	state iface.ReadOnlyBeaconState,
   120  	slashing *ethpb.AttesterSlashing,
   121  ) error {
   122  	p.lock.Lock()
   123  	defer p.lock.Unlock()
   124  	ctx, span := trace.StartSpan(ctx, "operations.InsertAttesterSlashing")
   125  	defer span.End()
   126  
   127  	if err := blocks.VerifyAttesterSlashing(ctx, state, slashing); err != nil {
   128  		return errors.Wrap(err, "could not verify attester slashing")
   129  	}
   130  
   131  	slashedVal := sliceutil.IntersectionUint64(slashing.Attestation_1.AttestingIndices, slashing.Attestation_2.AttestingIndices)
   132  	cantSlash := make([]uint64, 0, len(slashedVal))
   133  	for _, val := range slashedVal {
   134  		// Has this validator index been included recently?
   135  		ok, err := p.validatorSlashingPreconditionCheck(state, types.ValidatorIndex(val))
   136  		if err != nil {
   137  			return err
   138  		}
   139  		// If the validator has already exited, has already been slashed, or if its index
   140  		// has been recently included in the pool of slashings, skip including this indice.
   141  		if !ok {
   142  			cantSlash = append(cantSlash, val)
   143  			continue
   144  		}
   145  
   146  		// Check if the validator already exists in the list of slashings.
   147  		// Use binary search to find the answer.
   148  		found := sort.Search(len(p.pendingAttesterSlashing), func(i int) bool {
   149  			return uint64(p.pendingAttesterSlashing[i].validatorToSlash) >= val
   150  		})
   151  		if found != len(p.pendingAttesterSlashing) && uint64(p.pendingAttesterSlashing[found].validatorToSlash) == val {
   152  			cantSlash = append(cantSlash, val)
   153  			continue
   154  		}
   155  
   156  		pendingSlashing := &PendingAttesterSlashing{
   157  			attesterSlashing: slashing,
   158  			validatorToSlash: types.ValidatorIndex(val),
   159  		}
   160  		// Insert into pending list and sort again.
   161  		p.pendingAttesterSlashing = append(p.pendingAttesterSlashing, pendingSlashing)
   162  		sort.Slice(p.pendingAttesterSlashing, func(i, j int) bool {
   163  			return p.pendingAttesterSlashing[i].validatorToSlash < p.pendingAttesterSlashing[j].validatorToSlash
   164  		})
   165  		numPendingAttesterSlashings.Set(float64(len(p.pendingAttesterSlashing)))
   166  	}
   167  	if len(cantSlash) == len(slashedVal) {
   168  		return fmt.Errorf("could not slash any of %d validators in submitted slashing", len(slashedVal))
   169  	}
   170  	return nil
   171  }
   172  
   173  // InsertProposerSlashing into the pool. This method is a no-op if the pending slashing already exists,
   174  // has been included recently, the validator is already exited, or the validator was already slashed.
   175  func (p *Pool) InsertProposerSlashing(
   176  	ctx context.Context,
   177  	state iface.BeaconState,
   178  	slashing *ethpb.ProposerSlashing,
   179  ) error {
   180  	p.lock.Lock()
   181  	defer p.lock.Unlock()
   182  	ctx, span := trace.StartSpan(ctx, "operations.InsertProposerSlashing")
   183  	defer span.End()
   184  
   185  	if err := blocks.VerifyProposerSlashing(state, slashing); err != nil {
   186  		return errors.Wrap(err, "could not verify proposer slashing")
   187  	}
   188  
   189  	idx := slashing.Header_1.Header.ProposerIndex
   190  	ok, err := p.validatorSlashingPreconditionCheck(state, idx)
   191  	if err != nil {
   192  		return err
   193  	}
   194  	// If the validator has already exited, has already been slashed, or if its index
   195  	// has been recently included in the pool of slashings, do not process this new
   196  	// slashing.
   197  	if !ok {
   198  		return fmt.Errorf("validator at index %d cannot be slashed", idx)
   199  	}
   200  
   201  	// Check if the validator already exists in the list of slashings.
   202  	// Use binary search to find the answer.
   203  	found := sort.Search(len(p.pendingProposerSlashing), func(i int) bool {
   204  		return p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex >= slashing.Header_1.Header.ProposerIndex
   205  	})
   206  	if found != len(p.pendingProposerSlashing) && p.pendingProposerSlashing[found].Header_1.Header.ProposerIndex ==
   207  		slashing.Header_1.Header.ProposerIndex {
   208  		return errors.New("slashing object already exists in pending proposer slashings")
   209  	}
   210  
   211  	// Insert into pending list and sort again.
   212  	p.pendingProposerSlashing = append(p.pendingProposerSlashing, slashing)
   213  	sort.Slice(p.pendingProposerSlashing, func(i, j int) bool {
   214  		return p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex < p.pendingProposerSlashing[j].Header_1.Header.ProposerIndex
   215  	})
   216  	numPendingProposerSlashings.Set(float64(len(p.pendingProposerSlashing)))
   217  
   218  	return nil
   219  }
   220  
   221  // MarkIncludedAttesterSlashing is used when an attester slashing has been included in a beacon block.
   222  // Every block seen by this node that contains proposer slashings should call this method to include
   223  // the proposer slashings.
   224  func (p *Pool) MarkIncludedAttesterSlashing(as *ethpb.AttesterSlashing) {
   225  	p.lock.Lock()
   226  	defer p.lock.Unlock()
   227  	slashedVal := sliceutil.IntersectionUint64(as.Attestation_1.AttestingIndices, as.Attestation_2.AttestingIndices)
   228  	for _, val := range slashedVal {
   229  		i := sort.Search(len(p.pendingAttesterSlashing), func(i int) bool {
   230  			return uint64(p.pendingAttesterSlashing[i].validatorToSlash) >= val
   231  		})
   232  		if i != len(p.pendingAttesterSlashing) && uint64(p.pendingAttesterSlashing[i].validatorToSlash) == val {
   233  			p.pendingAttesterSlashing = append(p.pendingAttesterSlashing[:i], p.pendingAttesterSlashing[i+1:]...)
   234  		}
   235  		p.included[types.ValidatorIndex(val)] = true
   236  		numAttesterSlashingsIncluded.Inc()
   237  	}
   238  }
   239  
   240  // MarkIncludedProposerSlashing is used when an proposer slashing has been included in a beacon block.
   241  // Every block seen by this node that contains proposer slashings should call this method to include
   242  // the proposer slashings.
   243  func (p *Pool) MarkIncludedProposerSlashing(ps *ethpb.ProposerSlashing) {
   244  	p.lock.Lock()
   245  	defer p.lock.Unlock()
   246  	i := sort.Search(len(p.pendingProposerSlashing), func(i int) bool {
   247  		return p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex >= ps.Header_1.Header.ProposerIndex
   248  	})
   249  	if i != len(p.pendingProposerSlashing) && p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex == ps.Header_1.Header.ProposerIndex {
   250  		p.pendingProposerSlashing = append(p.pendingProposerSlashing[:i], p.pendingProposerSlashing[i+1:]...)
   251  	}
   252  	p.included[ps.Header_1.Header.ProposerIndex] = true
   253  	numProposerSlashingsIncluded.Inc()
   254  }
   255  
   256  // this function checks a few items about a validator before proceeding with inserting
   257  // a proposer/attester slashing into the pool. First, it checks if the validator
   258  // has been recently included in the pool, then it checks if the validator is slashable.
   259  // Note: this method requires caller to hold the lock.
   260  func (p *Pool) validatorSlashingPreconditionCheck(
   261  	state iface.ReadOnlyBeaconState,
   262  	valIdx types.ValidatorIndex,
   263  ) (bool, error) {
   264  	if !mutexasserts.RWMutexLocked(&p.lock) && !mutexasserts.RWMutexRLocked(&p.lock) {
   265  		return false, errors.New("pool.validatorSlashingPreconditionCheck: caller must hold read/write lock")
   266  	}
   267  
   268  	// Check if the validator index has been included recently.
   269  	if p.included[valIdx] {
   270  		return false, nil
   271  	}
   272  	validator, err := state.ValidatorAtIndexReadOnly(valIdx)
   273  	if err != nil {
   274  		return false, err
   275  	}
   276  	// Checking if the validator is slashable.
   277  	if !helpers.IsSlashableValidatorUsingTrie(validator, helpers.CurrentEpoch(state)) {
   278  		return false, nil
   279  	}
   280  	return true, nil
   281  }