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 }