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) { // 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 NOT_SEEN_IN_CHAIN: 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.hasWorkDone()) { if (confidence.getConfidenceType() != ConfidenceType.BUILDING) { log.warn("Have workDone but not BUILDING for tx {}", tx.getHashAsString()); return; } confidence.setWorkDone(BigInteger.valueOf(confidenceProto.getWorkDone())); } 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 RuntimeException(e); // IP address is of invalid length. } 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; } }
@Override public void onCoinsSent( Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) { BigInteger amt = prevBalance.subtract(newBalance); final long amount = amt.longValue(); WalletApplication app = (WalletApplication) getApplicationContext(); final BTCFmt btcfmt = app.getBTCFmt(); // We allocate a new notification id for each receive. // We use it on both the receive and confirm so it // will replace the receive note with the confirm ... final int noteId = ++mNoteId; mLogger.info(String.format("showing notification send %d", amount)); showEventNotification( noteId, R.drawable.ic_note_bc_red_lt, mRes.getString(R.string.wallet_service_note_sent_title, btcfmt.unitStr()), mRes.getString( R.string.wallet_service_note_sent_msg, btcfmt.format(amount), btcfmt.unitStr())); final TransactionConfidence txconf = tx.getConfidence(); final TransactionConfidence.Listener listener = new TransactionConfidence.Listener() { @Override public void onConfidenceChanged(Transaction tx, ChangeReason reason) { // Wait until it's not pending anymore. if (tx.isPending()) return; ConfidenceType ct = tx.getConfidence().getConfidenceType(); if (ct == ConfidenceType.BUILDING) { mLogger.info(String.format("send %d confirm", amount)); // Show no longer pending. showEventNotification( noteId, R.drawable.ic_note_bc_red, mRes.getString(R.string.wallet_service_note_scnf_title, btcfmt.unitStr()), mRes.getString( R.string.wallet_service_note_scnf_msg, btcfmt.format(amount), btcfmt.unitStr())); } else if (ct == ConfidenceType.DEAD) { mLogger.info(String.format("send %d dead", amount)); // Notify dead. showEventNotification( noteId, R.drawable.ic_note_bc_gray, mRes.getString(R.string.wallet_service_note_sdead_title, btcfmt.unitStr()), mRes.getString( R.string.wallet_service_note_sdead_msg, btcfmt.format(amount), btcfmt.unitStr())); } else { mLogger.info(String.format("send %d unknown", amount)); } // We're all done listening ... txconf.removeEventListener(this); } }; txconf.addEventListener(listener); }
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.getWorkDone() != null) { confidenceBuilder.setWorkDone(confidence.getWorkDone().longValue()); } } 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); }