func (s *Service) Propose(ctx context.Context, data interface{}) {
	started := time.Now()
	duty, ok := data.(*beaconblockproposer.Duty)
	if !ok {
		log.Error().Msg("Passed invalid data structure")
		s.monitor.BeaconBlockProposalCompleted(started, "failed")
		return
	}
	log := log.With().Uint64("proposing_slot", uint64(duty.Slot())).Uint64("validator_index", uint64(duty.ValidatorIndex())).Logger()
	log.Trace().Msg("Proposing")
	var zeroSig phase0.BLSSignature
	if duty.RANDAOReveal() == zeroSig {
		log.Error().Msg("Missing RANDAO reveal")
		s.monitor.BeaconBlockProposalCompleted(started, "failed")
		return
	}
	var graffiti []byte
	var err error
	if s.graffitiProvider != nil {
		graffiti, err = s.graffitiProvider.Graffiti(ctx, duty.Slot(), duty.ValidatorIndex())
		if err != nil {
			log.Warn().Err(err).Msg("Failed to obtain graffiti")
			graffiti = nil
		}
	}
	log.Trace().Dur("elapsed", time.Since(started)).Msg("Obtained graffiti")
	proposal, err := s.proposalProvider.BeaconBlockProposal(ctx, duty.Slot(), duty.RANDAOReveal(), graffiti)
	if err != nil {
		log.Error().Err(err).Msg("Failed to obtain proposal data")
		s.monitor.BeaconBlockProposalCompleted(started, "failed")
		return
	}
	if proposal == nil {
		log.Error().Msg("Provider did not return beacon block proposal")
		s.monitor.BeaconBlockProposalCompleted(started, "failed")
		return
	}
	log.Trace().Dur("elapsed", time.Since(started)).Msg("Obtained proposal")
	proposalSlot, err := proposal.Slot()
	if err != nil {
		log.Error().Str("version", proposal.Version.String()).Err(err).Msg("Unknown proposal version")
		s.monitor.BeaconBlockProposalCompleted(started, "failed")
		return
	}
	if proposalSlot != duty.Slot() {
		log.Error().Uint64("proposal_slot", uint64(proposalSlot)).Msg("Proposal data for incorrect slot; not proceeding")
		s.monitor.BeaconBlockProposalCompleted(started, "failed")
		return
	}
	bodyRoot, err := proposal.BodyRoot()
	if err != nil {
		log.Error().Err(err).Msg("Failed to calculate hash tree root of block")
		s.monitor.BeaconBlockProposalCompleted(started, "failed")
		return
	}
	parentRoot, err := proposal.ParentRoot()
	if err != nil {
		log.Error().Err(err).Msg("Failed to obtain parent root of block")
		s.monitor.BeaconBlockProposalCompleted(started, "failed")
		return
	}
	stateRoot, err := proposal.StateRoot()
	if err != nil {
		log.Error().Err(err).Msg("Failed to obtain state root of block")
		s.monitor.BeaconBlockProposalCompleted(started, "failed")
		return
	}
	sig, err := s.beaconBlockSigner.SignBeaconBlockProposal(ctx,
		duty.Account(),
		proposalSlot,
		duty.ValidatorIndex(),
		parentRoot,
		stateRoot,
		bodyRoot)
	if err != nil {
		log.Error().Err(err).Msg("Failed to sign beacon block proposal")
		s.monitor.BeaconBlockProposalCompleted(started, "failed")
		return
	}
	log.Trace().Dur("elapsed", time.Since(started)).Msg("Signed proposal")
	signedBlock := &spec.VersionedSignedBeaconBlock{
		Version: proposal.Version,
	}
	switch signedBlock.Version {
	case spec.DataVersionPhase0:
		signedBlock.Phase0 = &phase0.SignedBeaconBlock{
			Message:   proposal.Phase0,
			Signature: sig,
		}
	case spec.DataVersionAltair:
		signedBlock.Altair = &altair.SignedBeaconBlock{
			Message:   proposal.Altair,
			Signature: sig,
		}
	default:
		log.Error().Str("version", proposal.Version.String()).Msg("Unknown proposal version")
		s.monitor.BeaconBlockProposalCompleted(started, "failed")
		return
	}
	if err := s.beaconBlockSubmitter.SubmitBeaconBlock(ctx, signedBlock); err != nil {
		log.Error().Err(err).Msg("Failed to submit beacon block proposal")
		s.monitor.BeaconBlockProposalCompleted(started, "failed")
		return
	}
	log.Trace().Dur("elapsed", time.Since(started)).Msg("Submitted proposal")
	s.monitor.BeaconBlockProposalCompleted(started, "succeeded")
}
Propose proposes a block.
package standard
import (
	"context"
	"fmt"
	"time"
	eth2client "github.com/attestantio/go-eth2-client"
	"github.com/attestantio/go-eth2-client/spec"
	"github.com/attestantio/go-eth2-client/spec/altair"
	"github.com/attestantio/go-eth2-client/spec/phase0"
	"github.com/attestantio/vouch/services/accountmanager"
	"github.com/attestantio/vouch/services/beaconblockproposer"
	"github.com/attestantio/vouch/services/chaintime"
	"github.com/attestantio/vouch/services/graffitiprovider"
	"github.com/attestantio/vouch/services/metrics"
	"github.com/attestantio/vouch/services/signer"
	"github.com/attestantio/vouch/services/submitter"
	"github.com/pkg/errors"
	"github.com/rs/zerolog"
	zerologger "github.com/rs/zerolog/log"
)
type Service struct {
	monitor                    metrics.BeaconBlockProposalMonitor
	chainTimeService           chaintime.Service
	proposalProvider           eth2client.BeaconBlockProposalProvider
	validatingAccountsProvider accountmanager.ValidatingAccountsProvider
	graffitiProvider           graffitiprovider.Service
	beaconBlockSubmitter       submitter.BeaconBlockSubmitter
	randaoRevealSigner         signer.RANDAORevealSigner
	beaconBlockSigner          signer.BeaconBlockSigner
}
var log zerolog.Logger
func New(ctx context.Context, params ...Parameter) (*Service, error) {
	parameters, err := parseAndCheckParameters(params...)
	if err != nil {
		return nil, errors.Wrap(err, "problem with parameters")
	}
	log = zerologger.With().Str("service", "beaconblockproposer").Str("impl", "standard").Logger()
	if parameters.logLevel != log.GetLevel() {
		log = log.Level(parameters.logLevel)
	}
	s := &Service{
		monitor:                    parameters.monitor,
		chainTimeService:           parameters.chainTimeService,
		proposalProvider:           parameters.proposalProvider,
		validatingAccountsProvider: parameters.validatingAccountsProvider,
		graffitiProvider:           parameters.graffitiProvider,
		beaconBlockSubmitter:       parameters.beaconBlockSubmitter,
		randaoRevealSigner:         parameters.randaoRevealSigner,
		beaconBlockSigner:          parameters.beaconBlockSigner,
	}
	return s, nil
}
func (s *Service) Prepare(ctx context.Context, data interface{}) error {
	started := time.Now()
	duty, ok := data.(*beaconblockproposer.Duty)
	if !ok {
		return errors.New("passed invalid data structure")
	}
	log := log.With().Uint64("proposing_slot", uint64(duty.Slot())).Uint64("validator_index", uint64(duty.ValidatorIndex())).Logger()
	log.Trace().Msg("Preparing")
	dutyEpoch := s.chainTimeService.SlotToEpoch(duty.Slot())
	accounts, err := s.validatingAccountsProvider.ValidatingAccountsForEpochByIndex(ctx,
		dutyEpoch,
		[]phase0.ValidatorIndex{duty.ValidatorIndex()},
	)
	if err != nil {
		return errors.Wrap(err, "failed to obtain proposing validator account")
	}
	if len(accounts) != 1 {
		return fmt.Errorf("unknown proposing validator account %d", duty.ValidatorIndex())
	}
	account := accounts[duty.ValidatorIndex()]
	log.Trace().Dur("elapsed", time.Since(started)).Msg("Obtained proposing account")
	duty.SetAccount(account)
	randaoReveal, err := s.randaoRevealSigner.SignRANDAOReveal(ctx, account, duty.Slot())
	if err != nil {
		return errors.Wrap(err, "failed to sign RANDAO reveal")
	}
	log.Trace().Dur("elapsed", time.Since(started)).Msg("Signed RANDAO reveal")
	duty.SetRandaoReveal(randaoReveal)
	return nil
}