class SyncSourceBatch implements Batch {

  @Override
  public void requestMoreBlocks(final Runnable callback) {
    checkState(
        !isComplete() || isContested(), "Attempting to request more blocks from a complete batch");
    final RequestHandler requestHandler = new RequestHandler();
    final UInt64 startSlot =
        getLastBlock().map(SignedBeaconBlock::getSlot).map(UInt64::increment).orElse(firstSlot);
    final UInt64 remainingSlots = count.minus(startSlot.minus(firstSlot));
    checkState(
        remainingSlots.isGreaterThan(UInt64.ZERO),
        "Attempting to request more blocks when block for last slot already present.");
    if (currentSyncSource.isEmpty()) {
      currentSyncSource = syncSourceProvider.selectSource();
      if (currentSyncSource.isEmpty()) {
        eventThread.executeLater(callback);
        return;
      }
    }
    awaitingBlocks = true;
    final SyncSource syncSource = currentSyncSource.orElseThrow();
    LOG.debug(
        "Requesting {} slots starting at {} from peer {}", remainingSlots, startSlot, syncSource);
    syncSource
        .requestBlocksByRange(startSlot, remainingSlots, UInt64.ONE, requestHandler)
        .thenRunAsync(() -> onRequestComplete(requestHandler), eventThread)
        .handleAsync(
            (__, error) -> {
              if (error != null) {
                handleRequestErrors(error);
              }
              // Ensure there is time for other events to be processed before the callback completes
              // Allows external events like peers disconnecting to be processed before retrying
              eventThread.executeLater(callback);
              return null;
            },
            eventThread)
        .reportExceptions();
  }

    SyncSourceBatch(
      final EventThread eventThread,
      final SyncSourceSelector syncSourceProvider,
      final ConflictResolutionStrategy conflictResolutionStrategy,
      final TargetChain targetChain,
      final UInt64 firstSlot,
      final UInt64 count);

}