github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/p2p/subnets.go (about) 1 package p2p 2 3 import ( 4 "context" 5 "sync" 6 7 "github.com/ethereum/go-ethereum/p2p/enode" 8 "github.com/ethereum/go-ethereum/p2p/enr" 9 "github.com/prysmaticlabs/go-bitfield" 10 "github.com/prysmaticlabs/prysm/shared/interfaces" 11 "go.opencensus.io/trace" 12 13 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 14 "github.com/prysmaticlabs/prysm/shared/params" 15 ) 16 17 var attestationSubnetCount = params.BeaconNetworkConfig().AttestationSubnetCount 18 19 var attSubnetEnrKey = params.BeaconNetworkConfig().AttSubnetKey 20 21 // FindPeersWithSubnet performs a network search for peers 22 // subscribed to a particular subnet. Then we try to connect 23 // with those peers. This method will block until the required amount of 24 // peers are found, the method only exits in the event of context timeouts. 25 func (s *Service) FindPeersWithSubnet(ctx context.Context, topic string, 26 index, threshold uint64) (bool, error) { 27 ctx, span := trace.StartSpan(ctx, "p2p.FindPeersWithSubnet") 28 defer span.End() 29 30 span.AddAttributes(trace.Int64Attribute("index", int64(index))) 31 32 if s.dv5Listener == nil { 33 // return if discovery isn't set 34 return false, nil 35 } 36 37 topic += s.Encoding().ProtocolSuffix() 38 iterator := s.dv5Listener.RandomNodes() 39 iterator = filterNodes(ctx, iterator, s.filterPeerForSubnet(index)) 40 41 currNum := uint64(len(s.pubsub.ListPeers(topic))) 42 wg := new(sync.WaitGroup) 43 for { 44 if err := ctx.Err(); err != nil { 45 return false, err 46 } 47 if currNum >= threshold { 48 break 49 } 50 nodes := enode.ReadNodes(iterator, int(params.BeaconNetworkConfig().MinimumPeersInSubnetSearch)) 51 for _, node := range nodes { 52 info, _, err := convertToAddrInfo(node) 53 if err != nil { 54 continue 55 } 56 wg.Add(1) 57 go func() { 58 if err := s.connectWithPeer(ctx, *info); err != nil { 59 log.WithError(err).Tracef("Could not connect with peer %s", info.String()) 60 } 61 wg.Done() 62 }() 63 } 64 // Wait for all dials to be completed. 65 wg.Wait() 66 currNum = uint64(len(s.pubsub.ListPeers(topic))) 67 } 68 return true, nil 69 } 70 71 // returns a method with filters peers specifically for a particular attestation subnet. 72 func (s *Service) filterPeerForSubnet(index uint64) func(node *enode.Node) bool { 73 return func(node *enode.Node) bool { 74 if !s.filterPeer(node) { 75 return false 76 } 77 subnets, err := attSubnets(node.Record()) 78 if err != nil { 79 return false 80 } 81 indExists := false 82 for _, comIdx := range subnets { 83 if comIdx == index { 84 indExists = true 85 break 86 } 87 } 88 return indExists 89 } 90 } 91 92 // lower threshold to broadcast object compared to searching 93 // for a subnet. So that even in the event of poor peer 94 // connectivity, we can still broadcast an attestation. 95 func (s *Service) hasPeerWithSubnet(topic string) bool { 96 return len(s.pubsub.ListPeers(topic+s.Encoding().ProtocolSuffix())) >= 1 97 } 98 99 // Updates the service's discv5 listener record's attestation subnet 100 // with a new value for a bitfield of subnets tracked. It also updates 101 // the node's metadata by increasing the sequence number and the 102 // subnets tracked by the node. 103 func (s *Service) updateSubnetRecordWithMetadata(bitV bitfield.Bitvector64) { 104 entry := enr.WithEntry(attSubnetEnrKey, &bitV) 105 s.dv5Listener.LocalNode().Set(entry) 106 s.metaData = interfaces.WrappedMetadataV0(&pb.MetaDataV0{ 107 SeqNumber: s.metaData.SequenceNumber() + 1, 108 Attnets: bitV, 109 }) 110 } 111 112 // Initializes a bitvector of attestation subnets beacon nodes is subscribed to 113 // and creates a new ENR entry with its default value. 114 func intializeAttSubnets(node *enode.LocalNode) *enode.LocalNode { 115 bitV := bitfield.NewBitvector64() 116 entry := enr.WithEntry(attSubnetEnrKey, bitV.Bytes()) 117 node.Set(entry) 118 return node 119 } 120 121 // Reads the attestation subnets entry from a node's ENR and determines 122 // the committee indices of the attestation subnets the node is subscribed to. 123 func attSubnets(record *enr.Record) ([]uint64, error) { 124 bitV, err := bitvector(record) 125 if err != nil { 126 return nil, err 127 } 128 var committeeIdxs []uint64 129 for i := uint64(0); i < attestationSubnetCount; i++ { 130 if bitV.BitAt(i) { 131 committeeIdxs = append(committeeIdxs, i) 132 } 133 } 134 return committeeIdxs, nil 135 } 136 137 // Parses the attestation subnets ENR entry in a node and extracts its value 138 // as a bitvector for further manipulation. 139 func bitvector(record *enr.Record) (bitfield.Bitvector64, error) { 140 bitV := bitfield.NewBitvector64() 141 entry := enr.WithEntry(attSubnetEnrKey, &bitV) 142 err := record.Load(entry) 143 if err != nil { 144 return nil, err 145 } 146 return bitV, nil 147 } 148 149 func (s *Service) subnetLocker(i uint64) *sync.RWMutex { 150 s.subnetsLockLock.Lock() 151 defer s.subnetsLockLock.Unlock() 152 l, ok := s.subnetsLock[i] 153 if !ok { 154 l = &sync.RWMutex{} 155 s.subnetsLock[i] = l 156 } 157 return l 158 }