@Test
  public void testLastBlockSeenHash() throws Exception {
    // Test the lastBlockSeenHash field works.

    // LastBlockSeenHash should be empty if never set.
    Wallet wallet = new Wallet(params);
    Protos.Wallet walletProto = new WalletProtobufSerializer().walletToProto(wallet);
    ByteString lastSeenBlockHash = walletProto.getLastSeenBlockHash();
    assertTrue(lastSeenBlockHash.isEmpty());

    // Create a block.
    Block block = params.getDefaultSerializer().makeBlock(BlockTest.blockBytes);
    Sha256Hash blockHash = block.getHash();
    wallet.setLastBlockSeenHash(blockHash);
    wallet.setLastBlockSeenHeight(1);

    // Roundtrip the wallet and check it has stored the blockHash.
    Wallet wallet1 = roundTrip(wallet);
    assertEquals(blockHash, wallet1.getLastBlockSeenHash());
    assertEquals(1, wallet1.getLastBlockSeenHeight());

    // Test the Satoshi genesis block (hash of all zeroes) is roundtripped ok.
    Block genesisBlock = MainNetParams.get().getGenesisBlock();
    wallet.setLastBlockSeenHash(genesisBlock.getHash());
    Wallet wallet2 = roundTrip(wallet);
    assertEquals(genesisBlock.getHash(), wallet2.getLastBlockSeenHash());
  }
  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));
        }
      }
    }
  }
 @Test
 public void coinbaseTxns() throws Exception {
   // Covers issue 420 where the outpoint index of a coinbase tx input was being mis-serialized.
   Block b =
       params
           .getGenesisBlock()
           .createNextBlockWithCoinbase(
               Block.BLOCK_VERSION_GENESIS,
               myKey.getPubKey(),
               FIFTY_COINS,
               Block.BLOCK_HEIGHT_GENESIS);
   Transaction coinbase = b.getTransactions().get(0);
   assertTrue(coinbase.isCoinBase());
   BlockChain chain = new BlockChain(params, myWallet, new MemoryBlockStore(params));
   assertTrue(chain.add(b));
   // Wallet now has a coinbase tx in it.
   assertEquals(1, myWallet.getTransactions(true).size());
   assertTrue(myWallet.getTransaction(coinbase.getHash()).isCoinBase());
   Wallet wallet2 = roundTrip(myWallet);
   assertEquals(1, wallet2.getTransactions(true).size());
   assertTrue(wallet2.getTransaction(coinbase.getHash()).isCoinBase());
 }
  @Override
  public void onBlocksDownloaded(
      Peer peer, Block block, FilteredBlock filteredBlock, int blocksLeft) {
    super.onBlocksDownloaded(peer, block, filteredBlock, blocksLeft);
    // Log.i(TAG, "onBlocksDownloaded");

    size += block.getMessageSize();

    double pct = 100.0 - (100.0 * (blocksLeft / (double) originalBlocksLeft));
    if ((int) pct != lastPercent) {
      lastPercent = (int) pct;
      walletObservable.setPercSync(lastPercent);
      walletObservable.notifyObservers();
    }
  }
 @Override
 public void verifyBlockAddition(
     Block added,
     @Nullable List<Sha256Hash> filteredTxHashList,
     @Nullable Map<Sha256Hash, Transaction> filteredTxn) {
   if (network.permitsMasternodesLogic()
       && network.getSporkManager().isSporkActive(SporkManager.SPORK_3_INSTANTX_BLOCK_FILTERING)) {
     if (filteredTxHashList != null && filteredTxn != null) {
       List<Transaction> toCheck = Lists.newArrayListWithExpectedSize(filteredTxn.size());
       for (final Sha256Hash txHash : filteredTxHashList) {
         final Transaction tx = filteredTxn.get(txHash);
         if (tx != null) toCheck.add(tx);
       }
       if (toCheck.size() > 0) {
         checkTxs(toCheck, added.getHashAsString());
       }
     } else {
       final List<Transaction> transactions = added.getTransactions();
       if (transactions != null) {
         checkTxs(transactions, added.getHashAsString());
       }
     }
   }
 }
  private static void kimotoGravityWellCheck(
      StoredBlock prevBlock, Block added, BlockStore store, NetworkParameters params)
      throws BlockStoreException {
    final long blocksTargetSpacing = (long) (2.5 * 60); // 2.5 minutes
    int timeDaySeconds = 60 * 60 * 24;
    long pastSecondsMin = timeDaySeconds / 40;
    long pastSecondsMax = timeDaySeconds * 7;
    long pastBlocksMin = pastSecondsMin / blocksTargetSpacing;
    long pastBlocksMax = pastSecondsMax / blocksTargetSpacing;

    StoredBlock blockReading = prevBlock;

    long pastBlocksMass = 0;
    long pastRateActualSeconds = 0;
    long pastRateTargetSeconds = 0;
    double pastRateAdjustmentRatio = 1.0f;
    BigInteger pastDifficultyAverage = BigInteger.valueOf(0);
    BigInteger pastDifficultyAveragePrev = BigInteger.valueOf(0);
    double eventHorizonDeviation;
    double eventHorizonDeviationFast;
    double eventHorizonDeviationSlow;

    if (prevBlock == null
        || prevBlock.getHeight() == 0
        || (long) prevBlock.getHeight() < pastBlocksMin) {
      verifyDifficulty(prevBlock, added, params.getMaxTarget(), params);
      return;
    }

    final Block prevHeader = prevBlock.getHeader();
    long latestBlockTime = prevHeader.getTimeSeconds();

    for (int i = 1; blockReading.getHeight() > 0; i++) {
      if (pastBlocksMax > 0 && i > pastBlocksMax) {
        break;
      }
      pastBlocksMass++;

      if (i == 1) {
        pastDifficultyAverage = blockReading.getHeader().getDifficultyTargetAsInteger();
      } else {
        pastDifficultyAverage =
            (blockReading
                    .getHeader()
                    .getDifficultyTargetAsInteger()
                    .subtract(pastDifficultyAveragePrev))
                .divide(BigInteger.valueOf(i))
                .add(pastDifficultyAveragePrev);
      }
      pastDifficultyAveragePrev = pastDifficultyAverage;

      if (blockReading.getHeight() > 646120
          && latestBlockTime < blockReading.getHeader().getTimeSeconds()) {
        // eliminates the ability to go back in time
        latestBlockTime = blockReading.getHeader().getTimeSeconds();
      }

      pastRateActualSeconds =
          prevHeader.getTimeSeconds() - blockReading.getHeader().getTimeSeconds();
      pastRateTargetSeconds = blocksTargetSpacing * pastBlocksMass;
      if (blockReading.getHeight() > 646120) {
        // this should slow down the upward difficulty change
        if (pastRateActualSeconds < 5) {
          pastRateActualSeconds = 5;
        }
      } else {
        if (pastRateActualSeconds < 0) {
          pastRateActualSeconds = 0;
        }
      }
      if (pastRateActualSeconds != 0 && pastRateTargetSeconds != 0) {
        pastRateAdjustmentRatio = (double) pastRateTargetSeconds / pastRateActualSeconds;
      }
      eventHorizonDeviation = 1 + 0.7084 * Math.pow((double) pastBlocksMass / 28.2d, -1.228);
      eventHorizonDeviationFast = eventHorizonDeviation;
      eventHorizonDeviationSlow = 1 / eventHorizonDeviation;

      if (pastBlocksMass >= pastBlocksMin) {
        if (pastRateAdjustmentRatio <= eventHorizonDeviationSlow
            || pastRateAdjustmentRatio >= eventHorizonDeviationFast) {
          break;
        }
      }
      blockReading = store.get(blockReading.getHeader().getPrevBlockHash());
      if (blockReading == null) {
        return;
      }
    }

    BigInteger newDifficulty = pastDifficultyAverage;
    if (pastRateActualSeconds != 0 && pastRateTargetSeconds != 0) {
      newDifficulty = newDifficulty.multiply(BigInteger.valueOf(pastRateActualSeconds));
      newDifficulty = newDifficulty.divide(BigInteger.valueOf(pastRateTargetSeconds));
    }

    if (newDifficulty.compareTo(params.getMaxTarget()) > 0) {
      log.info("Difficulty hit proof of work limit: {}", newDifficulty.toString(16));
      newDifficulty = params.getMaxTarget();
    }

    verifyDifficulty(prevBlock, added, newDifficulty, params);
  }
  @Test
  public void testAppearedAtChainHeightDepthAndWorkDone() throws Exception {
    // Test the TransactionConfidence appearedAtChainHeight, depth and workDone field are stored.

    BlockChain chain = new BlockChain(params, myWallet, new MemoryBlockStore(params));

    final ArrayList<Transaction> txns = new ArrayList<Transaction>(2);
    myWallet.addEventListener(
        new AbstractWalletEventListener() {
          @Override
          public void onCoinsReceived(
              Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
            txns.add(tx);
          }
        });

    // Start by building two blocks on top of the genesis block.
    Block b1 = params.getGenesisBlock().createNextBlock(myAddress);
    BigInteger work1 = b1.getWork();
    assertTrue(work1.signum() > 0);

    Block b2 = b1.createNextBlock(myAddress);
    BigInteger work2 = b2.getWork();
    assertTrue(work2.signum() > 0);

    assertTrue(chain.add(b1));
    assertTrue(chain.add(b2));

    // We now have the following chain:
    //     genesis -> b1 -> b2

    // Check the transaction confidence levels are correct before wallet roundtrip.
    Threading.waitForUserCode();
    assertEquals(2, txns.size());

    TransactionConfidence confidence0 = txns.get(0).getConfidence();
    TransactionConfidence confidence1 = txns.get(1).getConfidence();

    assertEquals(1, confidence0.getAppearedAtChainHeight());
    assertEquals(2, confidence1.getAppearedAtChainHeight());

    assertEquals(2, confidence0.getDepthInBlocks());
    assertEquals(1, confidence1.getDepthInBlocks());

    // Roundtrip the wallet and check it has stored the depth and workDone.
    Wallet rebornWallet = roundTrip(myWallet);

    Set<Transaction> rebornTxns = rebornWallet.getTransactions(false);
    assertEquals(2, rebornTxns.size());

    // The transactions are not guaranteed to be in the same order so sort them to be in chain
    // height order if required.
    Iterator<Transaction> it = rebornTxns.iterator();
    Transaction txA = it.next();
    Transaction txB = it.next();

    Transaction rebornTx0, rebornTx1;
    if (txA.getConfidence().getAppearedAtChainHeight() == 1) {
      rebornTx0 = txA;
      rebornTx1 = txB;
    } else {
      rebornTx0 = txB;
      rebornTx1 = txA;
    }

    TransactionConfidence rebornConfidence0 = rebornTx0.getConfidence();
    TransactionConfidence rebornConfidence1 = rebornTx1.getConfidence();

    assertEquals(1, rebornConfidence0.getAppearedAtChainHeight());
    assertEquals(2, rebornConfidence1.getAppearedAtChainHeight());

    assertEquals(2, rebornConfidence0.getDepthInBlocks());
    assertEquals(1, rebornConfidence1.getDepthInBlocks());
  }