mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-25 04:47:18 +00:00
d077483577
* v3 import renamings * tidy * fmt * rev * Update beacon-chain/core/epoch/precompute/reward_penalty_test.go * Update beacon-chain/core/helpers/validators_test.go * Update beacon-chain/db/alias.go * Update beacon-chain/db/alias.go * Update beacon-chain/db/alias.go * Update beacon-chain/db/iface/BUILD.bazel * Update beacon-chain/db/kv/kv.go * Update beacon-chain/db/kv/state.go * Update beacon-chain/rpc/prysm/v1alpha1/validator/attester_test.go * Update beacon-chain/rpc/prysm/v1alpha1/validator/attester_test.go * Update beacon-chain/sync/initial-sync/service.go * fix deps * fix bad replacements * fix bad replacements * change back * gohashtree version * fix deps Co-authored-by: Nishant Das <nishdas93@gmail.com> Co-authored-by: Potuz <potuz@prysmaticlabs.com>
282 lines
9.2 KiB
Go
282 lines
9.2 KiB
Go
package p2p
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
"github.com/ethereum/go-ethereum/p2p/enr"
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/go-bitfield"
|
|
"github.com/prysmaticlabs/prysm/v3/cmd/beacon-chain/flags"
|
|
"github.com/prysmaticlabs/prysm/v3/consensus-types/wrapper"
|
|
mathutil "github.com/prysmaticlabs/prysm/v3/math"
|
|
"go.opencensus.io/trace"
|
|
|
|
"github.com/prysmaticlabs/prysm/v3/config/params"
|
|
pb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
|
)
|
|
|
|
var attestationSubnetCount = params.BeaconNetworkConfig().AttestationSubnetCount
|
|
var syncCommsSubnetCount = params.BeaconConfig().SyncCommitteeSubnetCount
|
|
|
|
var attSubnetEnrKey = params.BeaconNetworkConfig().AttSubnetKey
|
|
var syncCommsSubnetEnrKey = params.BeaconNetworkConfig().SyncCommsSubnetKey
|
|
|
|
// The value used with the subnet, inorder
|
|
// to create an appropriate key to retrieve
|
|
// the relevant lock. This is used to differentiate
|
|
// sync subnets from attestation subnets. This is deliberately
|
|
// chosen as more than 64(attestation subnet count).
|
|
const syncLockerVal = 100
|
|
|
|
// FindPeersWithSubnet performs a network search for peers
|
|
// subscribed to a particular subnet. Then we try to connect
|
|
// with those peers. This method will block until the required amount of
|
|
// peers are found, the method only exits in the event of context timeouts.
|
|
func (s *Service) FindPeersWithSubnet(ctx context.Context, topic string,
|
|
index uint64, threshold int) (bool, error) {
|
|
ctx, span := trace.StartSpan(ctx, "p2p.FindPeersWithSubnet")
|
|
defer span.End()
|
|
|
|
span.AddAttributes(trace.Int64Attribute("index", int64(index))) // lint:ignore uintcast -- It's safe to do this for tracing.
|
|
|
|
if s.dv5Listener == nil {
|
|
// return if discovery isn't set
|
|
return false, nil
|
|
}
|
|
|
|
topic += s.Encoding().ProtocolSuffix()
|
|
iterator := s.dv5Listener.RandomNodes()
|
|
switch {
|
|
case strings.Contains(topic, GossipAttestationMessage):
|
|
iterator = filterNodes(ctx, iterator, s.filterPeerForAttSubnet(index))
|
|
case strings.Contains(topic, GossipSyncCommitteeMessage):
|
|
iterator = filterNodes(ctx, iterator, s.filterPeerForSyncSubnet(index))
|
|
default:
|
|
return false, errors.New("no subnet exists for provided topic")
|
|
}
|
|
|
|
currNum := len(s.pubsub.ListPeers(topic))
|
|
wg := new(sync.WaitGroup)
|
|
for {
|
|
if err := ctx.Err(); err != nil {
|
|
return false, errors.Errorf("unable to find requisite number of peers for topic %s - "+
|
|
"only %d out of %d peers were able to be found", topic, currNum, threshold)
|
|
}
|
|
if currNum >= threshold {
|
|
break
|
|
}
|
|
nodes := enode.ReadNodes(iterator, int(params.BeaconNetworkConfig().MinimumPeersInSubnetSearch))
|
|
for _, node := range nodes {
|
|
info, _, err := convertToAddrInfo(node)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
wg.Add(1)
|
|
go func() {
|
|
if err := s.connectWithPeer(ctx, *info); err != nil {
|
|
log.WithError(err).Tracef("Could not connect with peer %s", info.String())
|
|
}
|
|
wg.Done()
|
|
}()
|
|
}
|
|
// Wait for all dials to be completed.
|
|
wg.Wait()
|
|
currNum = len(s.pubsub.ListPeers(topic))
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// returns a method with filters peers specifically for a particular attestation subnet.
|
|
func (s *Service) filterPeerForAttSubnet(index uint64) func(node *enode.Node) bool {
|
|
return func(node *enode.Node) bool {
|
|
if !s.filterPeer(node) {
|
|
return false
|
|
}
|
|
subnets, err := attSubnets(node.Record())
|
|
if err != nil {
|
|
return false
|
|
}
|
|
indExists := false
|
|
for _, comIdx := range subnets {
|
|
if comIdx == index {
|
|
indExists = true
|
|
break
|
|
}
|
|
}
|
|
return indExists
|
|
}
|
|
}
|
|
|
|
// returns a method with filters peers specifically for a particular sync subnet.
|
|
func (s *Service) filterPeerForSyncSubnet(index uint64) func(node *enode.Node) bool {
|
|
return func(node *enode.Node) bool {
|
|
if !s.filterPeer(node) {
|
|
return false
|
|
}
|
|
subnets, err := syncSubnets(node.Record())
|
|
if err != nil {
|
|
return false
|
|
}
|
|
indExists := false
|
|
for _, comIdx := range subnets {
|
|
if comIdx == index {
|
|
indExists = true
|
|
break
|
|
}
|
|
}
|
|
return indExists
|
|
}
|
|
}
|
|
|
|
// lower threshold to broadcast object compared to searching
|
|
// for a subnet. So that even in the event of poor peer
|
|
// connectivity, we can still broadcast an attestation.
|
|
func (s *Service) hasPeerWithSubnet(topic string) bool {
|
|
// In the event peer threshold is lower, we will choose the lower
|
|
// threshold.
|
|
minPeers := mathutil.Min(1, uint64(flags.Get().MinimumPeersPerSubnet))
|
|
return len(s.pubsub.ListPeers(topic+s.Encoding().ProtocolSuffix())) >= int(minPeers) // lint:ignore uintcast -- Min peers can be safely cast to int.
|
|
}
|
|
|
|
// Updates the service's discv5 listener record's attestation subnet
|
|
// with a new value for a bitfield of subnets tracked. It also updates
|
|
// the node's metadata by increasing the sequence number and the
|
|
// subnets tracked by the node.
|
|
func (s *Service) updateSubnetRecordWithMetadata(bitV bitfield.Bitvector64) {
|
|
entry := enr.WithEntry(attSubnetEnrKey, &bitV)
|
|
s.dv5Listener.LocalNode().Set(entry)
|
|
s.metaData = wrapper.WrappedMetadataV0(&pb.MetaDataV0{
|
|
SeqNumber: s.metaData.SequenceNumber() + 1,
|
|
Attnets: bitV,
|
|
})
|
|
}
|
|
|
|
// Updates the service's discv5 listener record's attestation subnet
|
|
// with a new value for a bitfield of subnets tracked. It also record's
|
|
// the sync committee subnet in the enr. It also updates the node's
|
|
// metadata by increasing the sequence number and the subnets tracked by the node.
|
|
func (s *Service) updateSubnetRecordWithMetadataV2(bitVAtt bitfield.Bitvector64, bitVSync bitfield.Bitvector4) {
|
|
entry := enr.WithEntry(attSubnetEnrKey, &bitVAtt)
|
|
subEntry := enr.WithEntry(syncCommsSubnetEnrKey, &bitVSync)
|
|
s.dv5Listener.LocalNode().Set(entry)
|
|
s.dv5Listener.LocalNode().Set(subEntry)
|
|
s.metaData = wrapper.WrappedMetadataV1(&pb.MetaDataV1{
|
|
SeqNumber: s.metaData.SequenceNumber() + 1,
|
|
Attnets: bitVAtt,
|
|
Syncnets: bitVSync,
|
|
})
|
|
}
|
|
|
|
// Initializes a bitvector of attestation subnets beacon nodes is subscribed to
|
|
// and creates a new ENR entry with its default value.
|
|
func initializeAttSubnets(node *enode.LocalNode) *enode.LocalNode {
|
|
bitV := bitfield.NewBitvector64()
|
|
entry := enr.WithEntry(attSubnetEnrKey, bitV.Bytes())
|
|
node.Set(entry)
|
|
return node
|
|
}
|
|
|
|
// Initializes a bitvector of sync committees subnets beacon nodes is subscribed to
|
|
// and creates a new ENR entry with its default value.
|
|
func initializeSyncCommSubnets(node *enode.LocalNode) *enode.LocalNode {
|
|
bitV := bitfield.Bitvector4{byte(0x00)}
|
|
entry := enr.WithEntry(syncCommsSubnetEnrKey, bitV.Bytes())
|
|
node.Set(entry)
|
|
return node
|
|
}
|
|
|
|
// Reads the attestation subnets entry from a node's ENR and determines
|
|
// the committee indices of the attestation subnets the node is subscribed to.
|
|
func attSubnets(record *enr.Record) ([]uint64, error) {
|
|
bitV, err := attBitvector(record)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// lint:ignore uintcast -- subnet count can be safely cast to int.
|
|
if len(bitV) != byteCount(int(attestationSubnetCount)) {
|
|
return []uint64{}, errors.Errorf("invalid bitvector provided, it has a size of %d", len(bitV))
|
|
}
|
|
var committeeIdxs []uint64
|
|
for i := uint64(0); i < attestationSubnetCount; i++ {
|
|
if bitV.BitAt(i) {
|
|
committeeIdxs = append(committeeIdxs, i)
|
|
}
|
|
}
|
|
return committeeIdxs, nil
|
|
}
|
|
|
|
// Reads the sync subnets entry from a node's ENR and determines
|
|
// the committee indices of the sync subnets the node is subscribed to.
|
|
func syncSubnets(record *enr.Record) ([]uint64, error) {
|
|
bitV, err := syncBitvector(record)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// lint:ignore uintcast -- subnet count can be safely cast to int.
|
|
if len(bitV) != byteCount(int(syncCommsSubnetCount)) {
|
|
return []uint64{}, errors.Errorf("invalid bitvector provided, it has a size of %d", len(bitV))
|
|
}
|
|
var committeeIdxs []uint64
|
|
for i := uint64(0); i < syncCommsSubnetCount; i++ {
|
|
if bitV.BitAt(i) {
|
|
committeeIdxs = append(committeeIdxs, i)
|
|
}
|
|
}
|
|
return committeeIdxs, nil
|
|
}
|
|
|
|
// Parses the attestation subnets ENR entry in a node and extracts its value
|
|
// as a bitvector for further manipulation.
|
|
func attBitvector(record *enr.Record) (bitfield.Bitvector64, error) {
|
|
bitV := bitfield.NewBitvector64()
|
|
entry := enr.WithEntry(attSubnetEnrKey, &bitV)
|
|
err := record.Load(entry)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bitV, nil
|
|
}
|
|
|
|
// Parses the attestation subnets ENR entry in a node and extracts its value
|
|
// as a bitvector for further manipulation.
|
|
func syncBitvector(record *enr.Record) (bitfield.Bitvector4, error) {
|
|
bitV := bitfield.Bitvector4{byte(0x00)}
|
|
entry := enr.WithEntry(syncCommsSubnetEnrKey, &bitV)
|
|
err := record.Load(entry)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bitV, nil
|
|
}
|
|
|
|
// The subnet locker is a map which keeps track of all
|
|
// mutexes stored per subnet. This locker is re-used
|
|
// between both the attestation and sync subnets. In
|
|
// order to differentiate between attestation and sync
|
|
// subnets. Sync subnets are stored by (subnet+syncLockerVal). This
|
|
// is to prevent conflicts while allowing both subnets
|
|
// to use a single locker.
|
|
func (s *Service) subnetLocker(i uint64) *sync.RWMutex {
|
|
s.subnetsLockLock.Lock()
|
|
defer s.subnetsLockLock.Unlock()
|
|
l, ok := s.subnetsLock[i]
|
|
if !ok {
|
|
l = &sync.RWMutex{}
|
|
s.subnetsLock[i] = l
|
|
}
|
|
return l
|
|
}
|
|
|
|
// Determines the number of bytes that are used
|
|
// to represent the provided number of bits.
|
|
func byteCount(bitCount int) int {
|
|
numOfBytes := bitCount / 8
|
|
if bitCount%8 != 0 {
|
|
numOfBytes++
|
|
}
|
|
return numOfBytes
|
|
}
|