/** * Broadcast outgoing transactions. * * <p>This method should only be called from the wallet manager * * @return false if synchronization failed due to failed blockchain connection */ public synchronized boolean broadcastOutgoingTransactions() { checkNotArchived(); List<Sha256Hash> broadcastedIds = new LinkedList<Sha256Hash>(); Map<Sha256Hash, byte[]> transactions = _backing.getOutgoingTransactions(); for (byte[] rawTransaction : transactions.values()) { TransactionEx tex = TransactionEx.fromUnconfirmedTransaction(rawTransaction); BroadcastResult result = broadcastTransaction(TransactionEx.toTransaction(tex)); if (result == BroadcastResult.SUCCESS) { broadcastedIds.add(tex.txid); _backing.removeOutgoingTransaction(tex.txid); } else { if (result == BroadcastResult.REJECTED) { // invalid tx _backing.deleteTransaction(tex.txid); _backing.removeOutgoingTransaction(tex.txid); } else { // No connection --> retry next sync } } } if (!broadcastedIds.isEmpty()) { onTransactionsBroadcasted(broadcastedIds); } return true; }
@Override public synchronized boolean deleteTransaction(Sha256Hash transactionId) { TransactionEx tex = _backing.getTransaction(transactionId); if (tex == null) return false; Transaction tx = TransactionEx.toTransaction(tex); _backing.beginTransaction(); try { // See if any of the outputs are stored locally and remove them for (int i = 0; i < tx.outputs.length; i++) { TransactionOutput output = tx.outputs[i]; OutPoint outPoint = new OutPoint(tx.getHash(), i); TransactionOutputEx utxo = _backing.getUnspentOutput(outPoint); if (utxo != null) { _backing.deleteUnspentOutput(outPoint); } } // remove it from the backing _backing.deleteTransaction(transactionId); _backing.setTransactionSuccessful(); } finally { _backing.endTransaction(); } updateLocalBalance(); // will still need a new sync besides re-calculating return true; }
protected boolean monitorYoungTransactions() { Collection<TransactionEx> list = _backing.getYoungTransactions(5, getBlockChainHeight()); if (list.isEmpty()) { return true; } List<Sha256Hash> txids = new ArrayList<Sha256Hash>(list.size()); for (TransactionEx tex : list) { txids.add(tex.txid); } CheckTransactionsResponse result; try { result = _wapi.checkTransactions(new CheckTransactionsRequest(txids)).getResult(); } catch (WapiException e) { postEvent(Event.SERVER_CONNECTION_ERROR); _logger.logError("Server connection failed with error code: " + e.errorCode, e); // We failed to check transactions return false; } for (TransactionStatus t : result.transactions) { if (!t.found) { // We have a transaction locally that does not exist in the // blockchain. Must be a residue due to double-spend or malleability _backing.deleteTransaction(t.txid); continue; } TransactionEx tex = _backing.getTransaction(t.txid); Preconditions.checkNotNull(tex); if (tex.height != t.height || tex.time != t.time) { // The transaction got a new height or timestamp. There could be // several reasons for that. It got a new timestamp from the server, // it confirmed, or might also be a reorg. TransactionEx newTex = new TransactionEx(tex.txid, t.height, t.time, tex.binary); System.out.println("Replacing:\n" + tex.toString() + "\nWith:\n" + newTex.toString()); postEvent(Event.TRANSACTION_HISTORY_CHANGED); _backing.deleteTransaction(tex.txid); _backing.putTransaction(newTex); } } return true; }
@Override public synchronized boolean cancelQueuedTransaction(Sha256Hash transaction) { Map<Sha256Hash, byte[]> outgoingTransactions = _backing.getOutgoingTransactions(); if (!outgoingTransactions.containsKey(transaction)) { return false; } Transaction tx; try { tx = Transaction.fromBytes(outgoingTransactions.get(transaction)); } catch (TransactionParsingException e) { return false; } _backing.beginTransaction(); try { // See if any of the outputs are stored locally and remove them for (int i = 0; i < tx.outputs.length; i++) { TransactionOutput output = tx.outputs[i]; OutPoint outPoint = new OutPoint(tx.getHash(), i); TransactionOutputEx utxo = _backing.getUnspentOutput(outPoint); if (utxo != null) { _backing.deleteUnspentOutput(outPoint); } } // Remove a queued transaction from our outgoing buffer _backing.removeOutgoingTransaction(transaction); // remove it from the backing _backing.deleteTransaction(transaction); _backing.setTransactionSuccessful(); } finally { _backing.endTransaction(); } // calc the new balance to remove the outgoing amount // the total balance will still be wrong, as we already deleted some UTXOs to build the queued // transaction // these will get restored after the next sync updateLocalBalance(); // markTransactionAsSpent(transaction); return true; }