@Override public synchronized String toString() { StringBuilder builder = new StringBuilder(); int peers = numBroadcastPeers(); if (peers > 0) { builder.append("Seen by ").append(peers).append(peers > 1 ? " peers" : " peer"); builder.append(". "); switch (getConfidenceType()) { case UNKNOWN: builder.append("Unknown confidence level."); case BUILDING: builder.append(String.format(Locale.US, "Appeared in best chain at height %d, depth %d.", getAppearedAtChainHeight(), getDepthInBlocks())); break;
public synchronized ListenableFuture<TransactionConfidence> getDepthFuture(final int depth) { return getDepthFuture(depth, Threading.USER_THREAD); }
@Override public void setSource(TransactionConfidence.Source source) { tx.getConfidence().setSource(source); }
@Override public void onConfidenceChanged(TransactionConfidence confidence, ChangeReason reason) { if (getDepthInBlocks() >= depth) { removeEventListener(this); result.set(confidence); } } });
/** * Returns a future that completes when the transaction has been confirmed by "depth" blocks. For instance setting * depth to one will wait until it appears in a block on the best chain, and zero will wait until it has been seen * on the network. */ public synchronized ListenableFuture<TransactionConfidence> getDepthFuture(final int depth, Executor executor) { final SettableFuture<TransactionConfidence> result = SettableFuture.create(); if (getDepthInBlocks() >= depth) { result.set(this); } addEventListener(executor, new Listener() { @Override public void onConfidenceChanged(TransactionConfidence confidence, ChangeReason reason) { if (getDepthInBlocks() >= depth) { removeEventListener(this); result.set(confidence); } } }); return result; }
/** * Subtract the supplied depth from the given transactions. */ private void subtractDepth(int depthToSubtract, Collection<Transaction> transactions) { for (Transaction tx : transactions) { if (tx.getConfidence().getConfidenceType() == ConfidenceType.BUILDING) { tx.getConfidence().setDepthInBlocks(tx.getConfidence().getDepthInBlocks() - depthToSubtract); confidenceChanged.put(tx, TransactionConfidence.Listener.ChangeReason.DEPTH); } } }
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) { 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; for (PeerAddress address : confidence.getBroadcastBy()) { Protos.PeerAddress proto = Protos.PeerAddress.newBuilder() .setIpAddress(ByteString.copyFrom(address.getAddr().getAddress())) confidenceBuilder.addBroadcastBy(proto); Date lastBroadcastedAt = confidence.getLastBroadcastedAt(); if (lastBroadcastedAt != null) confidenceBuilder.setLastBroadcastedAt(lastBroadcastedAt.getTime());
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) { 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; TransactionConfidence.IXType ixType = confidence.getIXType(); switch(ixType) for (PeerAddress address : confidence.getBroadcastBy()) { Protos.PeerAddress proto = Protos.PeerAddress.newBuilder() .setIpAddress(ByteString.copyFrom(address.getAddr().getAddress())) Date lastBroadcastedAt = confidence.getLastBroadcastedAt(); if (lastBroadcastedAt != null) confidenceBuilder.setLastBroadcastedAt(lastBroadcastedAt.getTime()); confidenceBuilder.setMinConnections(confidence.getMinConnections());
Protos.TransactionConfidence.Builder confidenceBuilder) { synchronized (confidence) { confidenceBuilder.setType(Protos.TransactionConfidence.Type.valueOf(confidence.getConfidenceType().getValue())); if (confidence.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) { confidenceBuilder.setAppearedAtHeight(confidence.getAppearedAtChainHeight()); confidenceBuilder.setDepth(confidence.getDepthInBlocks()); TransactionConfidence.Source source = confidence.getSource(); switch (source) { case SELF: confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_SELF); break; for (ListIterator<PeerAddress> it = confidence.getBroadcastBy(); it.hasNext();) { PeerAddress address = it.next(); Protos.PeerAddress proto = Protos.PeerAddress.newBuilder()
public static boolean isSelectable(Transaction tx) { // Only pick chain-included transactions, or transactions that are ours and pending. TransactionConfidence confidence = tx.getConfidence(); TransactionConfidence.ConfidenceType type = confidence.getConfidenceType(); return type.equals(TransactionConfidence.ConfidenceType.BUILDING) || type.equals(TransactionConfidence.ConfidenceType.PENDING) && confidence.getSource().equals(TransactionConfidence.Source.SELF) && // In regtest mode we expect to have only one peer, so we won't see transactions propagate. // TODO: The value 1 below dates from a time when transactions we broadcast *to* were counted, set to 0 (confidence.numBroadcastPeers() > 1 || tx.getParams().getId().equals(NetworkParameters.ID_REGTEST)); } }
/** * A transaction is mature if it is either a building coinbase tx that is as deep or deeper than the required coinbase depth, or a non-coinbase tx. */ public boolean isMature() { if (!isCoinBase()) return true; if (getConfidence().getConfidenceType() != ConfidenceType.BUILDING) return false; return getConfidence().getDepthInBlocks() >= params.getSpendableCoinbaseDepth(); }
public static boolean isSelectable(Transaction tx, boolean usingInstantX) { // Only pick chain-included transactions, or transactions that are pending (whether ours or not). // InstantSend requires 6 confirmations TransactionConfidence confidence = tx.getConfidence(); TransactionConfidence.ConfidenceType type = confidence.getConfidenceType(); return (type.equals(TransactionConfidence.ConfidenceType.BUILDING) && (usingInstantX ? confidence.getDepthInBlocks() >= 6 : true)) || type.equals(TransactionConfidence.ConfidenceType.PENDING) && (usingInstantX ? false : true) && // In regtest mode we expect to have only one peer, so we won't see transactions propagate. // TODO: The value 1 below dates from a time when transactions we broadcast *to* were counted, set to 0 (confidence.numBroadcastPeers() > 1 || tx.getParams() == RegTestParams.get()); } }
t1.getConfidence().markBroadcastBy(new PeerAddress(PARAMS, InetAddress.getByName("1.2.3.4"))); t1.getConfidence().markBroadcastBy(new PeerAddress(PARAMS, InetAddress.getByName("5.6.7.8"))); t1.getConfidence().setSource(TransactionConfidence.Source.NETWORK); myWallet.receivePending(t1, null); Wallet wallet1 = roundTrip(myWallet); Transaction t1copy = wallet1.getTransaction(t1.getHash()); assertArrayEquals(t1.unsafeBitcoinSerialize(), t1copy.unsafeBitcoinSerialize()); assertEquals(2, t1copy.getConfidence().numBroadcastPeers()); assertNotNull(t1copy.getConfidence().getLastBroadcastedAt()); assertEquals(TransactionConfidence.Source.NETWORK, t1copy.getConfidence().getSource());
inv.addTransaction(tx); assertEquals(0, tx.getConfidence().numBroadcastPeers()); assertNull(tx.getConfidence().getLastBroadcastedAt()); assertEquals(1, tx.getConfidence().numBroadcastPeers()); assertNull(event[0]); assertEquals(2, tx.getConfidence().numBroadcastPeers()); assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p1).getAddress())); assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p2).getAddress())); assertNotNull(tx.getConfidence().getLastBroadcastedAt()); tx.getConfidence().addEventListener(new TransactionConfidence.Listener() { @Override public void onConfidenceChanged(TransactionConfidence confidence, TransactionConfidence.Listener.ChangeReason reason) { pingAndWait(p3); Threading.waitForUserCode(); assertEquals(tx.getHash(), confEvent[0].getTransactionHash()); assertEquals(3, tx.getConfidence().numBroadcastPeers()); assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p3).getAddress()));
@Override public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) { if (tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.DEAD) { eventDead[0] = tx; eventReplacement[0] = tx.getConfidence().getOverridingTransaction(); } } });
@Override public int compare(final Transaction tx1, final Transaction tx2) { final TransactionConfidence confidence1 = tx1.getConfidence(); final int height1 = confidence1.getConfidenceType() == ConfidenceType.BUILDING ? confidence1.getAppearedAtChainHeight() : Block.BLOCK_HEIGHT_UNKNOWN; final TransactionConfidence confidence2 = tx2.getConfidence(); final int height2 = confidence2.getConfidenceType() == ConfidenceType.BUILDING ? confidence2.getAppearedAtChainHeight() : Block.BLOCK_HEIGHT_UNKNOWN; final int heightComparison = -(Ints.compare(height1, height2)); //If height1==height2, compare by tx hash to make comparator consistent with equals return heightComparison != 0 ? heightComparison : tx1.getHash().compareTo(tx2.getHash()); } };
@Test public void selectable() throws Exception { Transaction t; t = new Transaction(PARAMS); t.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.PENDING); assertFalse(DefaultCoinSelector.isSelectable(t)); t.getConfidence().setSource(TransactionConfidence.Source.SELF); assertFalse(DefaultCoinSelector.isSelectable(t)); t.getConfidence().markBroadcastBy(new PeerAddress(PARAMS, InetAddress.getByName("1.2.3.4"))); assertFalse(DefaultCoinSelector.isSelectable(t)); t.getConfidence().markBroadcastBy(new PeerAddress(PARAMS, InetAddress.getByName("5.6.7.8"))); assertTrue(DefaultCoinSelector.isSelectable(t)); t = new Transaction(PARAMS); t.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.BUILDING); assertTrue(DefaultCoinSelector.isSelectable(t)); t = new Transaction(RegTestParams.get()); t.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.PENDING); t.getConfidence().setSource(TransactionConfidence.Source.SELF); assertTrue(DefaultCoinSelector.isSelectable(t)); }
/** * <p>Adds an event listener that will be run when this confidence object is updated. The listener will be locked and * is likely to be invoked on a peer thread.</p> * * <p>Note that this is NOT called when every block arrives. Instead it is called when the transaction * transitions between confidence states, ie, from not being seen in the chain to being seen (not necessarily in * the best chain). If you want to know when the transaction gets buried under another block, implement a * {@link BlockChainListener}, attach it to a {@link BlockChain} and then use the getters on the * confidence object to determine the new depth.</p> */ public void addEventListener(Listener listener) { addEventListener(Threading.USER_THREAD, listener); }
@Override public int getDepthInBlocks() { return tx.getConfidence().getDepthInBlocks(); }
/** * Erases the set of broadcast/seen peers. This cannot be called whilst the confidence is PENDING. It is useful * for saving memory and wallet space once a tx is buried so deep it doesn't seem likely to go pending again. */ public void clearBroadcastBy() { checkState(getConfidenceType() != ConfidenceType.PENDING); broadcastBy.clear(); lastBroadcastedAt = null; }