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

     1  package p2p
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"math"
     7  	"time"
     8  
     9  	"github.com/ethereum/go-ethereum/p2p/enode"
    10  	"github.com/ethereum/go-ethereum/p2p/enr"
    11  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    12  	pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    13  	"github.com/prysmaticlabs/prysm/shared/p2putils"
    14  	"github.com/prysmaticlabs/prysm/shared/params"
    15  	"github.com/prysmaticlabs/prysm/shared/timeutils"
    16  	"github.com/sirupsen/logrus"
    17  )
    18  
    19  // ENR key used for Ethereum consensus-related fork data.
    20  var eth2ENRKey = params.BeaconNetworkConfig().ETH2Key
    21  
    22  // ForkDigest returns the current fork digest of
    23  // the node.
    24  func (s *Service) forkDigest() ([4]byte, error) {
    25  	if s.currentForkDigest != [4]byte{} {
    26  		return s.currentForkDigest, nil
    27  	}
    28  	fd, err := p2putils.CreateForkDigest(s.genesisTime, s.genesisValidatorsRoot)
    29  	if err != nil {
    30  		s.currentForkDigest = fd
    31  	}
    32  	return fd, err
    33  }
    34  
    35  // Compares fork ENRs between an incoming peer's record and our node's
    36  // local record values for current and next fork version/epoch.
    37  func (s *Service) compareForkENR(record *enr.Record) error {
    38  	currentRecord := s.dv5Listener.LocalNode().Node().Record()
    39  	peerForkENR, err := forkEntry(record)
    40  	if err != nil {
    41  		return err
    42  	}
    43  	currentForkENR, err := forkEntry(currentRecord)
    44  	if err != nil {
    45  		return err
    46  	}
    47  	enrString, err := SerializeENR(record)
    48  	if err != nil {
    49  		return err
    50  	}
    51  	// Clients SHOULD connect to peers with current_fork_digest, next_fork_version,
    52  	// and next_fork_epoch that match local values.
    53  	if !bytes.Equal(peerForkENR.CurrentForkDigest, currentForkENR.CurrentForkDigest) {
    54  		return fmt.Errorf(
    55  			"fork digest of peer with ENR %s: %v, does not match local value: %v",
    56  			enrString,
    57  			peerForkENR.CurrentForkDigest,
    58  			currentForkENR.CurrentForkDigest,
    59  		)
    60  	}
    61  	// Clients MAY connect to peers with the same current_fork_version but a
    62  	// different next_fork_version/next_fork_epoch. Unless ENRForkID is manually
    63  	// updated to matching prior to the earlier next_fork_epoch of the two clients,
    64  	// these type of connecting clients will be unable to successfully interact
    65  	// starting at the earlier next_fork_epoch.
    66  	if peerForkENR.NextForkEpoch != currentForkENR.NextForkEpoch {
    67  		log.WithFields(logrus.Fields{
    68  			"peerNextForkEpoch": peerForkENR.NextForkEpoch,
    69  			"peerENR":           enrString,
    70  		}).Debug("Peer matches fork digest but has different next fork epoch")
    71  	}
    72  	if !bytes.Equal(peerForkENR.NextForkVersion, currentForkENR.NextForkVersion) {
    73  		log.WithFields(logrus.Fields{
    74  			"peerNextForkVersion": peerForkENR.NextForkVersion,
    75  			"peerENR":             enrString,
    76  		}).Debug("Peer matches fork digest but has different next fork version")
    77  	}
    78  	return nil
    79  }
    80  
    81  // Adds a fork entry as an ENR record under the Ethereum consensus EnrKey for
    82  // the local node. The fork entry is an ssz-encoded enrForkID type
    83  // which takes into account the current fork version from the current
    84  // epoch to create a fork digest, the next fork version,
    85  // and the next fork epoch.
    86  func addForkEntry(
    87  	node *enode.LocalNode,
    88  	genesisTime time.Time,
    89  	genesisValidatorsRoot []byte,
    90  ) (*enode.LocalNode, error) {
    91  	digest, err := p2putils.CreateForkDigest(genesisTime, genesisValidatorsRoot)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	currentSlot := helpers.SlotsSince(genesisTime)
    96  	currentEpoch := helpers.SlotToEpoch(currentSlot)
    97  	if timeutils.Now().Before(genesisTime) {
    98  		currentEpoch = 0
    99  	}
   100  	fork, err := p2putils.Fork(currentEpoch)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	nextForkEpoch := params.BeaconConfig().NextForkEpoch
   106  	nextForkVersion := params.BeaconConfig().NextForkVersion
   107  	// Set to the current fork version if our next fork is not planned.
   108  	if nextForkEpoch == math.MaxUint64 {
   109  		nextForkVersion = fork.CurrentVersion
   110  	}
   111  	enrForkID := &pb.ENRForkID{
   112  		CurrentForkDigest: digest[:],
   113  		NextForkVersion:   nextForkVersion,
   114  		NextForkEpoch:     nextForkEpoch,
   115  	}
   116  	enc, err := enrForkID.MarshalSSZ()
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	forkEntry := enr.WithEntry(eth2ENRKey, enc)
   121  	node.Set(forkEntry)
   122  	return node, nil
   123  }
   124  
   125  // Retrieves an enrForkID from an ENR record by key lookup
   126  // under the Ethereum consensus EnrKey
   127  func forkEntry(record *enr.Record) (*pb.ENRForkID, error) {
   128  	sszEncodedForkEntry := make([]byte, 16)
   129  	entry := enr.WithEntry(eth2ENRKey, &sszEncodedForkEntry)
   130  	err := record.Load(entry)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	forkEntry := &pb.ENRForkID{}
   135  	if err := forkEntry.UnmarshalSSZ(sszEncodedForkEntry); err != nil {
   136  		return nil, err
   137  	}
   138  	return forkEntry, nil
   139  }