mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-08 18:51:19 +00:00
06a5548424
* `Test_processAttestations`: Remove duplicated tests. * Sort indexed attestations by data root. * `processAttestations`: Don't return duplicate slashings anymore. Fix https://github.com/prysmaticlabs/prysm/issues/13592. * `AttesterDoubleVote`: Rename fields. * Detect double votes in different batches. In order to do that: 1. Each attestation of the batch is tested against the other attestations of the batch. 2. Each attestation of the batch is tested against the content of the database. 2. Attestations are saved into the database. Fixes https://github.com/prysmaticlabs/prysm/issues/13590.
201 lines
6.3 KiB
Go
201 lines
6.3 KiB
Go
package simulator
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"math"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/signing"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
|
"github.com/prysmaticlabs/prysm/v4/config/params"
|
|
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
|
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
|
"github.com/prysmaticlabs/prysm/v4/crypto/rand"
|
|
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
|
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
func (s *Simulator) generateAttestationsForSlot(
|
|
ctx context.Context, slot primitives.Slot,
|
|
) ([]*ethpb.IndexedAttestation, []*ethpb.AttesterSlashing, error) {
|
|
attestations := make([]*ethpb.IndexedAttestation, 0)
|
|
slashings := make([]*ethpb.AttesterSlashing, 0)
|
|
currentEpoch := slots.ToEpoch(slot)
|
|
|
|
committeesPerSlot := helpers.SlotCommitteeCount(s.srvConfig.Params.NumValidators)
|
|
valsPerCommittee := s.srvConfig.Params.NumValidators /
|
|
(committeesPerSlot * uint64(s.srvConfig.Params.SlotsPerEpoch))
|
|
valsPerSlot := committeesPerSlot * valsPerCommittee
|
|
|
|
if currentEpoch < 2 {
|
|
return nil, nil, nil
|
|
}
|
|
sourceEpoch := currentEpoch - 1
|
|
|
|
var slashedIndices []uint64
|
|
startIdx := valsPerSlot * uint64(slot%s.srvConfig.Params.SlotsPerEpoch)
|
|
endIdx := startIdx + valsPerCommittee
|
|
for c := primitives.CommitteeIndex(0); uint64(c) < committeesPerSlot; c++ {
|
|
attData := ðpb.AttestationData{
|
|
Slot: slot,
|
|
CommitteeIndex: c,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("block"), 32),
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: sourceEpoch,
|
|
Root: bytesutil.PadTo([]byte("source"), 32),
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: currentEpoch,
|
|
Root: bytesutil.PadTo([]byte("target"), 32),
|
|
},
|
|
}
|
|
|
|
valsPerAttestation := uint64(math.Floor(s.srvConfig.Params.AggregationPercent * float64(valsPerCommittee)))
|
|
for i := startIdx; i < endIdx; i += valsPerAttestation {
|
|
attEndIdx := i + valsPerAttestation
|
|
if attEndIdx >= endIdx {
|
|
attEndIdx = endIdx
|
|
}
|
|
indices := make([]uint64, 0, valsPerAttestation)
|
|
for idx := i; idx < attEndIdx; idx++ {
|
|
indices = append(indices, idx)
|
|
}
|
|
att := ðpb.IndexedAttestation{
|
|
AttestingIndices: indices,
|
|
Data: attData,
|
|
Signature: params.BeaconConfig().EmptySignature[:],
|
|
}
|
|
beaconState, err := s.srvConfig.AttestationStateFetcher.AttestationTargetState(ctx, att.Data.Target)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Sign the attestation with a valid signature.
|
|
aggSig, err := s.aggregateSigForAttestation(beaconState, att)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
att.Signature = aggSig.Marshal()
|
|
|
|
attestations = append(attestations, att)
|
|
if rand.NewGenerator().Float64() < s.srvConfig.Params.AttesterSlashingProbab {
|
|
slashableAtt := makeSlashableFromAtt(att, []uint64{indices[0]})
|
|
aggSig, err := s.aggregateSigForAttestation(beaconState, slashableAtt)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
slashableAtt.Signature = aggSig.Marshal()
|
|
slashedIndices = append(slashedIndices, slashableAtt.AttestingIndices...)
|
|
|
|
attDataRoot, err := att.Data.HashTreeRoot()
|
|
if err != nil {
|
|
return nil, nil, errors.Wrap(err, "cannot compte `att` hash tree root")
|
|
}
|
|
|
|
slashableAttDataRoot, err := slashableAtt.Data.HashTreeRoot()
|
|
if err != nil {
|
|
return nil, nil, errors.Wrap(err, "cannot compte `slashableAtt` hash tree root")
|
|
}
|
|
|
|
slashing := ðpb.AttesterSlashing{
|
|
Attestation_1: att,
|
|
Attestation_2: slashableAtt,
|
|
}
|
|
|
|
// Ensure the attestation with the lower data root is the first attestation.
|
|
if bytes.Compare(attDataRoot[:], slashableAttDataRoot[:]) > 0 {
|
|
slashing = ðpb.AttesterSlashing{
|
|
Attestation_1: slashableAtt,
|
|
Attestation_2: att,
|
|
}
|
|
}
|
|
|
|
slashings = append(slashings, slashing)
|
|
attestations = append(attestations, slashableAtt)
|
|
}
|
|
}
|
|
startIdx += valsPerCommittee
|
|
endIdx += valsPerCommittee
|
|
}
|
|
if len(slashedIndices) > 0 {
|
|
log.WithFields(logrus.Fields{
|
|
"amount": len(slashedIndices),
|
|
"indices": slashedIndices,
|
|
}).Infof("Slashable attestation made")
|
|
}
|
|
return attestations, slashings, nil
|
|
}
|
|
|
|
func (s *Simulator) aggregateSigForAttestation(
|
|
beaconState state.ReadOnlyBeaconState, att *ethpb.IndexedAttestation,
|
|
) (bls.Signature, error) {
|
|
domain, err := signing.Domain(
|
|
beaconState.Fork(),
|
|
att.Data.Target.Epoch,
|
|
params.BeaconConfig().DomainBeaconAttester,
|
|
beaconState.GenesisValidatorsRoot(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
signingRoot, err := signing.ComputeSigningRoot(att.Data, domain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sigs := make([]bls.Signature, len(att.AttestingIndices))
|
|
for i, validatorIndex := range att.AttestingIndices {
|
|
privKey := s.srvConfig.PrivateKeysByValidatorIndex[primitives.ValidatorIndex(validatorIndex)]
|
|
sigs[i] = privKey.Sign(signingRoot[:])
|
|
}
|
|
return bls.AggregateSignatures(sigs), nil
|
|
}
|
|
|
|
func makeSlashableFromAtt(att *ethpb.IndexedAttestation, indices []uint64) *ethpb.IndexedAttestation {
|
|
if att.Data.Source.Epoch <= 2 {
|
|
return makeDoubleVoteFromAtt(att, indices)
|
|
}
|
|
attData := ðpb.AttestationData{
|
|
Slot: att.Data.Slot,
|
|
CommitteeIndex: att.Data.CommitteeIndex,
|
|
BeaconBlockRoot: att.Data.BeaconBlockRoot,
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: att.Data.Source.Epoch - 3,
|
|
Root: att.Data.Source.Root,
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: att.Data.Target.Epoch,
|
|
Root: att.Data.Target.Root,
|
|
},
|
|
}
|
|
return ðpb.IndexedAttestation{
|
|
AttestingIndices: indices,
|
|
Data: attData,
|
|
Signature: params.BeaconConfig().EmptySignature[:],
|
|
}
|
|
}
|
|
|
|
func makeDoubleVoteFromAtt(att *ethpb.IndexedAttestation, indices []uint64) *ethpb.IndexedAttestation {
|
|
attData := ðpb.AttestationData{
|
|
Slot: att.Data.Slot,
|
|
CommitteeIndex: att.Data.CommitteeIndex,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("slash me"), 32),
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: att.Data.Source.Epoch,
|
|
Root: att.Data.Source.Root,
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: att.Data.Target.Epoch,
|
|
Root: att.Data.Target.Root,
|
|
},
|
|
}
|
|
return ðpb.IndexedAttestation{
|
|
AttestingIndices: indices,
|
|
Data: attData,
|
|
Signature: params.BeaconConfig().EmptySignature[:],
|
|
}
|
|
}
|