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 }