github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/p2p/broadcaster.go (about)

     1  package p2p
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"reflect"
     8  	"time"
     9  
    10  	"github.com/pkg/errors"
    11  	eth "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    12  	"github.com/prysmaticlabs/prysm/shared/hashutil"
    13  	"github.com/prysmaticlabs/prysm/shared/params"
    14  	"github.com/prysmaticlabs/prysm/shared/traceutil"
    15  	"go.opencensus.io/trace"
    16  	"google.golang.org/protobuf/proto"
    17  )
    18  
    19  // ErrMessageNotMapped occurs on a Broadcast attempt when a message has not been defined in the
    20  // GossipTypeMapping.
    21  var ErrMessageNotMapped = errors.New("message type is not mapped to a PubSub topic")
    22  
    23  // Broadcast a message to the p2p network.
    24  func (s *Service) Broadcast(ctx context.Context, msg proto.Message) error {
    25  	ctx, span := trace.StartSpan(ctx, "p2p.Broadcast")
    26  	defer span.End()
    27  
    28  	twoSlots := time.Duration(2*params.BeaconConfig().SecondsPerSlot) * time.Second
    29  	ctx, cancel := context.WithTimeout(ctx, twoSlots)
    30  	defer cancel()
    31  
    32  	forkDigest, err := s.forkDigest()
    33  	if err != nil {
    34  		err := errors.Wrap(err, "could not retrieve fork digest")
    35  		traceutil.AnnotateError(span, err)
    36  		return err
    37  	}
    38  
    39  	topic, ok := GossipTypeMapping[reflect.TypeOf(msg)]
    40  	if !ok {
    41  		traceutil.AnnotateError(span, ErrMessageNotMapped)
    42  		return ErrMessageNotMapped
    43  	}
    44  	return s.broadcastObject(ctx, msg, fmt.Sprintf(topic, forkDigest))
    45  }
    46  
    47  // BroadcastAttestation broadcasts an attestation to the p2p network.
    48  func (s *Service) BroadcastAttestation(ctx context.Context, subnet uint64, att *eth.Attestation) error {
    49  	ctx, span := trace.StartSpan(ctx, "p2p.BroadcastAttestation")
    50  	defer span.End()
    51  	forkDigest, err := s.forkDigest()
    52  	if err != nil {
    53  		err := errors.Wrap(err, "could not retrieve fork digest")
    54  		traceutil.AnnotateError(span, err)
    55  		return err
    56  	}
    57  
    58  	// Non-blocking broadcast, with attempts to discover a subnet peer if none available.
    59  	go s.broadcastAttestation(ctx, subnet, att, forkDigest)
    60  
    61  	return nil
    62  }
    63  
    64  func (s *Service) broadcastAttestation(ctx context.Context, subnet uint64, att *eth.Attestation, forkDigest [4]byte) {
    65  	ctx, span := trace.StartSpan(ctx, "p2p.broadcastAttestation")
    66  	defer span.End()
    67  	ctx = trace.NewContext(context.Background(), span) // clear parent context / deadline.
    68  
    69  	oneEpoch := time.Duration(1*params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot)) * time.Second
    70  	ctx, cancel := context.WithTimeout(ctx, oneEpoch)
    71  	defer cancel()
    72  
    73  	// Ensure we have peers with this subnet.
    74  	s.subnetLocker(subnet).RLock()
    75  	hasPeer := s.hasPeerWithSubnet(attestationToTopic(subnet, forkDigest))
    76  	s.subnetLocker(subnet).RUnlock()
    77  
    78  	span.AddAttributes(
    79  		trace.BoolAttribute("hasPeer", hasPeer),
    80  		trace.Int64Attribute("slot", int64(att.Data.Slot)),
    81  		trace.Int64Attribute("subnet", int64(subnet)),
    82  	)
    83  
    84  	if !hasPeer {
    85  		attestationBroadcastAttempts.Inc()
    86  		if err := func() error {
    87  			s.subnetLocker(subnet).Lock()
    88  			defer s.subnetLocker(subnet).Unlock()
    89  			ok, err := s.FindPeersWithSubnet(ctx, attestationToTopic(subnet, forkDigest), subnet, 1)
    90  			if err != nil {
    91  				return err
    92  			}
    93  			if ok {
    94  				savedAttestationBroadcasts.Inc()
    95  				return nil
    96  			}
    97  			return errors.New("failed to find peers for subnet")
    98  		}(); err != nil {
    99  			log.WithError(err).Error("Failed to find peers")
   100  			traceutil.AnnotateError(span, err)
   101  		}
   102  	}
   103  
   104  	if err := s.broadcastObject(ctx, att, attestationToTopic(subnet, forkDigest)); err != nil {
   105  		log.WithError(err).Error("Failed to broadcast attestation")
   106  		traceutil.AnnotateError(span, err)
   107  	}
   108  }
   109  
   110  // method to broadcast messages to other peers in our gossip mesh.
   111  func (s *Service) broadcastObject(ctx context.Context, obj interface{}, topic string) error {
   112  	_, span := trace.StartSpan(ctx, "p2p.broadcastObject")
   113  	defer span.End()
   114  
   115  	span.AddAttributes(trace.StringAttribute("topic", topic))
   116  
   117  	buf := new(bytes.Buffer)
   118  	if _, err := s.Encoding().EncodeGossip(buf, obj); err != nil {
   119  		err := errors.Wrap(err, "could not encode message")
   120  		traceutil.AnnotateError(span, err)
   121  		return err
   122  	}
   123  
   124  	if span.IsRecordingEvents() {
   125  		id := hashutil.FastSum64(buf.Bytes())
   126  		messageLen := int64(buf.Len())
   127  		span.AddMessageSendEvent(int64(id), messageLen /*uncompressed*/, messageLen /*compressed*/)
   128  	}
   129  
   130  	if err := s.PublishToTopic(ctx, topic+s.Encoding().ProtocolSuffix(), buf.Bytes()); err != nil {
   131  		err := errors.Wrap(err, "could not publish message")
   132  		traceutil.AnnotateError(span, err)
   133  		return err
   134  	}
   135  	return nil
   136  }
   137  
   138  func attestationToTopic(subnet uint64, forkDigest [4]byte) string {
   139  	return fmt.Sprintf(AttestationSubnetTopicFormat, forkDigest, subnet)
   140  }