-
Notifications
You must be signed in to change notification settings - Fork 929
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Slasher double block detection to E2E #5936
Changes from 3 commits
2a0ccdd
20a9514
3bcbad5
eb17faa
a590779
5abbfda
cba933f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package evaluators | |
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
|
||
ptypes "github.com/gogo/protobuf/types" | ||
|
@@ -19,33 +20,33 @@ import ( | |
// InjectDoubleVote broadcasts a double vote into the beacon node pool for the slasher to detect. | ||
var InjectDoubleVote = types.Evaluator{ | ||
Name: "inject_double_vote_%d", | ||
Policy: beforeEpoch(2), | ||
Policy: onEpoch(1), | ||
Evaluation: insertDoubleAttestationIntoPool, | ||
} | ||
|
||
// ProposeDoubleBlock broadcasts a double block to the beacon node for the slasher to detect. | ||
var ProposeDoubleBlock = types.Evaluator{ | ||
Name: "propose_double_block_%d", | ||
Policy: onEpoch(1), | ||
Evaluation: proposeDoubleBlock, | ||
} | ||
|
||
// ValidatorsSlashed ensures the expected amount of validators are slashed. | ||
var ValidatorsSlashed = types.Evaluator{ | ||
Name: "validators_slashed_epoch_%d", | ||
Policy: afterNthEpoch(0), | ||
Policy: afterNthEpoch(1), | ||
Evaluation: validatorsSlashed, | ||
} | ||
|
||
// SlashedValidatorsLoseBalance checks if the validators slashed lose the right balance. | ||
var SlashedValidatorsLoseBalance = types.Evaluator{ | ||
Name: "slashed_validators_lose_valance_epoch_%d", | ||
Policy: afterNthEpoch(0), | ||
Policy: afterNthEpoch(1), | ||
Evaluation: validatorsLoseBalance, | ||
} | ||
|
||
var slashedIndices []uint64 | ||
|
||
// Not including first epoch because of issues with genesis. | ||
func beforeEpoch(epoch uint64) func(uint64) bool { | ||
return func(currentEpoch uint64) bool { | ||
return currentEpoch < epoch | ||
} | ||
} | ||
|
||
func validatorsSlashed(conns ...*grpc.ClientConn) error { | ||
conn := conns[0] | ||
ctx := context.Background() | ||
|
@@ -55,8 +56,8 @@ func validatorsSlashed(conns ...*grpc.ClientConn) error { | |
if err != nil { | ||
return err | ||
} | ||
if len(changes.SlashedIndices) != 2 && len(changes.SlashedIndices) != 4 { | ||
return fmt.Errorf("expected 2 or 4 indices to be slashed, received %d", len(changes.SlashedIndices)) | ||
if len(changes.SlashedIndices) != len(slashedIndices) { | ||
return fmt.Errorf("expected %d indices to be slashed, received %d", len(slashedIndices), len(changes.SlashedIndices)) | ||
} | ||
return nil | ||
} | ||
|
@@ -169,14 +170,105 @@ func insertDoubleAttestationIntoPool(conns ...*grpc.ClientConn) error { | |
Data: attData, | ||
Signature: privKeys[committee[i]].Sign(signingRoot[:]).Marshal(), | ||
} | ||
for _, conn := range conns { | ||
client := eth.NewBeaconNodeValidatorClient(conn) | ||
_, err = client.ProposeAttestation(ctx, att) | ||
if err != nil { | ||
return err | ||
} | ||
client := eth.NewBeaconNodeValidatorClient(conns[0]) | ||
_, err = client.ProposeAttestation(ctx, att) | ||
if err != nil { | ||
return err | ||
} | ||
slashedIndices = append(slashedIndices, committee[i]) | ||
} | ||
return nil | ||
} | ||
|
||
func proposeDoubleBlock(conns ...*grpc.ClientConn) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can return more informative errors in each of the conditionals in this func, something like errors.Wrap(err, "could not do X") |
||
conn := conns[0] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what happen if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It will always be something here, the test would fail much earlier elsewhere if it wasn't. That would mean we don't have any beacon nodes to connect to. At least 1 node is a guarantee here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree. But better to be defensive. Someone brand new can be writing an e2e test and use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, why pass in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Putting the check here would be very odd, these style of tests follow an interface for all of E2E. Only There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests are at least run with 1 node, so pinging for any chain data that shouldn't be node dependent is done with the node index that we know is running. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, only using one beacon node here helps test There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. I got confused. Perhaps we should add a comment to mention only con0 is explicitly used but input takes in a list of conns. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure thing, that sounds good 👍 |
||
valClient := eth.NewBeaconNodeValidatorClient(conn) | ||
beaconClient := eth.NewBeaconChainClient(conn) | ||
|
||
ctx := context.Background() | ||
chainHead, err := beaconClient.GetChainHead(ctx, &ptypes.Empty{}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
_, privKeys, err := testutil.DeterministicDepositsAndKeys(64) | ||
if err != nil { | ||
return err | ||
} | ||
pubKeys := make([][]byte, len(privKeys)) | ||
for i, priv := range privKeys { | ||
pubKeys[i] = priv.PublicKey().Marshal() | ||
} | ||
duties, err := valClient.GetDuties(ctx, ð.DutiesRequest{ | ||
Epoch: chainHead.HeadEpoch, | ||
PublicKeys: pubKeys, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var proposerIndex uint64 | ||
for i, duty := range duties.CurrentEpochDuties { | ||
if sliceutil.IsInUint64(chainHead.HeadSlot-1, duty.ProposerSlots) { | ||
proposerIndex = uint64(i) | ||
break | ||
} | ||
} | ||
|
||
blk := ð.BeaconBlock{ | ||
Slot: chainHead.HeadSlot - 1, | ||
ParentRoot: to32BytesSlice([]byte("bad parent root")), | ||
StateRoot: to32BytesSlice([]byte("bad state root")), | ||
ProposerIndex: proposerIndex, | ||
Body: ð.BeaconBlockBody{ | ||
Eth1Data: ð.Eth1Data{ | ||
BlockHash: to32BytesSlice([]byte("bad block hash")), | ||
DepositRoot: to32BytesSlice([]byte("bad deposit root")), | ||
DepositCount: 1, | ||
}, | ||
RandaoReveal: to96BytesSlice([]byte("bad randao")), | ||
Graffiti: to32BytesSlice([]byte("teehee")), | ||
ProposerSlashings: []*eth.ProposerSlashing{}, | ||
AttesterSlashings: []*eth.AttesterSlashing{}, | ||
Attestations: []*eth.Attestation{}, | ||
Deposits: []*eth.Deposit{}, | ||
VoluntaryExits: []*eth.SignedVoluntaryExit{}, | ||
}, | ||
} | ||
|
||
req := ð.DomainRequest{ | ||
Epoch: chainHead.HeadEpoch, | ||
Domain: params.BeaconConfig().DomainBeaconProposer[:], | ||
} | ||
resp, err := valClient.DomainData(ctx, req) | ||
if err != nil { | ||
return err | ||
} | ||
signingRoot, err := helpers.ComputeSigningRoot(blk, resp.SignatureDomain) | ||
if err != nil { | ||
return err | ||
} | ||
sig := privKeys[proposerIndex].Sign(signingRoot[:]).Marshal() | ||
signedBlk := ð.SignedBeaconBlock{ | ||
Block: blk, | ||
Signature: sig, | ||
} | ||
|
||
client := eth.NewBeaconNodeValidatorClient(conns[0]) | ||
_, err = client.ProposeBlock(ctx, signedBlk) | ||
if err == nil { | ||
return errors.New("expected block to fail processing") | ||
} | ||
slashedIndices = append(slashedIndices, proposerIndex) | ||
return nil | ||
} | ||
|
||
func to32BytesSlice(byteArray []byte) []byte { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't have this in |
||
bytes32 := bytesutil.PadTo(byteArray, 32) | ||
return bytes32[:] | ||
} | ||
|
||
func to96BytesSlice(byteArray []byte) []byte { | ||
bytes96 := bytesutil.PadTo(byteArray, 96) | ||
return bytes96[:] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if _, err = client.ProposeAttestation