code: func (p *BlockPuller) probeEndpoints(minRequestedSequence uint64) *endpointInfoBucket {
  	endpointsInfo := make(chan *endpointInfo, len(p.Endpoints))
  
  	var wg sync.WaitGroup
  	wg.Add(len(p.Endpoints))
  
  	var forbiddenErr uint32
  	var unavailableErr uint32
  
  	for _, endpoint := range p.Endpoints {
  		go func(endpoint string) {
  			defer wg.Done()
  			ei, err := p.probeEndpoint(endpoint, minRequestedSequence)
  			if err != nil {
  				p.Logger.Warningf("Received error of type '%v' from %s", err, endpoint)
  				if err == ErrForbidden {
  					atomic.StoreUint32(&forbiddenErr, 1)
  				}
  				if err == ErrServiceUnavailable {
  					atomic.StoreUint32(&unavailableErr, 1)
  				}
  				return
  			}
  			endpointsInfo <- ei
  		}(endpoint)
  	}
  	wg.Wait()
  
  	close(endpointsInfo)
  	eib := &endpointInfoBucket{
  		bucket: endpointsInfo,
  		logger: p.Logger,
  	}
  
  	if unavailableErr == 1 && len(endpointsInfo) == 0 {
  		eib.err = ErrServiceUnavailable
  	}
  	if forbiddenErr == 1 && len(endpointsInfo) == 0 {
  		eib.err = ErrForbidden
  	}
  	return eib
  }
docs: // probeEndpoints reaches to all endpoints known and returns the latest block sequences
  // of the endpoints, as well as gRPC connections to them.