@Override public void onSuccess(ResultMessage result) { try { String rawTx = result.getResult().getString(0); byte[] txBytes = Utils.HEX.decode(rawTx); BitTransaction tx = new BitTransaction(type, txBytes); if (!tx.getHash().equals(txHash)) { throw new Exception("Requested TX " + txHash + " but got " + tx.getHashAsString()); } listener.onTransactionUpdate(tx); if (cacheDir != null) { try { Files.write(txBytes, getTxCacheFile(txHash)); } catch (IOException e) { log.warn("Error writing cached transaction", e); } } } catch (Exception e) { onFailure(e); } }
@Override public boolean broadcastTxSync(final BitTransaction tx) { checkNotNull(stratumClient); CallMessage message = new CallMessage("blockchain.transaction.broadcast", Arrays.asList(Utils.HEX.encode(tx.bitcoinSerialize()))); try { ResultMessage result = stratumClient.call(message).get(); String txId = result.getResult().getString(0); // FIXME could return {u'message': u'', u'code': -25} log.info("got tx {} =?= {}", txId, tx.getHash()); checkState(tx.getHash().toString().equals(txId)); return true; } catch (Exception e) { log.error("Could not get reply for blockchain.transaction.broadcast", e); } return false; }
public OutPointOutput(BitTransaction tx, long index) { this(new TrimmedOutput(tx.getOutput((int) index), index, tx.getHash()), tx.isGenerated()); }
private void guessSource(BitTransaction tx) { checkState(lock.isHeldByCurrentThread(), "Lock is held by another thread"); if (tx.getSource() == Source.UNKNOWN) { boolean isReceiving = tx.getValue(this).isPositive(); if (isReceiving) { tx.setSource(Source.NETWORK); } else { tx.setSource(Source.SELF); } } }
/** * If the transactions outputs are all marked as spent, and it's in the unspent map, move it. * If the owned transactions outputs are not all marked as spent, and it's in the spent map, move it. */ private void maybeMovePool(BitTransaction tx) { lock.lock(); try { if (tx.getConfidenceType() == BUILDING) { // Transaction is confirmed, move it if (pending.remove(tx.getHash()) != null) { confirmed.put(tx.getHash(), tx); trimTransaction(tx.getHash()); } } } finally { lock.unlock(); } }
if (transaction == null || transaction.isTrimmed()) return false; for (TransactionInput input : transaction.getInputs()) { final Value valueSent = transaction.getValueSent(this); final Value valueReceived = transaction.getValueReceived(this); boolean isReceiving = valueReceived.compareTo(valueSent) > 0; final Value fee = isReceiving ? null : transaction.getRawTxFee(this); Transaction txFull = transaction.getRawTransaction(); List<TransactionOutput> outputs = txFull.getOutputs(); BitTransaction.fromTrimmed(hash, tx, valueSent, valueReceived, fee)); return true;
if (includeUnsafe || tx.getSource() == Source.SELF) { boolean isDoubleSpending = false; txSpendingTxo.clear(); for (TransactionInput txi : tx.getInputs()) { TrimmedOutPoint outPoint = TrimmedOutPoint.get(txi); tx.getHash(), outPoint.getHash(), outPoint.getIndex()); isDoubleSpending = true; break; List<TransactionOutput> outputs = tx.getOutputs(); for (int i = 0; i < outputs.size(); i++) { TransactionOutput output = outputs.get(i);
private static Protos.Transaction makeBitTxProto(BitWalletTransaction wtx) { BitTransaction bitTx = wtx.getTransaction(); boolean isTrimmed = bitTx.isTrimmed(); Transaction tx = bitTx.getRawTransaction(); Protos.Transaction.Builder txBuilder = Protos.Transaction.newBuilder(); .setHash(hashToByteString(bitTx.getHash())) .setVersion((int) tx.getVersion()); if (bitTx.isTrimmed()) { txBuilder.setIsTrimmed(true); txBuilder.setValueReceived(bitTx.getValueReceived().value); txBuilder.setValueSent(bitTx.getValueSent().value); if (bitTx.getFee() != null) txBuilder.setFee(bitTx.getFee().value);
lock.lock(); try { Sha256Hash hash = tx.getHash(); if (tx.getConfidenceType() == UNKNOWN) { maybeUpdateBlockDepth(tx, false); } else { tx.setConfidenceType(PENDING); for (TransactionInput input : tx.getInputs()) { Sha256Hash dependencyTx = input.getOutpoint().getHash(); outOfOrderTransactions.remove(outOfOrderTx.getKey().getHash()); addNewTransactionIfNeeded(outOfOrderTx.getKey());
txHashes.add(tx.getHash()); for (TransactionOutput txo : tx.getOutputs()) { if (txo.isAvailableForSpending() && txo.isMineOrWatched(account)) { OutPointOutput utxo = new OutPointOutput(tx, txo.getIndex()); if (tx.getConfidenceType() == BUILDING) { utxo.setAppearedAtChainHeight(tx.getAppearedAtChainHeight()); utxo.setDepthInBlocks(tx.getDepthInBlocks());
assertEquals(tx, newPocket.getTransaction(tx.getHash())); BitTransaction txNew = checkNotNull(newPocket.getTransaction(tx.getHash())); assertInputsEquals(tx.getInputs(), txNew.getInputs()); assertOutputsEquals(tx.getOutputs(false), txNew.getOutputs(false));
private Sha256Hash send(Value value, WalletPocketHD w1, WalletPocketHD w2) throws Exception { assertEquals(w1.getCoinType(), w2.getCoinType()); CoinType type = w1.getCoinType(); BitSendRequest req = w1.sendCoinsOffline(w2.getReceiveAddress(), value); req.feePerTxSize = type.value("0.01"); w1.completeAndSignTx(req); byte[] txBytes = req.tx.bitcoinSerialize(); w1.addNewTransactionIfNeeded(new BitTransaction(type, txBytes)); w2.addNewTransactionIfNeeded(new BitTransaction(type, txBytes)); return req.tx.getHash(); }
private void maybeUpdateBlockDepth(BitTransaction tx, boolean updateUtxoSet) { checkState(lock.isHeldByCurrentThread(), "Lock is held by another thread"); if (tx.getConfidenceType() != BUILDING) return; int newDepth = lastBlockSeenHeight - tx.getAppearedAtChainHeight() + 1; if (newDepth > 1) { tx.setDepthInBlocks(newDepth); // Update unspent outputs if (updateUtxoSet) { for (TransactionOutput output : tx.getOutputs(false)) { OutPointOutput unspentOutput = unspentOutputs.get(TrimmedOutPoint.get(output)); if (unspentOutput != null) { unspentOutput.setDepthInBlocks(newDepth); } } } } }
@Nullable public Value getRawTxFee(TransactionWatcherWallet wallet) { checkState(!isTrimmed, "Cannot get raw tx fee from a trimmed transaction"); Value fee = type.value(0); for (TransactionInput input : tx.getInputs()) { TransactionOutPoint outPoint = input.getOutpoint(); BitTransaction inTx = wallet.getTransaction(outPoint.getHash()); if (inTx == null || !inTx.isOutputAvailable((int) outPoint.getIndex())) { return null; } TransactionOutput txo = inTx.getOutput((int) outPoint.getIndex()); fee = fee.add(txo.getValue()); } for (TransactionOutput output : getOutputs()) { fee = fee.subtract(output.getValue()); } return fee; }
for (int i = 0 ; i < tx.getOutputs(false).size() ; i++) { TransactionOutput output = tx.getOutputs().get(i); final Protos.TransactionOutput transactionOutput = txProto.getTransactionOutput(i); if (tx.isTrimmed()) { if (transactionOutput.getIsSpent() && output.isAvailableForSpending()) { output.markAsSpent(null); final ByteString spentByTransactionHash = transactionOutput.getSpentByTransactionHash(); BitTransaction spendingTx = txMap.get(spentByTransactionHash); if (spendingTx == null || spendingTx.isTrimmed()) { throw new UnreadableWalletException(String.format("Could not connect %s to %s", tx.getHashAsString(), byteStringToHash(spentByTransactionHash))); TransactionInput input = checkNotNull(spendingTx.getRawTransaction().getInput(spendingIndex), "A spending index does not exist"); input.connect(output); Transaction rawTx = tx.getRawTransaction(); TransactionConfidence confidence = rawTx.getConfidence(); readConfidence(rawTx, confidenceProto, confidence);
final Value valueReceived = type.value(txProto.getValueReceived()); final Value fee = txProto.hasFee() ? type.value(txProto.getFee()) : null; bitTx = BitTransaction.fromTrimmed(byteStringToHash(txProto.getHash()), tx, valueSent, valueReceived, fee); } else { bitTx = new BitTransaction(tx); if (!bitTx.getHash().equals(hash)) throw new UnreadableWalletException(String.format("Transaction did not deserialize completely: %s vs %s", tx.getHash(), hash)); if (txMap.containsKey(txProto.getHash()))
@Override public void run() { File txCachedFile = getTxCacheFile(txHash); if (txCachedFile.exists()) { try { byte[] txBytes = Files.toByteArray(txCachedFile); BitTransaction tx = new BitTransaction(type, txBytes); if (!tx.getHash().equals(txHash)) { if (!txCachedFile.delete()) { log.warn("Error deleting cached transaction {}", txCachedFile); } } else { listener.onTransactionUpdate(tx); return; } } catch (IOException e) { log.warn("Error reading cached transaction", e); } } // Fallback to fetching from the network getTransactionFromNetwork(txHash, listener); } });
try { if (pool == null) { switch (tx.getConfidenceType()) { case BUILDING: pool = WalletTransaction.Pool.CONFIRMED; default: throw new RuntimeException("Unsupported confidence type: " + tx.getConfidenceType().name()); trimTransaction(tx.getHash()); if (tx.getSource() == Source.SELF) queueOnNewBalance(); } finally { lock.unlock();