mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-10 19:51:20 +00:00
46c72798c7
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
145 lines
5.4 KiB
Go
145 lines
5.4 KiB
Go
package validator
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/core"
|
|
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
|
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
|
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
|
"go.opencensus.io/trace"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
"google.golang.org/protobuf/types/known/emptypb"
|
|
)
|
|
|
|
// GetAttestationData requests that the beacon node produce an attestation data object,
|
|
// which the validator acting as an attester will then sign.
|
|
func (vs *Server) GetAttestationData(ctx context.Context, req *ethpb.AttestationDataRequest) (*ethpb.AttestationData, error) {
|
|
ctx, span := trace.StartSpan(ctx, "AttesterServer.RequestAttestation")
|
|
defer span.End()
|
|
span.AddAttributes(
|
|
trace.Int64Attribute("slot", int64(req.Slot)),
|
|
trace.Int64Attribute("committeeIndex", int64(req.CommitteeIndex)),
|
|
)
|
|
|
|
if vs.SyncChecker.Syncing() {
|
|
return nil, status.Errorf(codes.Unavailable, "Syncing to latest head, not ready to respond")
|
|
}
|
|
|
|
// An optimistic validator MUST NOT participate in attestation. (i.e., sign across the DOMAIN_BEACON_ATTESTER, DOMAIN_SELECTION_PROOF or DOMAIN_AGGREGATE_AND_PROOF domains).
|
|
if err := vs.optimisticStatus(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res, err := vs.CoreService.GetAttestationData(ctx, req)
|
|
if err != nil {
|
|
return nil, status.Errorf(core.ErrorReasonToGRPC(err.Reason), "Could not get attestation data: %v", err.Err)
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
// ProposeAttestation is a function called by an attester to vote
|
|
// on a block via an attestation object as defined in the Ethereum Serenity specification.
|
|
func (vs *Server) ProposeAttestation(ctx context.Context, att *ethpb.Attestation) (*ethpb.AttestResponse, error) {
|
|
ctx, span := trace.StartSpan(ctx, "AttesterServer.ProposeAttestation")
|
|
defer span.End()
|
|
|
|
if _, err := bls.SignatureFromBytes(att.Signature); err != nil {
|
|
return nil, status.Error(codes.InvalidArgument, "Incorrect attestation signature")
|
|
}
|
|
|
|
root, err := att.Data.HashTreeRoot()
|
|
if err != nil {
|
|
return nil, status.Errorf(codes.Internal, "Could not tree hash attestation: %v", err)
|
|
}
|
|
|
|
// Broadcast the unaggregated attestation on a feed to notify other services in the beacon node
|
|
// of a received unaggregated attestation.
|
|
vs.OperationNotifier.OperationFeed().Send(&feed.Event{
|
|
Type: operation.UnaggregatedAttReceived,
|
|
Data: &operation.UnAggregatedAttReceivedData{
|
|
Attestation: att,
|
|
},
|
|
})
|
|
|
|
// Determine subnet to broadcast attestation to
|
|
wantedEpoch := slots.ToEpoch(att.Data.Slot)
|
|
vals, err := vs.HeadFetcher.HeadValidatorsIndices(ctx, wantedEpoch)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
subnet := helpers.ComputeSubnetFromCommitteeAndSlot(uint64(len(vals)), att.Data.CommitteeIndex, att.Data.Slot)
|
|
|
|
// Broadcast the new attestation to the network.
|
|
if err := vs.P2P.BroadcastAttestation(ctx, subnet, att); err != nil {
|
|
return nil, status.Errorf(codes.Internal, "Could not broadcast attestation: %v", err)
|
|
}
|
|
|
|
go func() {
|
|
ctx = trace.NewContext(context.Background(), trace.FromContext(ctx))
|
|
attCopy := ethpb.CopyAttestation(att)
|
|
if err := vs.AttPool.SaveUnaggregatedAttestation(attCopy); err != nil {
|
|
log.WithError(err).Error("Could not handle attestation in operations service")
|
|
return
|
|
}
|
|
}()
|
|
|
|
return ðpb.AttestResponse{
|
|
AttestationDataRoot: root[:],
|
|
}, nil
|
|
}
|
|
|
|
// SubscribeCommitteeSubnets subscribes to the committee ID subnet given subscribe request.
|
|
func (vs *Server) SubscribeCommitteeSubnets(ctx context.Context, req *ethpb.CommitteeSubnetsSubscribeRequest) (*emptypb.Empty, error) {
|
|
ctx, span := trace.StartSpan(ctx, "AttesterServer.SubscribeCommitteeSubnets")
|
|
defer span.End()
|
|
|
|
if len(req.Slots) != len(req.CommitteeIds) || len(req.CommitteeIds) != len(req.IsAggregator) {
|
|
return nil, status.Error(codes.InvalidArgument, "request fields are not the same length")
|
|
}
|
|
if len(req.Slots) == 0 {
|
|
return nil, status.Error(codes.InvalidArgument, "no attester slots provided")
|
|
}
|
|
|
|
fetchValsLen := func(slot primitives.Slot) (uint64, error) {
|
|
wantedEpoch := slots.ToEpoch(slot)
|
|
vals, err := vs.HeadFetcher.HeadValidatorsIndices(ctx, wantedEpoch)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return uint64(len(vals)), nil
|
|
}
|
|
|
|
// Request the head validator indices of epoch represented by the first requested
|
|
// slot.
|
|
currValsLen, err := fetchValsLen(req.Slots[0])
|
|
if err != nil {
|
|
return nil, status.Errorf(codes.Internal, "Could not retrieve head validator length: %v", err)
|
|
}
|
|
currEpoch := slots.ToEpoch(req.Slots[0])
|
|
|
|
for i := 0; i < len(req.Slots); i++ {
|
|
// If epoch has changed, re-request active validators length
|
|
if currEpoch != slots.ToEpoch(req.Slots[i]) {
|
|
currValsLen, err = fetchValsLen(req.Slots[i])
|
|
if err != nil {
|
|
return nil, status.Errorf(codes.Internal, "Could not retrieve head validator length: %v", err)
|
|
}
|
|
currEpoch = slots.ToEpoch(req.Slots[i])
|
|
}
|
|
subnet := helpers.ComputeSubnetFromCommitteeAndSlot(currValsLen, req.CommitteeIds[i], req.Slots[i])
|
|
cache.SubnetIDs.AddAttesterSubnetID(req.Slots[i], subnet)
|
|
if req.IsAggregator[i] {
|
|
cache.SubnetIDs.AddAggregatorSubnetID(req.Slots[i], subnet)
|
|
}
|
|
}
|
|
|
|
return &emptypb.Empty{}, nil
|
|
}
|