class SignedAggregateAndProofValidator {

  public SafeFuture<InternalValidationResult> validate(final ValidateableAttestation attestation) {
    final SignedAggregateAndProof signedAggregate = attestation.getSignedAggregateAndProof();
    final AggregateAndProof aggregateAndProof = signedAggregate.getMessage();
    final Attestation aggregate = aggregateAndProof.getAggregate();

    final UInt64 aggregateSlot = aggregate.getData().getSlot();
    final AggregatorIndexAndEpoch aggregatorIndexAndEpoch =
        new AggregatorIndexAndEpoch(
            aggregateAndProof.getIndex(), compute_epoch_at_slot(aggregateSlot));
    if (receivedAggregatorIndexAndEpochs.contains(aggregatorIndexAndEpoch)) {
      LOG.trace("Ignoring duplicate aggregate");
      return SafeFuture.completedFuture(IGNORE);
    }

    if (receivedValidAggregations.contains(attestation.hash_tree_root())) {
      LOG.trace("Ignoring duplicate aggregate based on hash tree root");
      return SafeFuture.completedFuture(IGNORE);
    }

    return attestationValidator
        .singleOrAggregateAttestationChecks(attestation, OptionalInt.empty())
        .thenCompose(
            aggregateInternalValidationResult -> {
              if (aggregateInternalValidationResult == REJECT
                  || aggregateInternalValidationResult == IGNORE) {
                LOG.trace("Rejecting aggregate because attestation failed validation");
                return SafeFuture.completedFuture(aggregateInternalValidationResult);
              }

              return recentChainData
                  .retrieveBlockState(aggregate.getData().getBeacon_block_root())
                  .thenCompose(
                      maybeState ->
                          maybeState.isEmpty()
                              ? SafeFuture.completedFuture(Optional.empty())
                              : attestationValidator.resolveStateForAttestation(
                                  aggregate, maybeState.get()))
                  .thenApply(
                      maybeState -> {
                        if (maybeState.isEmpty()) {
                          return SAVE_FOR_FUTURE;
                        }

                        final BeaconState state = maybeState.get();

                        final Optional<BLSPublicKey> aggregatorPublicKey =
                            ValidatorsUtil.getValidatorPubKey(state, aggregateAndProof.getIndex());
                        if (aggregatorPublicKey.isEmpty()) {
                          LOG.trace("Rejecting aggregate with invalid index");
                          return REJECT;
                        }

                        if (!isSelectionProofValid(
                            aggregateSlot,
                            state,
                            aggregatorPublicKey.get(),
                            aggregateAndProof.getSelection_proof())) {
                          LOG.trace("Rejecting aggregate with incorrect selection proof");
                          return REJECT;
                        }

                        final List<Integer> beaconCommittee =
                            CommitteeUtil.get_beacon_committee(
                                state, aggregateSlot, aggregate.getData().getIndex());

                        final int aggregatorModulo = getAggregatorModulo(beaconCommittee.size());
                        if (!isAggregator(
                            aggregateAndProof.getSelection_proof(), aggregatorModulo)) {
                          LOG.trace(
                              "Rejecting aggregate because selection proof does not select validator as aggregator");
                          return REJECT;
                        }
                        if (!beaconCommittee.contains(
                            toIntExact(aggregateAndProof.getIndex().longValue()))) {
                          LOG.trace(
                              "Rejecting aggregate because attester is not in committee. Should have been one of {}",
                              beaconCommittee);
                          return REJECT;
                        }

                        if (!isSignatureValid(signedAggregate, state, aggregatorPublicKey.get())) {
                          LOG.trace("Rejecting aggregate with invalid signature");
                          return REJECT;
                        }

                        if (!receivedAggregatorIndexAndEpochs.add(aggregatorIndexAndEpoch)) {
                          LOG.trace("Ignoring duplicate aggregate");
                          return IGNORE;
                        }

                        if (!receivedValidAggregations.add(attestation.hash_tree_root())) {
                          LOG.trace("Ignoring duplicate aggregate based on hash tree root");
                          return IGNORE;
                        }

                        return aggregateInternalValidationResult;
                      });
            });
  }

  public  SignedAggregateAndProofValidator(
      final RecentChainData recentChainData, final AttestationValidator attestationValidator);

}