mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-04-19 03:01:06 -04:00
<!-- Thanks for sending a PR! Before submitting: 1. If this is your first PR, check out our contribution guide here https://docs.prylabs.network/docs/contribute/contribution-guidelines You will then need to sign our Contributor License Agreement (CLA), which will show up as a comment from a bot in this pull request after you open it. We cannot review code without a signed CLA. 2. Please file an associated tracking issue if this pull request is non-trivial and requires context for our team to understand. All features and most bug fixes should have an associated issue with a design discussed and decided upon. Small bug fixes and documentation improvements don't need issues. 3. New features and bug fixes must have tests. Documentation may need to be updated. If you're unsure what to update, send the PR, and we'll discuss in review. 4. Note that PRs updating dependencies and new Go versions are not accepted. Please file an issue instead. 5. A changelog entry is required for user facing issues. --> **What type of PR is this?** Bug fix **What does this PR do? Why is it needed?** This PR fixes a typo which resulted in a wrong variable name to be returned on the Beacon API `/eth/v1/config/spec` endpoint: ``` curl http://127.0.0.1:49183/eth/v1/config/spec {"data":{"AGGREGRATE_DUE_BPS":"6667", [...] ``` I discovered the discrepancy while testing the change to these "BPS" values in the Vero VC which checks spec values against the ones it ships with. **Which issues(s) does this PR fix?** N/A **Other notes for review** **Acknowledgements** - [x] I have read [CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md). - [x] I have included a uniquely named [changelog fragment file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd). - [x] I have added a description with sufficient context for reviewers to understand this PR. - [x] I have tested that my changes work as expected and I added a testing plan to the PR description (if applicable). --------- Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
273 lines
9.7 KiB
Go
273 lines
9.7 KiB
Go
package client
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
|
|
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
|
"github.com/OffchainLabs/prysm/v7/config/params"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
|
"github.com/OffchainLabs/prysm/v7/crypto/bls"
|
|
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
|
|
"github.com/OffchainLabs/prysm/v7/network/httputil"
|
|
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
|
validatorpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1/validator-client"
|
|
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
|
"github.com/OffchainLabs/prysm/v7/time/slots"
|
|
"github.com/pkg/errors"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
// SubmitAggregateAndProof submits the validator's signed slot signature to the beacon node
|
|
// via gRPC. Beacon node will verify the slot signature and determine if the validator is also
|
|
// an aggregator. If yes, then beacon node will broadcast aggregated signature and
|
|
// proof on the validator's behalf.
|
|
func (v *validator) SubmitAggregateAndProof(ctx context.Context, slot primitives.Slot, pubKey [fieldparams.BLSPubkeyLength]byte) {
|
|
ctx, span := trace.StartSpan(ctx, "validator.SubmitAggregateAndProof")
|
|
defer span.End()
|
|
|
|
span.SetAttributes(trace.StringAttribute("validator", fmt.Sprintf("%#x", pubKey)))
|
|
fmtKey := fmt.Sprintf("%#x", pubKey[:])
|
|
|
|
duty, err := v.duty(pubKey)
|
|
if err != nil {
|
|
log.WithError(err).Error("Could not fetch validator assignment")
|
|
if v.emitAccountMetrics {
|
|
ValidatorAggFailVec.WithLabelValues(fmtKey).Inc()
|
|
}
|
|
return
|
|
}
|
|
|
|
var slotSig []byte
|
|
if !v.distributed {
|
|
// Avoid sending beacon node duplicated aggregation requests.
|
|
k := validatorSubnetSubscriptionKey(slot, duty.CommitteeIndex)
|
|
v.aggregatedSlotCommitteeIDCacheLock.Lock()
|
|
if v.aggregatedSlotCommitteeIDCache.Contains(k) {
|
|
v.aggregatedSlotCommitteeIDCacheLock.Unlock()
|
|
return
|
|
}
|
|
v.aggregatedSlotCommitteeIDCache.Add(k, true)
|
|
v.aggregatedSlotCommitteeIDCacheLock.Unlock()
|
|
|
|
slotSig, err = v.signSlotWithSelectionProof(ctx, pubKey, slot)
|
|
if err != nil {
|
|
log.WithError(err).Error("Could not sign slot")
|
|
if v.emitAccountMetrics {
|
|
ValidatorAggFailVec.WithLabelValues(fmtKey).Inc()
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
// As specified in spec, an aggregator should wait until two thirds of the way through slot
|
|
// to broadcast the best aggregate to the global aggregate channel.
|
|
// https://github.com/ethereum/consensus-specs/blob/v0.9.3/specs/validator/0_beacon-chain-validator.md#broadcast-aggregate
|
|
v.waitToSlotTwoThirds(ctx, slot)
|
|
|
|
// In a DV setup, selection proofs need to be agreed upon by the DV.
|
|
// Checking for selection proofs at slot 0 of the epoch will result in an error, as the call to the DV executes slower than the start of this function.
|
|
// Checking for selection proofs after 2/3 of slot in a DV setup is much faster than non-DV as it's quickly fetched from memory,
|
|
// hence it does not slow down the aggregation as a non-DV would.
|
|
if v.distributed {
|
|
slotSig, err = v.attSelection(attSelectionKey{slot: slot, index: duty.ValidatorIndex})
|
|
if err != nil {
|
|
log.WithError(err).Error("Could not find aggregated selection proof")
|
|
if v.emitAccountMetrics {
|
|
ValidatorAggFailVec.WithLabelValues(fmtKey).Inc()
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
postElectra := slots.ToEpoch(slot) >= params.BeaconConfig().ElectraForkEpoch
|
|
|
|
aggSelectionRequest := ðpb.AggregateSelectionRequest{
|
|
Slot: slot,
|
|
CommitteeIndex: duty.CommitteeIndex,
|
|
PublicKey: pubKey[:],
|
|
SlotSignature: slotSig,
|
|
}
|
|
// TODO: look at renaming SubmitAggregateSelectionProof functions as they are GET beacon API
|
|
var agg ethpb.AggregateAttAndProof
|
|
if postElectra {
|
|
res, err := v.validatorClient.SubmitAggregateSelectionProofElectra(ctx, aggSelectionRequest, duty.ValidatorIndex, duty.CommitteeLength)
|
|
if err != nil {
|
|
v.handleSubmitAggSelectionProofError(err, slot, fmtKey)
|
|
return
|
|
}
|
|
agg = res.AggregateAndProof
|
|
} else {
|
|
res, err := v.validatorClient.SubmitAggregateSelectionProof(ctx, aggSelectionRequest, duty.ValidatorIndex, duty.CommitteeLength)
|
|
if err != nil {
|
|
v.handleSubmitAggSelectionProofError(err, slot, fmtKey)
|
|
return
|
|
}
|
|
agg = res.AggregateAndProof
|
|
}
|
|
|
|
sig, err := v.aggregateAndProofSig(ctx, pubKey, agg, slot)
|
|
if err != nil {
|
|
log.WithError(err).Error("Could not sign aggregate and proof")
|
|
return
|
|
}
|
|
|
|
if postElectra {
|
|
msg, ok := agg.(*ethpb.AggregateAttestationAndProofElectra)
|
|
if !ok {
|
|
log.Errorf("Message is not %T", ðpb.AggregateAttestationAndProofElectra{})
|
|
if v.emitAccountMetrics {
|
|
ValidatorAggFailVec.WithLabelValues(fmtKey).Inc()
|
|
}
|
|
return
|
|
}
|
|
_, err = v.validatorClient.SubmitSignedAggregateSelectionProofElectra(ctx, ðpb.SignedAggregateSubmitElectraRequest{
|
|
SignedAggregateAndProof: ðpb.SignedAggregateAttestationAndProofElectra{
|
|
Message: msg,
|
|
Signature: sig,
|
|
},
|
|
})
|
|
if err != nil {
|
|
log.WithError(err).Error("Could not submit signed aggregate and proof to beacon node")
|
|
if v.emitAccountMetrics {
|
|
ValidatorAggFailVec.WithLabelValues(fmtKey).Inc()
|
|
}
|
|
return
|
|
}
|
|
} else {
|
|
msg, ok := agg.(*ethpb.AggregateAttestationAndProof)
|
|
if !ok {
|
|
log.Errorf("Message is not %T", ðpb.AggregateAttestationAndProof{})
|
|
if v.emitAccountMetrics {
|
|
ValidatorAggFailVec.WithLabelValues(fmtKey).Inc()
|
|
}
|
|
return
|
|
}
|
|
_, err = v.validatorClient.SubmitSignedAggregateSelectionProof(ctx, ðpb.SignedAggregateSubmitRequest{
|
|
SignedAggregateAndProof: ðpb.SignedAggregateAttestationAndProof{
|
|
Message: msg,
|
|
Signature: sig,
|
|
},
|
|
})
|
|
if err != nil {
|
|
log.WithError(err).Error("Could not submit signed aggregate and proof to beacon node")
|
|
if v.emitAccountMetrics {
|
|
ValidatorAggFailVec.WithLabelValues(fmtKey).Inc()
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
if err := v.saveSubmittedAtt(agg.AggregateVal(), pubKey[:], true); err != nil {
|
|
log.WithError(err).Error("Could not add aggregator indices to logs")
|
|
if v.emitAccountMetrics {
|
|
ValidatorAggFailVec.WithLabelValues(fmtKey).Inc()
|
|
}
|
|
return
|
|
}
|
|
if v.emitAccountMetrics {
|
|
ValidatorAggSuccessVec.WithLabelValues(fmtKey).Inc()
|
|
}
|
|
}
|
|
|
|
// Signs input slot with domain selection proof. This is used to create the signature for aggregator selection.
|
|
func (v *validator) signSlotWithSelectionProof(ctx context.Context, pubKey [fieldparams.BLSPubkeyLength]byte, slot primitives.Slot) (signature []byte, err error) {
|
|
ctx, span := trace.StartSpan(ctx, "validator.signSlotWithSelectionProof")
|
|
defer span.End()
|
|
|
|
domain, err := v.domainData(ctx, slots.ToEpoch(slot), params.BeaconConfig().DomainSelectionProof[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var sig bls.Signature
|
|
sszUint := primitives.SSZUint64(slot)
|
|
root, err := signing.ComputeSigningRoot(&sszUint, domain.SignatureDomain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sig, err = v.km.Sign(ctx, &validatorpb.SignRequest{
|
|
PublicKey: pubKey[:],
|
|
SigningRoot: root[:],
|
|
SignatureDomain: domain.SignatureDomain,
|
|
Object: &validatorpb.SignRequest_Slot{Slot: slot},
|
|
SigningSlot: slot,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return sig.Marshal(), nil
|
|
}
|
|
|
|
// waitToSlotTwoThirds waits until two third through the current slot period
|
|
// such that any attestations from this slot have time to reach the beacon node
|
|
// before creating the aggregated attestation.
|
|
func (v *validator) waitToSlotTwoThirds(ctx context.Context, slot primitives.Slot) {
|
|
v.waitUntilSlotComponent(ctx, slot, params.BeaconConfig().AggregateDueBPS)
|
|
}
|
|
|
|
// This returns the signature of validator signing over aggregate and
|
|
// proof object.
|
|
func (v *validator) aggregateAndProofSig(ctx context.Context, pubKey [fieldparams.BLSPubkeyLength]byte, agg ethpb.AggregateAttAndProof, slot primitives.Slot) ([]byte, error) {
|
|
ctx, span := trace.StartSpan(ctx, "validator.aggregateAndProofSig")
|
|
defer span.End()
|
|
|
|
d, err := v.domainData(ctx, slots.ToEpoch(agg.AggregateVal().GetData().Slot), params.BeaconConfig().DomainAggregateAndProof[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
root, err := signing.ComputeSigningRoot(agg, d.SignatureDomain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
signRequest := &validatorpb.SignRequest{
|
|
PublicKey: pubKey[:],
|
|
SigningRoot: root[:],
|
|
SignatureDomain: d.SignatureDomain,
|
|
SigningSlot: slot,
|
|
}
|
|
if agg.Version() >= version.Electra {
|
|
aggregate, ok := agg.(*ethpb.AggregateAttestationAndProofElectra)
|
|
if !ok {
|
|
return nil, fmt.Errorf("wrong aggregate type (expected %T, got %T)", ðpb.AggregateAttestationAndProofElectra{}, agg)
|
|
}
|
|
signRequest.Object = &validatorpb.SignRequest_AggregateAttestationAndProofElectra{AggregateAttestationAndProofElectra: aggregate}
|
|
} else {
|
|
aggregate, ok := agg.(*ethpb.AggregateAttestationAndProof)
|
|
if !ok {
|
|
return nil, fmt.Errorf("wrong aggregate type (expected %T, got %T)", ðpb.AggregateAttestationAndProof{}, agg)
|
|
}
|
|
signRequest.Object = &validatorpb.SignRequest_AggregateAttestationAndProof{AggregateAttestationAndProof: aggregate}
|
|
}
|
|
|
|
sig, err := v.km.Sign(ctx, signRequest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return sig.Marshal(), nil
|
|
}
|
|
|
|
func (v *validator) handleSubmitAggSelectionProofError(err error, slot primitives.Slot, hexPubkey string) {
|
|
// handle grpc not found
|
|
s, ok := status.FromError(err)
|
|
grpcNotFound := ok && s.Code() == codes.NotFound
|
|
// handle http not found
|
|
jsonErr := &httputil.DefaultJsonError{}
|
|
httpNotFound := errors.As(err, &jsonErr) && jsonErr.Code == http.StatusNotFound
|
|
|
|
if grpcNotFound || httpNotFound {
|
|
log.WithField("slot", slot).WithError(err).Warn("No attestations to aggregate")
|
|
} else {
|
|
log.WithField("slot", slot).WithError(err).Error("Could not submit aggregate selection proof to beacon node")
|
|
if v.emitAccountMetrics {
|
|
ValidatorAggFailVec.WithLabelValues(hexPubkey).Inc()
|
|
}
|
|
}
|
|
}
|