@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());
  }
 @Test
 public void tags() throws Exception {
   myWallet.setTag("foo", ByteString.copyFromUtf8("bar"));
   assertEquals("bar", myWallet.getTag("foo").toStringUtf8());
   myWallet = roundTrip(myWallet);
   assertEquals("bar", myWallet.getTag("foo").toStringUtf8());
 }
 @Test
 public void testRoundTripNormalWallet() throws Exception {
   Wallet wallet1 = roundTrip(myWallet);
   assertEquals(0, wallet1.getTransactions(true).size());
   assertEquals(Coin.ZERO, wallet1.getBalance());
   assertArrayEquals(
       myKey.getPubKey(), wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPubKey());
   assertArrayEquals(
       myKey.getPrivKeyBytes(),
       wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPrivKeyBytes());
   assertEquals(
       myKey.getCreationTimeSeconds(),
       wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getCreationTimeSeconds());
 }
  @Test
  public void extensionsWithError() throws Exception {
    WalletExtension extension =
        new WalletExtension() {
          @Override
          public String getWalletExtensionID() {
            return "test";
          }

          @Override
          public boolean isWalletExtensionMandatory() {
            return false;
          }

          @Override
          public byte[] serializeWalletExtension() {
            return new byte[0];
          }

          @Override
          public void deserializeWalletExtension(Wallet containingWallet, byte[] data)
              throws Exception {
            throw new NullPointerException(); // Something went wrong!
          }
        };
    myWallet.addExtension(extension);
    Protos.Wallet proto = new WalletProtobufSerializer().walletToProto(myWallet);
    Wallet wallet =
        new WalletProtobufSerializer().readWallet(params, new WalletExtension[] {extension}, proto);
    assertEquals(0, wallet.getExtensions().size());
  }
  @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 doubleSpend() throws Exception {
    // Check that we can serialize double spends correctly, as this is a slightly tricky case.
    FakeTxBuilder.DoubleSpends doubleSpends =
        FakeTxBuilder.createFakeDoubleSpendTxns(params, myAddress);
    // t1 spends to our wallet.
    myWallet.receivePending(doubleSpends.t1, null);
    // t2 rolls back t1 and spends somewhere else.
    myWallet.receiveFromBlock(doubleSpends.t2, null, BlockChain.NewBlockType.BEST_CHAIN, 0);
    Wallet wallet1 = roundTrip(myWallet);
    assertEquals(1, wallet1.getTransactions(true).size());
    Transaction t1 = wallet1.getTransaction(doubleSpends.t1.getHash());
    assertEquals(ConfidenceType.DEAD, t1.getConfidence().getConfidenceType());
    assertEquals(Coin.ZERO, wallet1.getBalance());

    // TODO: Wallet should store overriding transactions even if they are not wallet-relevant.
    // assertEquals(doubleSpends.t2, t1.getConfidence().getOverridingTransaction());
  }
 @Test
 public void raiseFeeTx() throws Exception {
   // Check basic tx serialization.
   Coin v1 = COIN;
   Transaction t1 = createFakeTx(params, v1, myAddress);
   t1.setPurpose(Purpose.RAISE_FEE);
   myWallet.receivePending(t1, null);
   Wallet wallet1 = roundTrip(myWallet);
   Transaction t1copy = wallet1.getTransaction(t1.getHash());
   assertEquals(Purpose.RAISE_FEE, t1copy.getPurpose());
 }
 @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 retryFailedBroadcast() throws Exception {
    // If we create a spend, it's sent to a peer that swallows it, and the peergroup is
    // removed/re-added then
    // the tx should be broadcast again.
    InboundMessageQueuer p1 = connectPeer(1);
    connectPeer(2);

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

    // Now create a spend, and expect the announcement on p1.
    Address dest = new ECKey().toAddress(params);
    Wallet.SendResult sendResult = wallet.sendCoins(peerGroup, dest, COIN);
    assertFalse(sendResult.broadcastComplete.isDone());
    Transaction t1;
    {
      Message m;
      while (!((m = outbound(p1)) instanceof Transaction)) ;
      t1 = (Transaction) m;
    }
    assertFalse(sendResult.broadcastComplete.isDone());

    // p1 eats it :( A bit later the PeerGroup is taken down.
    peerGroup.removeWallet(wallet);
    peerGroup.addWallet(wallet);

    // We want to hear about it again. Now, because we've disabled the randomness for the unit tests
    // it will
    // re-appear on p1 again. Of course in the real world it would end up with a different set of
    // peers and
    // select randomly so we get a second chance.
    Transaction t2 = (Transaction) outbound(p1);
    assertEquals(t1, t2);
  }
  @Test
  public void testRoundTripMarriedWallet() throws Exception {
    // create 2-of-2 married wallet
    myWallet = new Wallet(params);
    final DeterministicKeyChain partnerChain = new DeterministicKeyChain(new SecureRandom());
    DeterministicKey partnerKey =
        DeterministicKey.deserializeB58(
            null, partnerChain.getWatchingKey().serializePubB58(params), params);
    MarriedKeyChain chain =
        MarriedKeyChain.builder()
            .random(new SecureRandom())
            .followingKeys(partnerKey)
            .threshold(2)
            .build();
    myWallet.addAndActivateHDChain(chain);

    myAddress = myWallet.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS);

    Wallet wallet1 = roundTrip(myWallet);
    assertEquals(0, wallet1.getTransactions(true).size());
    assertEquals(Coin.ZERO, wallet1.getBalance());
    assertEquals(2, wallet1.getActiveKeyChain().getSigsRequiredToSpend());
    assertEquals(myAddress, wallet1.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS));
  }
 @Test
 public void empty() throws Exception {
   // Check the base case of a wallet with one key and no transactions.
   Wallet wallet1 = roundTrip(myWallet);
   assertEquals(0, wallet1.getTransactions(true).size());
   assertEquals(Coin.ZERO, wallet1.getBalance());
   assertArrayEquals(
       myKey.getPubKey(), wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPubKey());
   assertArrayEquals(
       myKey.getPrivKeyBytes(),
       wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getPrivKeyBytes());
   assertEquals(
       myKey.getCreationTimeSeconds(),
       wallet1.findKeyFromPubHash(myKey.getPubKeyHash()).getCreationTimeSeconds());
   assertEquals(mScriptCreationTime, wallet1.getWatchedScripts().get(0).getCreationTimeSeconds());
   assertEquals(1, wallet1.getWatchedScripts().size());
   assertEquals(
       ScriptBuilder.createOutputScript(myWatchedKey.toAddress(params)),
       wallet1.getWatchedScripts().get(0));
   assertEquals(WALLET_DESCRIPTION, wallet1.getDescription());
 }
  @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());
  }
  @Test
  public void oneTx() throws Exception {
    // Check basic tx serialization.
    Coin v1 = COIN;
    Transaction t1 = createFakeTx(params, v1, myAddress);
    t1.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByName("1.2.3.4")));
    t1.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByName("5.6.7.8")));
    t1.getConfidence().setSource(TransactionConfidence.Source.NETWORK);
    myWallet.receivePending(t1, null);
    Wallet wallet1 = roundTrip(myWallet);
    assertEquals(1, wallet1.getTransactions(true).size());
    assertEquals(v1, wallet1.getBalance(Wallet.BalanceType.ESTIMATED));
    Transaction t1copy = wallet1.getTransaction(t1.getHash());
    assertArrayEquals(t1.bitcoinSerialize(), t1copy.bitcoinSerialize());
    assertEquals(2, t1copy.getConfidence().numBroadcastPeers());
    assertEquals(TransactionConfidence.Source.NETWORK, t1copy.getConfidence().getSource());

    Protos.Wallet walletProto = new WalletProtobufSerializer().walletToProto(myWallet);
    assertEquals(Protos.Key.Type.ORIGINAL, walletProto.getKey(0).getType());
    assertEquals(0, walletProto.getExtensionCount());
    assertEquals(1, walletProto.getTransactionCount());
    assertEquals(6, walletProto.getKeyCount());

    Protos.Transaction t1p = walletProto.getTransaction(0);
    assertEquals(0, t1p.getBlockHashCount());
    assertArrayEquals(t1.getHash().getBytes(), t1p.getHash().toByteArray());
    assertEquals(Protos.Transaction.Pool.PENDING, t1p.getPool());
    assertFalse(t1p.hasLockTime());
    assertFalse(t1p.getTransactionInput(0).hasSequence());
    assertArrayEquals(
        t1.getInputs().get(0).getOutpoint().getHash().getBytes(),
        t1p.getTransactionInput(0).getTransactionOutPointHash().toByteArray());
    assertEquals(0, t1p.getTransactionInput(0).getTransactionOutPointIndex());
    assertEquals(t1p.getTransactionOutput(0).getValue(), v1.value);
  }