github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/connection/connector_factory.go (about)

     1  package connection
     2  
     3  import (
     4  	"crypto/rand"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/libp2p/go-libp2p/core/host"
     9  	discoveryBackoff "github.com/libp2p/go-libp2p/p2p/discovery/backoff"
    10  	"github.com/onflow/crypto/random"
    11  
    12  	"github.com/onflow/flow-go/network/p2p"
    13  )
    14  
    15  const (
    16  	// minBackoff is the minimum backoff duration for the backoff connector.
    17  	// We set it to 1 second as we want to let the LibP2PNode be in charge of connection establishment and can disconnect
    18  	// and reconnect to peers as soon as it needs. This is essential to ensure that the allow-listing and disallow-listing
    19  	// time intervals are working as expected.
    20  	minBackoff = 1 * time.Second
    21  	// maxBackoff is the maximum backoff duration for the backoff connector. When the backoff duration reaches this value,
    22  	// it will not increase any further.
    23  	maxBackoff = time.Hour
    24  	// timeUnit is the time unit for the backoff duration. The backoff duration will be a multiple of this value.
    25  	// As we use an exponential backoff, the backoff duration will be a multiple of this value multiplied by the exponential
    26  	// base raised to the exponential offset.
    27  	timeUnit = time.Second
    28  	// exponentialBackOffBase is the base for the exponential backoff. The backoff duration will be a multiple of the time unit
    29  	// multiplied by the exponential base raised to the exponential offset, i.e., exponentialBase^(timeUnit*attempt).
    30  	exponentialBackOffBase = 2.0
    31  	// exponentialBackOffOffset is the offset for the exponential backoff. It acts as a constant that is added result
    32  	// of the exponential base raised to the exponential offset, i.e., exponentialBase^(timeUnit*attempt) + exponentialBackOffOffset.
    33  	// This is used to ensure that the backoff duration is always greater than the time unit. We set this to 0 as we want the
    34  	// backoff duration to be a multiple of the time unit.
    35  	exponentialBackOffOffset = 0
    36  )
    37  
    38  // DefaultLibp2pBackoffConnectorFactory is a factory function to create a new BackoffConnector. It uses the default
    39  // values for the backoff connector.
    40  // (https://github.com/libp2p/go-libp2p-pubsub/blob/master/discovery.go#L34)
    41  func DefaultLibp2pBackoffConnectorFactory() p2p.ConnectorFactory {
    42  	return func(host host.Host) (p2p.Connector, error) {
    43  		rngSrc, err := newSource()
    44  		if err != nil {
    45  			return nil, fmt.Errorf("failed to generate a random source: %w", err)
    46  		}
    47  
    48  		cacheSize := 100
    49  		dialTimeout := time.Minute * 2
    50  		backoff := discoveryBackoff.NewExponentialBackoff(
    51  			minBackoff,
    52  			maxBackoff,
    53  			discoveryBackoff.FullJitter,
    54  			timeUnit,
    55  			exponentialBackOffBase,
    56  			exponentialBackOffOffset,
    57  			rngSrc,
    58  		)
    59  		backoffConnector, err := discoveryBackoff.NewBackoffConnector(host, cacheSize, dialTimeout, backoff)
    60  		if err != nil {
    61  			return nil, fmt.Errorf("failed to create backoff connector: %w", err)
    62  		}
    63  		return backoffConnector, nil
    64  	}
    65  }
    66  
    67  // `source` implements math/rand.Source so it can be used
    68  // by libp2p's `NewExponentialBackoff`.
    69  // It is backed by a more secure randomness than math/rand's `NewSource`.
    70  // `source` is only implemented to avoid using math/rand's `NewSource`.
    71  type source struct {
    72  	prg random.Rand
    73  }
    74  
    75  // Seed is not used by the backoff object from `NewExponentialBackoff`
    76  func (src *source) Seed(seed int64) {}
    77  
    78  // Int63 is used by `NewExponentialBackoff` and is based on a crypto PRG
    79  func (src *source) Int63() int64 {
    80  	return int64(src.prg.UintN(1 << 63))
    81  }
    82  
    83  // creates a source using a crypto PRG and secure random seed
    84  // returned errors:
    85  //   - exception error if the system randomness fails (the system and other components would
    86  //     have many other issues if this happens)
    87  //   - exception error if the CSPRG (Chacha20) isn't initialized properly (should not happen in normal
    88  //     operations)
    89  func newSource() (*source, error) {
    90  	seed := make([]byte, random.Chacha20SeedLen)
    91  	_, err := rand.Read(seed) // checking err only is enough
    92  	if err != nil {
    93  		return nil, fmt.Errorf("failed to generate a seed: %w", err)
    94  	}
    95  	prg, err := random.NewChacha20PRG(seed, nil)
    96  	if err != nil {
    97  		// should not happen in normal operations because `seed` has the correct length
    98  		return nil, fmt.Errorf("failed to generate a PRG: %w", err)
    99  	}
   100  	return &source{prg}, nil
   101  }