@Test
  public void extensions() throws Exception {
    myWallet.addExtension(new FooWalletExtension("com.whatever.required", true));
    Protos.Wallet proto = new WalletProtobufSerializer().walletToProto(myWallet);
    // Initial extension is mandatory: try to read it back into a wallet that doesn't know about it.
    try {
      new WalletProtobufSerializer().readWallet(params, null, proto);
      fail();
    } catch (UnreadableWalletException e) {
      assertTrue(e.getMessage().contains("mandatory"));
    }
    Wallet wallet =
        new WalletProtobufSerializer()
            .readWallet(
                params,
                new WalletExtension[] {new FooWalletExtension("com.whatever.required", true)},
                proto);
    assertTrue(wallet.getExtensions().containsKey("com.whatever.required"));

    // Non-mandatory extensions are ignored if the wallet doesn't know how to read them.
    Wallet wallet2 = new Wallet(params);
    wallet2.addExtension(new FooWalletExtension("com.whatever.optional", false));
    Protos.Wallet proto2 = new WalletProtobufSerializer().walletToProto(wallet2);
    Wallet wallet5 = new WalletProtobufSerializer().readWallet(params, null, proto2);
    assertEquals(0, wallet5.getExtensions().size());
  }
 @Test
 public void fourPeers() throws Exception {
   InboundMessageQueuer[] channels = {
     connectPeer(1), connectPeer(2), connectPeer(3), connectPeer(4)
   };
   Transaction tx = new Transaction(params);
   TransactionBroadcast broadcast = new TransactionBroadcast(peerGroup, tx);
   ListenableFuture<Transaction> future = broadcast.broadcast();
   assertFalse(future.isDone());
   // We expect two peers to receive a tx message, and at least one of the others must announce for
   // the future to
   // complete successfully.
   Message[] messages = {
     (Message) outbound(channels[0]),
     (Message) outbound(channels[1]),
     (Message) outbound(channels[2]),
     (Message) outbound(channels[3])
   };
   // 0 and 3 are randomly selected to receive the broadcast.
   assertEquals(tx, messages[0]);
   assertEquals(tx, messages[3]);
   assertNull(messages[1]);
   assertNull(messages[2]);
   Threading.waitForUserCode();
   assertFalse(future.isDone());
   inbound(channels[1], InventoryMessage.with(tx));
   pingAndWait(channels[1]);
   Threading.waitForUserCode();
   assertTrue(future.isDone());
 }
  @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 Wallet roundTrip(Wallet wallet) throws Exception {
   ByteArrayOutputStream output = new ByteArrayOutputStream();
   new WalletProtobufSerializer().writeWallet(wallet, output);
   ByteArrayInputStream test = new ByteArrayInputStream(output.toByteArray());
   assertTrue(WalletProtobufSerializer.isWallet(test));
   ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
   return new WalletProtobufSerializer().readWallet(input);
 }
 @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());
 }
  @Test
  public void peerGroupWalletIntegration() throws Exception {
    // Make sure we can create spends, and that they are announced. Then do the same with offline
    // mode.

    // Set up connections and block chain.
    VersionMessage ver = new VersionMessage(params, 2);
    ver.localServices = VersionMessage.NODE_NETWORK;
    InboundMessageQueuer p1 = connectPeer(1, ver);
    InboundMessageQueuer p2 = connectPeer(2);

    // Send ourselves a bit of money.
    Block b1 = FakeTxBuilder.makeSolvedTestBlock(blockStore, address);
    inbound(p1, b1);
    pingAndWait(p1);
    assertNull(outbound(p1));
    assertEquals(FIFTY_COINS, wallet.getBalance());

    // Check that the wallet informs us of changes in confidence as the transaction ripples across
    // the network.
    final Transaction[] transactions = new Transaction[1];
    wallet.addEventListener(
        new AbstractWalletEventListener() {
          @Override
          public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) {
            transactions[0] = tx;
          }
        });

    // Now create a spend, and expect the announcement on p1.
    Address dest = new ECKey().toAddress(params);
    Wallet.SendResult sendResult = wallet.sendCoins(peerGroup, dest, COIN);
    assertNotNull(sendResult.tx);
    Threading.waitForUserCode();
    assertFalse(sendResult.broadcastComplete.isDone());
    assertEquals(transactions[0], sendResult.tx);
    assertEquals(0, transactions[0].getConfidence().numBroadcastPeers());
    transactions[0] = null;
    Transaction t1;
    {
      peerGroup.waitForJobQueue();
      Message m = outbound(p1);
      // Hack: bloom filters are recalculated asynchronously to sending transactions to avoid lock
      // inversion, so we might or might not get the filter/mempool message first or second.
      while (!(m instanceof Transaction)) m = outbound(p1);
      t1 = (Transaction) m;
    }
    assertNotNull(t1);
    // 49 BTC in change.
    assertEquals(valueOf(49, 0), t1.getValueSentToMe(wallet));
    // The future won't complete until it's heard back from the network on p2.
    InventoryMessage inv = new InventoryMessage(params);
    inv.addTransaction(t1);
    inbound(p2, inv);
    pingAndWait(p2);
    Threading.waitForUserCode();
    assertTrue(sendResult.broadcastComplete.isDone());
    assertEquals(transactions[0], sendResult.tx);
    assertEquals(1, transactions[0].getConfidence().numBroadcastPeers());
    // Confirm it.
    Block b2 = FakeTxBuilder.createFakeBlock(blockStore, t1).block;
    inbound(p1, b2);
    pingAndWait(p1);
    assertNull(outbound(p1));

    // Do the same thing with an offline transaction.
    peerGroup.removeWallet(wallet);
    Wallet.SendRequest req = Wallet.SendRequest.to(dest, valueOf(2, 0));
    req.ensureMinRequiredFee = false;
    Transaction t3 = checkNotNull(wallet.sendCoinsOffline(req));
    assertNull(outbound(p1)); // Nothing sent.
    // Add the wallet to the peer group (simulate initialization). Transactions should be announced.
    peerGroup.addWallet(wallet);
    // Transaction announced to the first peer. No extra Bloom filter because no change address was
    // needed.
    assertEquals(t3.getHash(), ((Transaction) outbound(p1)).getHash());
  }
  @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());
  }