public void onConfidenceChanged(Transaction tx, ChangeReason reason) {
   // The number of peers that announced this tx has gone up.
   final TransactionConfidence conf = tx.getConfidence();
   int numSeenPeers = conf.numBroadcastPeers();
   boolean mined = tx.getAppearsInHashes() != null;
   log.info(
       "broadcastTransaction: {}:  TX {} seen by {} peers{}",
       reason,
       pinnedTx.getHashAsString(),
       numSeenPeers,
       mined ? " and mined" : "");
   if (numSeenPeers >= numWaitingFor || mined) {
     // We've seen the min required number of peers announce the transaction, or it was included
     // in a block. Normally we'd expect to see it fully propagate before it gets mined, but
     // it can be that a block is solved very soon after broadcast, and it's also possible that
     // due to version skew and changes in the relay rules our transaction is not going to
     // fully propagate yet can get mined anyway.
     //
     // Note that we can't wait for the current number of connected peers right now because we
     // could have added more peers after the broadcast took place, which means they won't
     // have seen the transaction. In future when peers sync up their memory pools after they
     // connect we could come back and change this.
     //
     // We're done! It's important that the PeerGroup lock is not held (by this thread) at this
     // point to avoid triggering inversions when the Future completes.
     log.info("broadcastTransaction: {} complete", pinnedTx.getHashAsString());
     tx.getConfidence().removeEventListener(this);
     future.set(pinnedTx); // RE-ENTRANCY POINT
   }
 }
  private static void writeConfidence(
      Protos.Transaction.Builder txBuilder,
      TransactionConfidence confidence,
      Protos.TransactionConfidence.Builder confidenceBuilder) {
    synchronized (confidence) {
      confidenceBuilder.setType(
          Protos.TransactionConfidence.Type.valueOf(confidence.getConfidenceType().getValue()));
      if (confidence.getConfidenceType() == ConfidenceType.BUILDING) {
        confidenceBuilder.setAppearedAtHeight(confidence.getAppearedAtChainHeight());
        confidenceBuilder.setDepth(confidence.getDepthInBlocks());
      }
      if (confidence.getConfidenceType() == ConfidenceType.DEAD) {
        // Copy in the overriding transaction, if available.
        // (A dead coinbase transaction has no overriding transaction).
        if (confidence.getOverridingTransaction() != null) {
          Sha256Hash overridingHash = confidence.getOverridingTransaction().getHash();
          confidenceBuilder.setOverridingTransaction(hashToByteString(overridingHash));
        }
      }
      TransactionConfidence.Source source = confidence.getSource();
      switch (source) {
        case SELF:
          confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_SELF);
          break;
        case NETWORK:
          confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_NETWORK);
          break;
        case UNKNOWN:
          // Fall through.
        default:
          confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_UNKNOWN);
          break;
      }
    }

    for (ListIterator<PeerAddress> it = confidence.getBroadcastBy(); it.hasNext(); ) {
      PeerAddress address = it.next();
      Protos.PeerAddress proto =
          Protos.PeerAddress.newBuilder()
              .setIpAddress(ByteString.copyFrom(address.getAddr().getAddress()))
              .setPort(address.getPort())
              .setServices(address.getServices().longValue())
              .build();
      confidenceBuilder.addBroadcastBy(proto);
    }
    txBuilder.setConfidence(confidenceBuilder);
  }
  private void watchCloseConfirmations() {
    // When we see the close transaction get a few confirmations, we can just delete the record
    // of this channel along with the refund tx from the wallet, because we're not going to need
    // any of that any more.
    final TransactionConfidence confidence = storedChannel.close.getConfidence();
    ListenableFuture<Transaction> future =
        confidence.getDepthFuture(CONFIRMATIONS_FOR_DELETE, Threading.SAME_THREAD);
    Futures.addCallback(
        future,
        new FutureCallback<Transaction>() {
          @Override
          public void onSuccess(Transaction result) {
            deleteChannelFromWallet();
          }

          @Override
          public void onFailure(Throwable t) {
            Throwables.propagate(t);
          }
        });
  }
 private void readConfidence(
     Transaction tx,
     Protos.TransactionConfidence confidenceProto,
     TransactionConfidence confidence)
     throws UnreadableWalletException {
   // We are lenient here because tx confidence is not an essential part of the wallet.
   // If the tx has an unknown type of confidence, ignore.
   if (!confidenceProto.hasType()) {
     log.warn("Unknown confidence type for tx {}", tx.getHashAsString());
     return;
   }
   ConfidenceType confidenceType;
   switch (confidenceProto.getType()) {
     case BUILDING:
       confidenceType = ConfidenceType.BUILDING;
       break;
     case DEAD:
       confidenceType = ConfidenceType.DEAD;
       break;
       // These two are equivalent (must be able to read old wallets).
     case NOT_IN_BEST_CHAIN:
       confidenceType = ConfidenceType.PENDING;
       break;
     case PENDING:
       confidenceType = ConfidenceType.PENDING;
       break;
     case UNKNOWN:
       // Fall through.
     default:
       confidenceType = ConfidenceType.UNKNOWN;
       break;
   }
   confidence.setConfidenceType(confidenceType);
   if (confidenceProto.hasAppearedAtHeight()) {
     if (confidence.getConfidenceType() != ConfidenceType.BUILDING) {
       log.warn("Have appearedAtHeight but not BUILDING for tx {}", tx.getHashAsString());
       return;
     }
     confidence.setAppearedAtChainHeight(confidenceProto.getAppearedAtHeight());
   }
   if (confidenceProto.hasDepth()) {
     if (confidence.getConfidenceType() != ConfidenceType.BUILDING) {
       log.warn("Have depth but not BUILDING for tx {}", tx.getHashAsString());
       return;
     }
     confidence.setDepthInBlocks(confidenceProto.getDepth());
   }
   if (confidenceProto.hasOverridingTransaction()) {
     if (confidence.getConfidenceType() != ConfidenceType.DEAD) {
       log.warn("Have overridingTransaction but not OVERRIDDEN for tx {}", tx.getHashAsString());
       return;
     }
     Transaction overridingTransaction = txMap.get(confidenceProto.getOverridingTransaction());
     if (overridingTransaction == null) {
       log.warn(
           "Have overridingTransaction that is not in wallet for tx {}", tx.getHashAsString());
       return;
     }
     confidence.setOverridingTransaction(overridingTransaction);
   }
   for (Protos.PeerAddress proto : confidenceProto.getBroadcastByList()) {
     InetAddress ip;
     try {
       ip = InetAddress.getByAddress(proto.getIpAddress().toByteArray());
     } catch (UnknownHostException e) {
       throw new UnreadableWalletException("Peer IP address does not have the right length", e);
     }
     int port = proto.getPort();
     PeerAddress address = new PeerAddress(ip, port);
     address.setServices(BigInteger.valueOf(proto.getServices()));
     confidence.markBroadcastBy(address);
   }
   switch (confidenceProto.getSource()) {
     case SOURCE_SELF:
       confidence.setSource(TransactionConfidence.Source.SELF);
       break;
     case SOURCE_NETWORK:
       confidence.setSource(TransactionConfidence.Source.NETWORK);
       break;
     case SOURCE_UNKNOWN:
       // Fall through.
     default:
       confidence.setSource(TransactionConfidence.Source.UNKNOWN);
       break;
   }
 }
Example #5
0
 @Override
 public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) {
   System.out.println("-----> confidence changed: " + tx.getHashAsString());
   TransactionConfidence confidence = tx.getConfidence();
   System.out.println("new block depth: " + confidence.getDepthInBlocks());
 }
  @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());
  }