private void handleNewExternalTransactionsInt(Collection<TransactionEx> transactions) throws WapiException { // Transform and put into two arrays with matching indexes ArrayList<TransactionEx> texArray = new ArrayList<TransactionEx>(transactions.size()); ArrayList<Transaction> txArray = new ArrayList<Transaction>(transactions.size()); for (TransactionEx tex : transactions) { try { txArray.add(Transaction.fromByteReader(new ByteReader(tex.binary))); texArray.add(tex); } catch (TransactionParsingException e) { // We hit a transaction that we cannot parse. Log but otherwise ignore it _logger.logError("Received transaction that we cannot parse: " + tex.txid.toString()); continue; } } // Grab and handle parent transactions fetchStoreAndValidateParentOutputs(txArray); // Store transaction locally for (int i = 0; i < txArray.size(); i++) { _backing.putTransaction(texArray.get(i)); onNewTransaction(texArray.get(i), txArray.get(i)); } }
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 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; }