private static void verifyDifficulty(
      StoredBlock prevBlock, Block added, BigInteger calcDiff, NetworkParameters params) {
    if (calcDiff.compareTo(params.getMaxTarget()) > 0) {
      log.info("Difficulty hit proof of work limit: {}", calcDiff.toString(16));
      calcDiff = params.getMaxTarget();
    }

    int accuracyBytes = (int) (added.getDifficultyTarget() >>> 24) - 3;
    final BigInteger receivedDifficulty = added.getDifficultyTargetAsInteger();

    // The calculated difficulty is to a higher precision than received, so reduce here.
    final BigInteger mask = BigInteger.valueOf(0xFFFFFFL).shiftLeft(accuracyBytes * 8);
    calcDiff = calcDiff.and(mask);

    if (CoinDefinition.TEST_NETWORK_STANDARD.equals(params.getStandardNetworkId())) {
      if (calcDiff.compareTo(receivedDifficulty) != 0) {
        throw new VerificationException(
            "Network provided difficulty bits do not match what was calculated: "
                + receivedDifficulty.toString(16)
                + " vs "
                + calcDiff.toString(16));
      }
    } else {
      final int height = prevBlock.getHeight() + 1;
      if (height <= 68589) {
        long nBitsNext = added.getDifficultyTarget();

        long calcDiffBits = (accuracyBytes + 3) << 24;
        calcDiffBits |= calcDiff.shiftRight(accuracyBytes * 8).longValue();

        final double n1 = CommonUtils.convertBitsToDouble(calcDiffBits);
        final double n2 = CommonUtils.convertBitsToDouble(nBitsNext);

        if (Math.abs(n1 - n2) > n1 * 0.2) {
          throw new VerificationException(
              "Network provided difficulty bits do not match what was calculated: "
                  + receivedDifficulty.toString(16)
                  + " vs "
                  + calcDiff.toString(16));
        }
      } else {
        if (calcDiff.compareTo(receivedDifficulty) != 0) {
          throw new VerificationException(
              "Network provided difficulty bits do not match what was calculated: "
                  + receivedDifficulty.toString(16)
                  + " vs "
                  + calcDiff.toString(16));
        }
      }
    }
  }
  @Override
  public void verifyDifficultyTransitions(
      StoredBlock prevBlock, Block added, NetworkParameters params) {
    int diffMode = 1;
    final int heightInc = prevBlock.getHeight() + 1;
    if (CoinDefinition.TEST_NETWORK_STANDARD.equals(params.getStandardNetworkId())) {
      if (heightInc >= 2000) {
        diffMode = 4;
      }
    } else {
      if (heightInc >= 68589) {
        diffMode = 4;
      } else if (heightInc >= 34140) {
        diffMode = 3;
      } else if (heightInc >= 15200) {
        diffMode = 2;
      }
    }

    try {
      switch (diffMode) {
        case 4:
          darkGravityWave3Check(
              prevBlock, added, linearExtension.getBlockChain().getBlockStore(), params);
          return;
        case 1:
          linearExtension.verifyDifficultyTransitions(prevBlock, added, params);
          return;
        case 2:
          kimotoGravityWellCheck(
              prevBlock, added, linearExtension.getBlockChain().getBlockStore(), params);
          return;
        case 3:
          darkGravityWaveCheck(
              prevBlock, added, linearExtension.getBlockChain().getBlockStore(), params);
          return;
        default:
          throw new RuntimeException("Unreachable");
      }
    } catch (BlockStoreException ex) {
      throw new VerificationException(
          "Block store exception during difficulty transitions check", ex);
    }
  }