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  }