github.com/prysmaticlabs/prysm@v1.4.4/slasher/rpc/server_test.go (about) 1 package rpc 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "testing" 8 9 "github.com/golang/mock/gomock" 10 types "github.com/prysmaticlabs/eth2-types" 11 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 12 p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types" 13 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 14 "github.com/prysmaticlabs/prysm/shared/bls" 15 "github.com/prysmaticlabs/prysm/shared/bytesutil" 16 "github.com/prysmaticlabs/prysm/shared/copyutil" 17 "github.com/prysmaticlabs/prysm/shared/mock" 18 "github.com/prysmaticlabs/prysm/shared/p2putils" 19 "github.com/prysmaticlabs/prysm/shared/params" 20 "github.com/prysmaticlabs/prysm/shared/testutil" 21 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 22 "github.com/prysmaticlabs/prysm/shared/testutil/require" 23 "github.com/prysmaticlabs/prysm/slasher/beaconclient" 24 testDB "github.com/prysmaticlabs/prysm/slasher/db/testing" 25 "github.com/prysmaticlabs/prysm/slasher/detection" 26 ) 27 28 func TestServer_IsSlashableAttestation(t *testing.T) { 29 db := testDB.SetupSlasherDB(t, false) 30 ctrl := gomock.NewController(t) 31 defer ctrl.Finish() 32 bClient := mock.NewMockBeaconChainClient(ctrl) 33 nClient := mock.NewMockNodeClient(ctrl) 34 ctx := context.Background() 35 36 _, keys, err := testutil.DeterministicDepositsAndKeys(4) 37 require.NoError(t, err) 38 wantedValidators1 := ðpb.Validators{ 39 ValidatorList: []*ethpb.Validators_ValidatorContainer{ 40 { 41 Index: 3, Validator: ðpb.Validator{PublicKey: keys[3].PublicKey().Marshal()}, 42 }, 43 }, 44 } 45 46 wantedGenesis := ðpb.Genesis{ 47 GenesisValidatorsRoot: bytesutil.PadTo([]byte("I am genesis"), 32), 48 } 49 50 savedAttestation := ðpb.IndexedAttestation{ 51 AttestingIndices: []uint64{3}, 52 Data: ðpb.AttestationData{ 53 Source: ðpb.Checkpoint{Epoch: 3, Root: make([]byte, 32)}, 54 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 55 BeaconBlockRoot: make([]byte, 32), 56 }, 57 Signature: make([]byte, 96), 58 } 59 60 cfg := &detection.Config{ 61 SlasherDB: db, 62 } 63 fork, err := p2putils.Fork(savedAttestation.Data.Target.Epoch) 64 require.NoError(t, err) 65 66 bcCfg := &beaconclient.Config{BeaconClient: bClient, NodeClient: nClient, SlasherDB: db} 67 bs, err := beaconclient.NewService(ctx, bcCfg) 68 require.NoError(t, err) 69 ds := detection.NewService(ctx, cfg) 70 server := Server{ctx: ctx, detector: ds, slasherDB: db, beaconClient: bs} 71 nClient.EXPECT().GetGenesis(gomock.Any(), gomock.Any()).Return(wantedGenesis, nil).AnyTimes() 72 bClient.EXPECT().ListValidators( 73 gomock.Any(), 74 gomock.Any(), 75 ).Return(wantedValidators1, nil).AnyTimes() 76 domain, err := helpers.Domain(fork, savedAttestation.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester, wantedGenesis.GenesisValidatorsRoot) 77 require.NoError(t, err) 78 wg := sync.WaitGroup{} 79 wg.Add(100) 80 var wentThrough bool 81 for i := types.Slot(0); i < 100; i++ { 82 go func(j types.Slot) { 83 defer wg.Done() 84 iatt := copyutil.CopyIndexedAttestation(savedAttestation) 85 iatt.Data.Slot += j 86 root, err := helpers.ComputeSigningRoot(iatt.Data, domain) 87 require.NoError(t, err) 88 validatorSig := keys[iatt.AttestingIndices[0]].Sign(root[:]) 89 marshalledSig := validatorSig.Marshal() 90 iatt.Signature = marshalledSig 91 slashings, err := server.IsSlashableAttestation(ctx, iatt) 92 require.NoError(t, err, "Got error while trying to detect slashing") 93 94 if len(slashings.AttesterSlashing) == 0 && !wentThrough { 95 wentThrough = true 96 } else if len(slashings.AttesterSlashing) == 0 && wentThrough { 97 t.Fatalf("Only one attestation should go through without slashing: %v", iatt) 98 } 99 }(i) 100 } 101 wg.Wait() 102 103 } 104 105 func TestServer_IsSlashableAttestationNoUpdate(t *testing.T) { 106 db := testDB.SetupSlasherDB(t, false) 107 ctrl := gomock.NewController(t) 108 defer ctrl.Finish() 109 bClient := mock.NewMockBeaconChainClient(ctrl) 110 nClient := mock.NewMockNodeClient(ctrl) 111 ctx := context.Background() 112 113 _, keys, err := testutil.DeterministicDepositsAndKeys(4) 114 require.NoError(t, err) 115 wantedValidators1 := ðpb.Validators{ 116 ValidatorList: []*ethpb.Validators_ValidatorContainer{ 117 { 118 Index: 3, Validator: ðpb.Validator{PublicKey: keys[3].PublicKey().Marshal()}, 119 }, 120 }, 121 } 122 bClient.EXPECT().ListValidators( 123 gomock.Any(), 124 gomock.Any(), 125 ).Return(wantedValidators1, nil) 126 127 wantedGenesis := ðpb.Genesis{ 128 GenesisValidatorsRoot: bytesutil.PadTo([]byte("I am genesis"), 32), 129 } 130 nClient.EXPECT().GetGenesis(gomock.Any(), gomock.Any()).Return(wantedGenesis, nil) 131 savedAttestation := ðpb.IndexedAttestation{ 132 AttestingIndices: []uint64{3}, 133 Data: ðpb.AttestationData{ 134 Source: ðpb.Checkpoint{Epoch: 3, Root: make([]byte, 32)}, 135 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 136 BeaconBlockRoot: make([]byte, 32), 137 }, 138 Signature: make([]byte, 96), 139 } 140 incomingAtt := ðpb.IndexedAttestation{ 141 AttestingIndices: []uint64{1, 3}, 142 Data: ðpb.AttestationData{ 143 Source: ðpb.Checkpoint{Epoch: 2, Root: make([]byte, 32)}, 144 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 145 BeaconBlockRoot: make([]byte, 32), 146 }, 147 Signature: make([]byte, 96), 148 } 149 cfg := &detection.Config{ 150 SlasherDB: db, 151 } 152 fork, err := p2putils.Fork(savedAttestation.Data.Target.Epoch) 153 require.NoError(t, err) 154 domain, err := helpers.Domain(fork, savedAttestation.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester, wantedGenesis.GenesisValidatorsRoot) 155 require.NoError(t, err) 156 root, err := helpers.ComputeSigningRoot(savedAttestation.Data, domain) 157 require.NoError(t, err) 158 var sig []bls.Signature 159 for _, idx := range savedAttestation.AttestingIndices { 160 validatorSig := keys[idx].Sign(root[:]) 161 sig = append(sig, validatorSig) 162 } 163 aggSig := bls.AggregateSignatures(sig) 164 marshalledSig := aggSig.Marshal() 165 166 savedAttestation.Signature = marshalledSig 167 168 bcCfg := &beaconclient.Config{BeaconClient: bClient, NodeClient: nClient, SlasherDB: db} 169 bs, err := beaconclient.NewService(ctx, bcCfg) 170 require.NoError(t, err) 171 ds := detection.NewService(ctx, cfg) 172 server := Server{ctx: ctx, detector: ds, slasherDB: db, beaconClient: bs} 173 slashings, err := server.IsSlashableAttestation(ctx, savedAttestation) 174 require.NoError(t, err, "Got error while trying to detect slashing") 175 require.Equal(t, 0, len(slashings.AttesterSlashing), "Found slashings while no slashing should have been found on first attestation") 176 sl, err := server.IsSlashableAttestationNoUpdate(ctx, incomingAtt) 177 require.NoError(t, err, "Got error while trying to detect slashing") 178 require.Equal(t, true, sl.Slashable, "Attestation should be found to be slashable") 179 } 180 181 func TestServer_IsSlashableBlock(t *testing.T) { 182 db := testDB.SetupSlasherDB(t, false) 183 ctrl := gomock.NewController(t) 184 defer ctrl.Finish() 185 bClient := mock.NewMockBeaconChainClient(ctrl) 186 nClient := mock.NewMockNodeClient(ctrl) 187 ctx := context.Background() 188 189 _, keys, err := testutil.DeterministicDepositsAndKeys(4) 190 require.NoError(t, err) 191 wantedValidators := ðpb.Validators{ 192 ValidatorList: []*ethpb.Validators_ValidatorContainer{ 193 { 194 Index: 1, Validator: ðpb.Validator{PublicKey: keys[1].PublicKey().Marshal()}, 195 }, 196 }, 197 } 198 bClient.EXPECT().ListValidators( 199 gomock.Any(), 200 gomock.Any(), 201 ).Return(wantedValidators, nil).AnyTimes() 202 203 wantedGenesis := ðpb.Genesis{ 204 GenesisValidatorsRoot: bytesutil.PadTo([]byte("I am genesis"), 32), 205 } 206 nClient.EXPECT().GetGenesis(gomock.Any(), gomock.Any()).Return(wantedGenesis, nil).AnyTimes() 207 savedBlock := testutil.HydrateSignedBeaconHeader(ðpb.SignedBeaconBlockHeader{ 208 Header: ðpb.BeaconBlockHeader{ 209 Slot: 1, 210 ProposerIndex: 1, 211 BodyRoot: bytesutil.PadTo([]byte("body root"), 32), 212 }, 213 }) 214 215 cfg := &detection.Config{ 216 SlasherDB: db, 217 } 218 savedBlockEpoch := helpers.SlotToEpoch(savedBlock.Header.Slot) 219 fork, err := p2putils.Fork(savedBlockEpoch) 220 require.NoError(t, err) 221 domain, err := helpers.Domain(fork, savedBlockEpoch, params.BeaconConfig().DomainBeaconProposer, wantedGenesis.GenesisValidatorsRoot) 222 require.NoError(t, err) 223 224 bcCfg := &beaconclient.Config{BeaconClient: bClient, NodeClient: nClient, SlasherDB: db} 225 bs, err := beaconclient.NewService(ctx, bcCfg) 226 require.NoError(t, err) 227 ds := detection.NewService(ctx, cfg) 228 server := Server{ctx: ctx, detector: ds, slasherDB: db, beaconClient: bs} 229 230 wg := sync.WaitGroup{} 231 wg.Add(100) 232 var wentThrough bool 233 for i := uint64(0); i < 100; i++ { 234 go func(j uint64) { 235 defer wg.Done() 236 sbbh := copyutil.CopySignedBeaconBlockHeader(savedBlock) 237 sbbh.Header.BodyRoot = bytesutil.PadTo([]byte(fmt.Sprintf("%d", j)), 32) 238 bhr, err := sbbh.Header.HashTreeRoot() 239 assert.NoError(t, err) 240 sszBytes := p2ptypes.SSZBytes(bhr[:]) 241 root, err := helpers.ComputeSigningRoot(&sszBytes, domain) 242 assert.NoError(t, err) 243 sbbh.Signature = keys[sbbh.Header.ProposerIndex].Sign(root[:]).Marshal() 244 slashings, err := server.IsSlashableBlock(ctx, sbbh) 245 require.NoError(t, err, "Got error while trying to detect slashing") 246 if len(slashings.ProposerSlashing) == 0 && !wentThrough { 247 wentThrough = true 248 } else if len(slashings.ProposerSlashing) == 0 && wentThrough { 249 t.Fatalf("Only one block should go through without slashing: %v", sbbh) 250 } 251 }(i) 252 } 253 wg.Wait() 254 } 255 256 func TestServer_IsSlashableBlockNoUpdate(t *testing.T) { 257 db := testDB.SetupSlasherDB(t, false) 258 ctrl := gomock.NewController(t) 259 defer ctrl.Finish() 260 bClient := mock.NewMockBeaconChainClient(ctrl) 261 nClient := mock.NewMockNodeClient(ctrl) 262 ctx := context.Background() 263 264 _, keys, err := testutil.DeterministicDepositsAndKeys(4) 265 require.NoError(t, err) 266 wantedValidators := ðpb.Validators{ 267 ValidatorList: []*ethpb.Validators_ValidatorContainer{ 268 { 269 Index: 1, Validator: ðpb.Validator{PublicKey: keys[1].PublicKey().Marshal()}, 270 }, 271 }, 272 } 273 bClient.EXPECT().ListValidators( 274 gomock.Any(), 275 gomock.Any(), 276 ).Return(wantedValidators, nil) 277 278 wantedGenesis := ðpb.Genesis{ 279 GenesisValidatorsRoot: bytesutil.PadTo([]byte("I am genesis"), 32), 280 } 281 nClient.EXPECT().GetGenesis(gomock.Any(), gomock.Any()).Return(wantedGenesis, nil) 282 savedBlock := ðpb.SignedBeaconBlockHeader{ 283 Header: ðpb.BeaconBlockHeader{ 284 Slot: 1, 285 ProposerIndex: 1, 286 BodyRoot: bytesutil.PadTo([]byte("body root"), 32), 287 StateRoot: bytesutil.PadTo([]byte("state root"), 32), 288 ParentRoot: bytesutil.PadTo([]byte("parent root"), 32), 289 }, 290 Signature: make([]byte, 96), 291 } 292 incomingBlock := ðpb.BeaconBlockHeader{ 293 Slot: 1, 294 ProposerIndex: 1, 295 BodyRoot: bytesutil.PadTo([]byte("body root2"), 32), 296 StateRoot: bytesutil.PadTo([]byte("state root2"), 32), 297 ParentRoot: bytesutil.PadTo([]byte("parent root2"), 32), 298 } 299 cfg := &detection.Config{ 300 SlasherDB: db, 301 } 302 savedBlockEpoch := helpers.SlotToEpoch(savedBlock.Header.Slot) 303 fork, err := p2putils.Fork(savedBlockEpoch) 304 require.NoError(t, err) 305 domain, err := helpers.Domain(fork, savedBlockEpoch, params.BeaconConfig().DomainBeaconProposer, wantedGenesis.GenesisValidatorsRoot) 306 require.NoError(t, err) 307 bhr, err := savedBlock.Header.HashTreeRoot() 308 require.NoError(t, err) 309 sszBytes := p2ptypes.SSZBytes(bhr[:]) 310 root, err := helpers.ComputeSigningRoot(&sszBytes, domain) 311 require.NoError(t, err) 312 blockSig := keys[savedBlock.Header.ProposerIndex].Sign(root[:]) 313 marshalledSig := blockSig.Marshal() 314 savedBlock.Signature = marshalledSig 315 bcCfg := &beaconclient.Config{BeaconClient: bClient, NodeClient: nClient, SlasherDB: db} 316 bs, err := beaconclient.NewService(ctx, bcCfg) 317 require.NoError(t, err) 318 ds := detection.NewService(ctx, cfg) 319 server := Server{ctx: ctx, detector: ds, slasherDB: db, beaconClient: bs} 320 slashings, err := server.IsSlashableBlock(ctx, savedBlock) 321 require.NoError(t, err, "Got error while trying to detect slashing") 322 require.Equal(t, 0, len(slashings.ProposerSlashing), "Found slashings while no slashing should have been found on first block") 323 sl, err := server.IsSlashableBlockNoUpdate(ctx, incomingBlock) 324 require.NoError(t, err, "Got error while trying to detect slashing") 325 require.Equal(t, true, sl.Slashable, "Block should be found to be slashable") 326 }