github.com/prysmaticlabs/prysm@v1.4.4/endtoend/evaluators/slashing.go (about) 1 package evaluators 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/pkg/errors" 8 types "github.com/prysmaticlabs/eth2-types" 9 "github.com/prysmaticlabs/go-bitfield" 10 corehelpers "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 11 "github.com/prysmaticlabs/prysm/endtoend/policies" 12 e2eTypes "github.com/prysmaticlabs/prysm/endtoend/types" 13 eth "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 14 "github.com/prysmaticlabs/prysm/shared/bytesutil" 15 "github.com/prysmaticlabs/prysm/shared/params" 16 "github.com/prysmaticlabs/prysm/shared/sliceutil" 17 "github.com/prysmaticlabs/prysm/shared/testutil" 18 "google.golang.org/grpc" 19 "google.golang.org/protobuf/types/known/emptypb" 20 ) 21 22 // InjectDoubleVote broadcasts a double vote into the beacon node pool for the slasher to detect. 23 var InjectDoubleVote = e2eTypes.Evaluator{ 24 Name: "inject_double_vote_%d", 25 Policy: policies.OnEpoch(1), 26 Evaluation: insertDoubleAttestationIntoPool, 27 } 28 29 // ProposeDoubleBlock broadcasts a double block to the beacon node for the slasher to detect. 30 var ProposeDoubleBlock = e2eTypes.Evaluator{ 31 Name: "propose_double_block_%d", 32 Policy: policies.OnEpoch(1), 33 Evaluation: proposeDoubleBlock, 34 } 35 36 // ValidatorsSlashed ensures the expected amount of validators are slashed. 37 var ValidatorsSlashed = e2eTypes.Evaluator{ 38 Name: "validators_slashed_epoch_%d", 39 Policy: policies.AfterNthEpoch(1), 40 Evaluation: validatorsSlashed, 41 } 42 43 // SlashedValidatorsLoseBalance checks if the validators slashed lose the right balance. 44 var SlashedValidatorsLoseBalance = e2eTypes.Evaluator{ 45 Name: "slashed_validators_lose_valance_epoch_%d", 46 Policy: policies.AfterNthEpoch(1), 47 Evaluation: validatorsLoseBalance, 48 } 49 50 var slashedIndices []uint64 51 52 func validatorsSlashed(conns ...*grpc.ClientConn) error { 53 conn := conns[0] 54 ctx := context.Background() 55 client := eth.NewBeaconChainClient(conn) 56 req := ð.GetValidatorActiveSetChangesRequest{} 57 changes, err := client.GetValidatorActiveSetChanges(ctx, req) 58 if err != nil { 59 return err 60 } 61 if len(changes.SlashedIndices) != len(slashedIndices) { 62 return fmt.Errorf("expected %d indices to be slashed, received %d", len(slashedIndices), len(changes.SlashedIndices)) 63 } 64 return nil 65 } 66 67 func validatorsLoseBalance(conns ...*grpc.ClientConn) error { 68 conn := conns[0] 69 ctx := context.Background() 70 client := eth.NewBeaconChainClient(conn) 71 72 for i, slashedIndex := range slashedIndices { 73 req := ð.GetValidatorRequest{ 74 QueryFilter: ð.GetValidatorRequest_Index{ 75 Index: types.ValidatorIndex(slashedIndex), 76 }, 77 } 78 valResp, err := client.GetValidator(ctx, req) 79 if err != nil { 80 return err 81 } 82 83 slashedPenalty := params.BeaconConfig().MaxEffectiveBalance / params.BeaconConfig().MinSlashingPenaltyQuotient 84 slashedBal := params.BeaconConfig().MaxEffectiveBalance - slashedPenalty + params.BeaconConfig().EffectiveBalanceIncrement/10 85 if valResp.EffectiveBalance >= slashedBal { 86 return fmt.Errorf( 87 "expected slashed validator %d to balance less than %d, received %d", 88 i, 89 slashedBal, 90 valResp.EffectiveBalance, 91 ) 92 } 93 94 } 95 return nil 96 } 97 98 func insertDoubleAttestationIntoPool(conns ...*grpc.ClientConn) error { 99 conn := conns[0] 100 valClient := eth.NewBeaconNodeValidatorClient(conn) 101 beaconClient := eth.NewBeaconChainClient(conn) 102 103 ctx := context.Background() 104 chainHead, err := beaconClient.GetChainHead(ctx, &emptypb.Empty{}) 105 if err != nil { 106 return errors.Wrap(err, "could not get chain head") 107 } 108 109 _, privKeys, err := testutil.DeterministicDepositsAndKeys(params.BeaconConfig().MinGenesisActiveValidatorCount) 110 if err != nil { 111 return err 112 } 113 pubKeys := make([][]byte, len(privKeys)) 114 for i, priv := range privKeys { 115 pubKeys[i] = priv.PublicKey().Marshal() 116 } 117 duties, err := valClient.GetDuties(ctx, ð.DutiesRequest{ 118 Epoch: chainHead.HeadEpoch, 119 PublicKeys: pubKeys, 120 }) 121 if err != nil { 122 return errors.Wrap(err, "could not get duties") 123 } 124 125 var committeeIndex types.CommitteeIndex 126 var committee []types.ValidatorIndex 127 for _, duty := range duties.Duties { 128 if duty.AttesterSlot == chainHead.HeadSlot-1 { 129 committeeIndex = duty.CommitteeIndex 130 committee = duty.Committee 131 break 132 } 133 } 134 135 attDataReq := ð.AttestationDataRequest{ 136 CommitteeIndex: committeeIndex, 137 Slot: chainHead.HeadSlot - 1, 138 } 139 140 attData, err := valClient.GetAttestationData(ctx, attDataReq) 141 if err != nil { 142 return err 143 } 144 blockRoot := bytesutil.ToBytes32([]byte("muahahahaha I'm an evil validator")) 145 attData.BeaconBlockRoot = blockRoot[:] 146 147 req := ð.DomainRequest{ 148 Epoch: chainHead.HeadEpoch, 149 Domain: params.BeaconConfig().DomainBeaconAttester[:], 150 } 151 resp, err := valClient.DomainData(ctx, req) 152 if err != nil { 153 return errors.Wrap(err, "could not get domain data") 154 } 155 signingRoot, err := corehelpers.ComputeSigningRoot(attData, resp.SignatureDomain) 156 if err != nil { 157 return errors.Wrap(err, "could not compute signing root") 158 } 159 160 valsToSlash := uint64(2) 161 for i := uint64(0); i < valsToSlash && i < uint64(len(committee)); i++ { 162 if len(sliceutil.IntersectionUint64(slashedIndices, []uint64{uint64(committee[i])})) > 0 { 163 valsToSlash++ 164 continue 165 } 166 // Set the bits of half the committee to be slashed. 167 attBitfield := bitfield.NewBitlist(uint64(len(committee))) 168 attBitfield.SetBitAt(i, true) 169 170 att := ð.Attestation{ 171 AggregationBits: attBitfield, 172 Data: attData, 173 Signature: privKeys[committee[i]].Sign(signingRoot[:]).Marshal(), 174 } 175 // We only broadcast to conns[0] here since we can trust that at least 1 node will be online. 176 // Only broadcasting the attestation to one node also helps test slashing propagation. 177 client := eth.NewBeaconNodeValidatorClient(conns[0]) 178 if _, err = client.ProposeAttestation(ctx, att); err != nil { 179 return errors.Wrap(err, "could not propose attestation") 180 } 181 slashedIndices = append(slashedIndices, uint64(committee[i])) 182 } 183 return nil 184 } 185 186 func proposeDoubleBlock(conns ...*grpc.ClientConn) error { 187 conn := conns[0] 188 valClient := eth.NewBeaconNodeValidatorClient(conn) 189 beaconClient := eth.NewBeaconChainClient(conn) 190 191 ctx := context.Background() 192 chainHead, err := beaconClient.GetChainHead(ctx, &emptypb.Empty{}) 193 if err != nil { 194 return errors.Wrap(err, "could not get chain head") 195 } 196 197 _, privKeys, err := testutil.DeterministicDepositsAndKeys(params.BeaconConfig().MinGenesisActiveValidatorCount) 198 if err != nil { 199 return err 200 } 201 pubKeys := make([][]byte, len(privKeys)) 202 for i, priv := range privKeys { 203 pubKeys[i] = priv.PublicKey().Marshal() 204 } 205 duties, err := valClient.GetDuties(ctx, ð.DutiesRequest{ 206 Epoch: chainHead.HeadEpoch, 207 PublicKeys: pubKeys, 208 }) 209 if err != nil { 210 return errors.Wrap(err, "could not get duties") 211 } 212 213 var proposerIndex types.ValidatorIndex 214 for i, duty := range duties.CurrentEpochDuties { 215 if sliceutil.IsInSlots(chainHead.HeadSlot-1, duty.ProposerSlots) { 216 proposerIndex = types.ValidatorIndex(i) 217 break 218 } 219 } 220 221 hashLen := 32 222 blk := ð.BeaconBlock{ 223 Slot: chainHead.HeadSlot - 1, 224 ParentRoot: bytesutil.PadTo([]byte("bad parent root"), hashLen), 225 StateRoot: bytesutil.PadTo([]byte("bad state root"), hashLen), 226 ProposerIndex: proposerIndex, 227 Body: ð.BeaconBlockBody{ 228 Eth1Data: ð.Eth1Data{ 229 BlockHash: bytesutil.PadTo([]byte("bad block hash"), hashLen), 230 DepositRoot: bytesutil.PadTo([]byte("bad deposit root"), hashLen), 231 DepositCount: 1, 232 }, 233 RandaoReveal: bytesutil.PadTo([]byte("bad randao"), params.BeaconConfig().BLSSignatureLength), 234 Graffiti: bytesutil.PadTo([]byte("teehee"), hashLen), 235 ProposerSlashings: []*eth.ProposerSlashing{}, 236 AttesterSlashings: []*eth.AttesterSlashing{}, 237 Attestations: []*eth.Attestation{}, 238 Deposits: []*eth.Deposit{}, 239 VoluntaryExits: []*eth.SignedVoluntaryExit{}, 240 }, 241 } 242 243 req := ð.DomainRequest{ 244 Epoch: chainHead.HeadEpoch, 245 Domain: params.BeaconConfig().DomainBeaconProposer[:], 246 } 247 resp, err := valClient.DomainData(ctx, req) 248 if err != nil { 249 return errors.Wrap(err, "could not get domain data") 250 } 251 signingRoot, err := corehelpers.ComputeSigningRoot(blk, resp.SignatureDomain) 252 if err != nil { 253 return errors.Wrap(err, "could not compute signing root") 254 } 255 sig := privKeys[proposerIndex].Sign(signingRoot[:]).Marshal() 256 signedBlk := ð.SignedBeaconBlock{ 257 Block: blk, 258 Signature: sig, 259 } 260 261 // We only broadcast to conns[0] here since we can trust that at least 1 node will be online. 262 // Only broadcasting the attestation to one node also helps test slashing propagation. 263 client := eth.NewBeaconNodeValidatorClient(conns[0]) 264 if _, err = client.ProposeBlock(ctx, signedBlk); err == nil { 265 return errors.New("expected block to fail processing") 266 } 267 slashedIndices = append(slashedIndices, uint64(proposerIndex)) 268 return nil 269 }