@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; }
@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; }
private void markTransactionAsSpent(Transaction transaction) { _backing.beginTransaction(); try { // Remove inputs from unspent, marking them as spent for (TransactionInput input : transaction.inputs) { TransactionOutputEx parentOutput = _backing.getUnspentOutput(input.outPoint); if (parentOutput != null) { _backing.deleteUnspentOutput(input.outPoint); _backing.putParentTransactionOutput(parentOutput); } } // See if any of the outputs are for ourselves and store them as // unspent for (int i = 0; i < transaction.outputs.length; i++) { TransactionOutput output = transaction.outputs[i]; if (isMine(output.script)) { _backing.putUnspentOutput( new TransactionOutputEx( new OutPoint(transaction.getHash(), i), -1, output.value, output.script.getScriptBytes(), false)); } } // Store transaction locally, so we have it in our history and don't // need to fetch it in a minute _backing.putTransaction(TransactionEx.fromUnconfirmedTransaction(transaction)); _backing.setTransactionSuccessful(); } finally { _backing.endTransaction(); } // Tell account that we have a new transaction onNewTransaction(TransactionEx.fromUnconfirmedTransaction(transaction), transaction); // Calculate local balance cache. It has changed because we have done // some spending updateLocalBalance(); persistContextIfNecessary(); }
protected boolean synchronizeUnspentOutputs(Collection<Address> addresses) { // Get the current unspent outputs as dictated by the block chain QueryUnspentOutputsResponse UnspentOutputResponse; try { UnspentOutputResponse = _wapi .queryUnspentOutputs(new QueryUnspentOutputsRequest(Wapi.VERSION, addresses)) .getResult(); } catch (WapiException e) { _logger.logError("Server connection failed with error code: " + e.errorCode, e); postEvent(Event.SERVER_CONNECTION_ERROR); return false; } Collection<TransactionOutputEx> remoteUnspent = UnspentOutputResponse.unspent; // Store the current block height setBlockChainHeight(UnspentOutputResponse.height); // Make a map for fast lookup Map<OutPoint, TransactionOutputEx> remoteMap = toMap(remoteUnspent); // Get the current unspent outputs as it is believed to be locally Collection<TransactionOutputEx> localUnspent = _backing.getAllUnspentOutputs(); // Make a map for fast lookup Map<OutPoint, TransactionOutputEx> localMap = toMap(localUnspent); // Find remotely removed unspent outputs for (TransactionOutputEx l : localUnspent) { TransactionOutputEx r = remoteMap.get(l.outPoint); if (r == null) { // An output has gone. Maybe it was spent in another wallet, or // never confirmed due to missing fees, double spend, or mutated. // Either way, we delete it locally _backing.deleteUnspentOutput(l.outPoint); } } // Find remotely added unspent outputs Set<Sha256Hash> transactionsToAddOrUpdate = new HashSet<Sha256Hash>(); List<TransactionOutputEx> unspentOutputsToAddOrUpdate = new LinkedList<TransactionOutputEx>(); for (TransactionOutputEx r : remoteUnspent) { TransactionOutputEx l = localMap.get(r.outPoint); if (l == null || l.height != r.height) { // New remote output or new height (Maybe it confirmed or we // might even have had a reorg). Either way we just update it unspentOutputsToAddOrUpdate.add(r); transactionsToAddOrUpdate.add(r.outPoint.hash); // Note: We are not adding the unspent output to the DB just yet. We // first want to verify the full set of funding transactions of the // transaction that this unspent output belongs to } } // Fetch updated or added transactions if (transactionsToAddOrUpdate.size() > 0) { GetTransactionsResponse response; try { response = _wapi .getTransactions( new GetTransactionsRequest(Wapi.VERSION, transactionsToAddOrUpdate)) .getResult(); } catch (WapiException e) { _logger.logError("Server connection failed with error code: " + e.errorCode, e); postEvent(Event.SERVER_CONNECTION_ERROR); return false; } try { handleNewExternalTransactions(response.transactions); } catch (WapiException e) { _logger.logError("Server connection failed with error code: " + e.errorCode, e); postEvent(Event.SERVER_CONNECTION_ERROR); return false; } // Finally update out list of unspent outputs with added or updated // outputs for (TransactionOutputEx output : unspentOutputsToAddOrUpdate) { _backing.putUnspentOutput(output); } } return true; }