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 }