github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/upstream/upstream_connector.go (about) 1 package upstream 2 3 import ( 4 "context" 5 "sync" 6 "time" 7 8 "github.com/onflow/flow-go/network/p2p" 9 10 "github.com/rs/zerolog" 11 "github.com/sethvargo/go-retry" 12 "go.uber.org/atomic" 13 14 "github.com/onflow/flow-go/network/p2p/utils" 15 16 "github.com/onflow/flow-go/model/flow" 17 "github.com/onflow/flow-go/module/lifecycle" 18 ) 19 20 // upstreamConnector tries to connect the unstaked AN with atleast one of the configured bootstrap access nodes 21 type upstreamConnector struct { 22 lm *lifecycle.LifecycleManager 23 bootstrapIdentities flow.IdentitySkeletonList 24 logger zerolog.Logger 25 unstakedNode p2p.LibP2PNode 26 cancel context.CancelFunc 27 retryInitialTimeout time.Duration 28 maxRetries uint64 29 } 30 31 func NewUpstreamConnector(bootstrapIdentities flow.IdentitySkeletonList, unstakedNode p2p.LibP2PNode, logger zerolog.Logger) *upstreamConnector { 32 return &upstreamConnector{ 33 lm: lifecycle.NewLifecycleManager(), 34 bootstrapIdentities: bootstrapIdentities, 35 unstakedNode: unstakedNode, 36 logger: logger, 37 retryInitialTimeout: time.Second, 38 maxRetries: 5, 39 } 40 } 41 func (connector *upstreamConnector) Ready() <-chan struct{} { 42 connector.lm.OnStart(func() { 43 // eventually, context will be passed in to Start method: https://github.com/dapperlabs/flow-go/issues/5730 44 ctx, cancel := context.WithCancel(context.TODO()) 45 connector.cancel = cancel 46 47 workerCtx, cancel := context.WithCancel(ctx) 48 defer cancel() 49 50 success := atomic.NewBool(false) 51 var wg sync.WaitGroup 52 53 // spawn a connect worker for each bootstrap node 54 for _, b := range connector.bootstrapIdentities { 55 id := *b 56 wg.Add(1) 57 go func() { 58 defer wg.Done() 59 lg := connector.logger.With().Str("bootstrap_node", id.NodeID.String()).Logger() 60 61 backoff := retry.NewFibonacci(connector.retryInitialTimeout) 62 backoff = retry.WithMaxRetries(connector.maxRetries, backoff) 63 64 if err := retry.Do(workerCtx, backoff, func(ctx context.Context) error { 65 return retry.RetryableError(connector.connect(ctx, id)) 66 }); err != nil { 67 lg.Err(err).Msg("failed to connect") 68 } else { 69 lg.Info().Msg("successfully connected to bootstrap node") 70 success.Store(true) 71 } 72 }() 73 } 74 75 wg.Wait() 76 77 if !success.Load() { 78 // log fatal as there is no point continuing further, the unstaked AN cannot connect to any of the bootstrap peers 79 connector.logger.Fatal(). 80 Msg("Failed to connect to a bootstrap node. " + 81 "Please ensure the network address and public key of the bootstrap access node are correct " + 82 "and that the node is running and reachable.") 83 } 84 }) 85 return connector.lm.Started() 86 } 87 88 // connect is run to connect to an boostrap peer 89 func (connector *upstreamConnector) connect(ctx context.Context, bootstrapPeer flow.IdentitySkeleton) error { 90 91 select { 92 // check for a cancelled/expired context 93 case <-ctx.Done(): 94 return ctx.Err() 95 default: 96 } 97 98 peerAddrInfo, err := utils.PeerAddressInfo(bootstrapPeer) 99 100 if err != nil { 101 return err 102 } 103 104 // try and connect to the bootstrap server 105 return connector.unstakedNode.ConnectToPeer(ctx, peerAddrInfo) 106 } 107 108 func (connector *upstreamConnector) Done() <-chan struct{} { 109 connector.lm.OnStop(func() { 110 // this function will only be executed if connector.lm.OnStart was previously called, 111 // in which case connector.cancel != nil 112 connector.cancel() 113 }) 114 return connector.lm.Stopped() 115 }