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)); }
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 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(); int protocolVersion = tx.getParams().getProtocolVersion(); PeerAddress address = new PeerAddress(ip, port, protocolVersion); 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; } }
@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()); }