prysm-pulse/validator/client/validator_propose.go
2019-11-12 09:14:03 -08:00

108 lines
3.7 KiB
Go

package client
// Validator client proposer functions.
import (
"context"
"encoding/binary"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-ssz"
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
// ProposeBlock A new beacon block for a given slot. This method collects the
// previous beacon block, any pending deposits, and ETH1 data from the beacon
// chain node to construct the new block. The new block is then processed with
// the state root computation, and finally signed by the validator before being
// sent back to the beacon node for broadcasting.
func (v *validator) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]byte) {
if slot == 0 {
log.Info("Assigned to genesis slot, skipping proposal")
return
}
ctx, span := trace.StartSpan(ctx, "validator.ProposeBlock")
defer span.End()
span.AddAttributes(trace.StringAttribute("validator", fmt.Sprintf("%#x", pubKey)))
log := log.WithField("pubKey", fmt.Sprintf("%#x", bytesutil.Trunc(pubKey[:])))
// Sign randao reveal, it's used to request block from beacon node
epoch := slot / params.BeaconConfig().SlotsPerEpoch
randaoReveal, err := v.signRandaoReveal(ctx, pubKey, epoch)
if err != nil {
log.WithError(err).Error("Failed to sign randao reveal")
return
}
// Request block from beacon node
b, err := v.proposerClient.RequestBlock(ctx, &pb.BlockRequest{
Slot: slot,
RandaoReveal: randaoReveal,
})
if err != nil {
log.WithError(err).Error("Failed to request block from beacon node")
return
}
// Sign returned block from beacon node
sig, err := v.signBlock(ctx, pubKey, epoch, b)
if err != nil {
log.WithError(err).Error("Failed to sign block")
return
}
b.Signature = sig
// Propose and broadcast block via beacon node
blkResp, err := v.proposerClient.ProposeBlock(ctx, b)
if err != nil {
log.WithError(err).Error("Failed to propose block")
return
}
span.AddAttributes(
trace.StringAttribute("blockRoot", fmt.Sprintf("%#x", blkResp.BlockRoot)),
trace.Int64Attribute("numDeposits", int64(len(b.Body.Deposits))),
trace.Int64Attribute("numAttestations", int64(len(b.Body.Attestations))),
)
blkRoot := fmt.Sprintf("%#x", bytesutil.Trunc(blkResp.BlockRoot))
log.WithFields(logrus.Fields{
"slot": b.Slot,
"blockRoot": blkRoot,
"numAttestations": len(b.Body.Attestations),
"numDeposits": len(b.Body.Deposits),
}).Info("Submitted new block")
}
// Sign randao reveal with randao domain and private key.
func (v *validator) signRandaoReveal(ctx context.Context, pubKey [48]byte, epoch uint64) ([]byte, error) {
domain, err := v.validatorClient.DomainData(ctx, &pb.DomainRequest{Epoch: epoch, Domain: params.BeaconConfig().DomainRandao})
if err != nil {
return nil, errors.Wrap(err, "could not get domain data")
}
buf := make([]byte, 32)
binary.LittleEndian.PutUint64(buf, epoch)
randaoReveal := v.keys[pubKey].SecretKey.Sign(buf, domain.SignatureDomain)
return randaoReveal.Marshal(), nil
}
// Sign block with proposer domain and private key.
func (v *validator) signBlock(ctx context.Context, pubKey [48]byte, epoch uint64, b *ethpb.BeaconBlock) ([]byte, error) {
domain, err := v.validatorClient.DomainData(ctx, &pb.DomainRequest{Epoch: epoch, Domain: params.BeaconConfig().DomainBeaconProposer})
if err != nil {
return nil, errors.Wrap(err, "could not get domain data")
}
root, err := ssz.SigningRoot(b)
if err != nil {
return nil, errors.Wrap(err, "could not get signing root")
}
sig := v.keys[pubKey].SecretKey.Sign(root[:], domain.SignatureDomain)
return sig.Marshal(), nil
}