public List<AddressStatus> getAllAddressStatus() { lock.lock(); try { ArrayList<AddressStatus> statuses = new ArrayList<>(addressesStatus.size()); for (Map.Entry<AbstractAddress, String> status : addressesStatus.entrySet()) { statuses.add(new AddressStatus(status.getKey(), status.getValue())); } return statuses; } finally { lock.unlock(); } }
void commitAddressStatus(AddressStatus newStatus) { lock.lock(); try { /*AddressStatus updatingStatus = statusPendingUpdates.get(newStatus.getAddress()); if (updatingStatus != null && updatingStatus.equals(newStatus)) { statusPendingUpdates.remove(newStatus.getAddress()); }*/ addressesStatus.put(newStatus.getAddress(), newStatus.getStatus()); } finally { lock.unlock(); } // Skip saving null statuses if (newStatus.getStatus() != null) { walletSaveLater(); } }
void commitAddressStatus(AddressStatus newStatus) { if (!newStatus.canCommitStatus()) { log.warn("Tried to commit an address status with a non applied state: {}:{}", newStatus.getAddress(), newStatus.getStatus()); return; } lock.lock(); try { AddressStatus updatingStatus = statusPendingUpdates.get(newStatus.getAddress()); if (updatingStatus != null && updatingStatus.equals(newStatus)) { statusPendingUpdates.remove(newStatus.getAddress()); } addressesStatus.put(newStatus.getAddress(), newStatus.getStatus()); queueOnConnectivity(); } finally { lock.unlock(); } // Skip saving null statuses if (newStatus.getStatus() != null) { walletSaveLater(); } }
@Override public void onUnspentTransactionUpdate(AddressStatus status, List<UnspentTx> unspentTxes) { lock.lock(); try { AddressStatus updatingStatus = statusPendingUpdates.get(status.getAddress()); // Check if this updating status is valid if (updatingStatus != null && updatingStatus.equals(status)) { updatingStatus.queueUnspentTransactions(unspentTxes); fetchTransactionsIfNeeded(unspentTxes); tryToApplyState(updatingStatus); } else { log.info("Ignoring unspent tx call because no entry found or newer entry."); } } finally { lock.unlock(); } }
/** * Try to apply the status state */ private void tryToApplyState(AddressStatus status) { lock.lock(); try { if (statusPendingUpdates.containsKey(status.getAddress())) { if (status.isUnspentTxQueued() && !status.isUnspentTxStateApplied()) { Set<Sha256Hash> txHashes = status.getUnspentTxHashes(); HashMap<Sha256Hash, BitTransaction> txs = getTransactions(txHashes); // We have all the transactions, apply state if (txs.size() == txHashes.size()) { applyUnspentState(status, txs); } } if (status.isHistoryTxQueued() && !status.isHistoryTxStateApplied()) { Set<Sha256Hash> txHashes = status.getHistoryTxHashes(); HashMap<Sha256Hash, BitTransaction> txs = getTransactions(txHashes); // We have all the transactions, apply state if (txs.size() == txHashes.size()) { applyHistoryState(status, txs); } } } } finally { lock.unlock(); } }
private void applyUnspentState(AddressStatus status, HashMap<Sha256Hash, BitTransaction> txs) { checkState(lock.isHeldByCurrentThread(), "Lock is held by another thread"); checkState(!status.isUnspentTxStateApplied(), "Unspent tx state already applied"); AbstractAddress address = status.getAddress(); Set<TrimmedOutPoint> notPresentInStatus = new HashSet<>(); for (Map.Entry<TrimmedOutPoint, OutPointOutput> utxo : unspentOutputs.entrySet()) { for (UnspentTx unspentTx : status.getUnspentTxs()) { TrimmedOutPoint outPoint = new TrimmedOutPoint(type, unspentTx.getTxPos(), unspentTx.getTxHash()); status.setUnspentTxStateApplied(true); if (status.canCommitStatus()) commitAddressStatus(status); queueOnNewBalance();
@Override public void onTransactionHistory(AddressStatus status, List<HistoryTx> historyTxes) { lock.lock(); try { AddressStatus updatingStatus = statusPendingUpdates.get(status.getAddress()); // Check if this updating status is valid if (updatingStatus != null && updatingStatus.equals(status)) { updatingStatus.queueHistoryTransactions(historyTxes); fetchTransactionsIfNeeded(historyTxes); tryToApplyState(); } else { log.info("Ignoring history tx call because no entry found or newer entry."); } } finally { lock.unlock(); } }
private void applyHistoryState(AddressStatus status, HashMap<Sha256Hash, BitTransaction> txs) { checkState(lock.isHeldByCurrentThread(), "Lock is held by another thread"); checkState(!status.isHistoryTxStateApplied(), "History tx state already applied"); // Update confirmation status if necessary for (HistoryTx historyTx : status.getHistoryTxs()) { BitTransaction tx = checkNotNull(txs.get(historyTx.getTxHash())); checkTxConfirmation(historyTx, tx); } status.setHistoryTxStateApplied(true); if (status.canCommitStatus()) commitAddressStatus(status); }
@Override public void getUnspentTx(AddressStatus status, BitTransactionEventListener listener) { List<UnspentTx> utx = unspentTxs.get(status.getAddress()); if (utx == null) { utx = ImmutableList.of(); } listener.onUnspentTransactionUpdate(status, utx); }
/** * Queue transactions that are going to be fetched */ public void queueHistoryTransactions(List<HistoryTx> txs) { if (historyTransactions == null) { historyTransactions = Sets.newHashSet(txs); historyTxHashes = fillTransactions(txs); stateMustBeApplied = true; } }
@Override public void getHistoryTx(AddressStatus status, TransactionEventListener<BitTransaction> listener) { List<HistoryTx> htx = historyTxs.get(status.getAddress()); if (htx == null) { htx = ImmutableList.of(); } listener.onTransactionHistory(status, htx); }
/** * Queue transactions that are going to be fetched */ public void queueUnspentTransactions(List<UnspentTx> txs) { if (unspentTransactions == null) { unspentTransactions = Sets.newHashSet(txs); unspentTxHashes = fillTransactions(txs); stateMustBeApplied = true; } }
/** * Sets that the specified status is currently updating i.e. getting transactions. * * Returns true if registered successfully or false if status already updating */ @VisibleForTesting boolean registerStatusForUpdate(AddressStatus status) { checkNotNull(status.getStatus()); lock.lock(); try { // If current address is updating if (statusPendingUpdates.containsKey(status.getAddress())) { AddressStatus updatingAddressStatus = statusPendingUpdates.get(status.getAddress()); String updatingStatus = updatingAddressStatus.getStatus(); // If the same status is updating, don't update again if (updatingStatus != null && updatingStatus.equals(status.getStatus())) { return false; } else { // Status is newer, so replace the updating status statusPendingUpdates.put(status.getAddress(), status); return true; } } else { // This status is new statusPendingUpdates.put(status.getAddress(), status); return true; } } finally { lock.unlock(); } }
@Nullable public AddressStatus getAddressStatus(AbstractAddress address) { lock.lock(); try { if (addressesStatus.containsKey(address)) { return new AddressStatus(address, addressesStatus.get(address)); } else { return null; } } finally { lock.unlock(); } }
Arrays.asList(status.getAddress().toString())); final ListenableFuture<ResultMessage> result = stratumClient.call(message);
private boolean isAddressStatusChanged(AddressStatus addressStatus) { lock.lock(); try { AbstractAddress address = addressStatus.getAddress(); String newStatus = addressStatus.getStatus(); if (addressesStatus.containsKey(address)) { String previousStatus = addressesStatus.get(address); if (previousStatus == null) { return newStatus != null; // Status changed if newStatus is not null } else { return !previousStatus.equals(newStatus); } } else { // Unused address, just mark it that we watch it if (newStatus == null) { commitAddressStatus(addressStatus); return false; } else { return true; } } } finally { lock.unlock(); } }
@Override public void onSuccess(ResultMessage result) { AddressStatus status = null; try { if (result.getResult().isNull(0)) { status = new AddressStatus(address, null); } else { status = new AddressStatus(address, result.getResult().getString(0)); } listener.onAddressStatusUpdate(status); } catch (JSONException e) { log.error("Unexpected JSON format", e); } }
@Override public void getUnspentTx(final AddressStatus status, final BitTransactionEventListener listener) { checkNotNull(stratumClient); CallMessage message = new CallMessage("blockchain.address.listunspent", Arrays.asList(status.getAddress().toString())); final ListenableFuture<ResultMessage> result = stratumClient.call(message); Futures.addCallback(result, new FutureCallback<ResultMessage>() { @Override public void onSuccess(ResultMessage result) { JSONArray resTxs = result.getResult(); ImmutableList.Builder<UnspentTx> utxes = ImmutableList.builder(); try { for (int i = 0; i < resTxs.length(); i++) { utxes.add(new UnspentTx(resTxs.getJSONObject(i))); } } catch (JSONException e) { onFailure(e); return; } listener.onUnspentTransactionUpdate(status, utxes.build()); } @Override public void onFailure(Throwable t) { log.error("Could not get reply for blockchain.address.listunspent", t); } }, Threading.USER_THREAD); }
private boolean isAddressStatusChanged(AddressStatus addressStatus) { lock.lock(); try { AbstractAddress address = addressStatus.getAddress(); String newStatus = addressStatus.getStatus(); if (addressesStatus.containsKey(address)) { String previousStatus = addressesStatus.get(address); if (previousStatus == null) { return newStatus != null; // Status changed if newStatus is not null } else { return !previousStatus.equals(newStatus); } } else { // Unused address, just mark it that we watch it if (newStatus == null) { commitAddressStatus(addressStatus); return false; } else { return true; } } } finally { lock.unlock(); } }
@Override public void handle(CallMessage message) { try { AbstractAddress address = BitAddress.from(type, message.getParams().getString(0)); AddressStatus status; if (message.getParams().isNull(1)) { status = new AddressStatus(address, null); } else { status = new AddressStatus(address, message.getParams().getString(1)); } listener.onAddressStatusUpdate(status); } catch (AddressMalformedException e) { log.error("Address subscribe sent a malformed address", e); } catch (JSONException e) { log.error("Unexpected JSON format", e); } } };