/** * Return true if the transaction in input is a valid imprinting transaction and contains the specified address in * one of its output, otherwise false. * @param tx the transaction to check if is valid imprinting * @param networkParameters the {@link NetworkParameters} * @param imprintingAddress the address to check for * @return true if it's an imprinting transaction otherwise false */ public static boolean isValidImprintingTransaction(Transaction tx, NetworkParameters networkParameters, Address imprintingAddress) { // Retrieve sender String sender = tx.getInput(0).getFromAddress().toBase58(); // Check output List<TransactionOutput> transactionOutputs = tx.getOutputs(); for (TransactionOutput to : transactionOutputs) { Address address = to.getAddressFromP2PKHScript(networkParameters); if (address != null && address.equals(imprintingAddress)) { return true; } } return false; }
@Nullable public static Address getWalletAddressOfReceived(final Transaction tx, final Wallet wallet) { for (final TransactionOutput output : tx.getOutputs()) { try { if (output.isMine(wallet)) { final Script script = output.getScriptPubKey(); return script.getToAddress(Constants.NETWORK_PARAMETERS, true); } } catch (final ScriptException x) { // swallow } } return null; }
/** * Marks all keys used in the transaction output as used in the wallet. * See {@link org.bitcoinj.wallet.DeterministicKeyChain#markKeyAsUsed(DeterministicKey)} for more info on this. */ private void markKeysAsUsed(Transaction tx) { keyChainGroupLock.lock(); try { for (TransactionOutput o : tx.getOutputs()) { try { Script script = o.getScriptPubKey(); if (script.isSentToRawPubKey()) { byte[] pubkey = script.getPubKey(); keyChainGroup.markPubKeyAsUsed(pubkey); } else if (script.isSentToAddress()) { byte[] pubkeyHash = script.getPubKeyHash(); keyChainGroup.markPubKeyHashAsUsed(pubkeyHash); } else if (script.isPayToScriptHash()) { Address a = Address.fromP2SHScript(tx.getParams(), script); keyChainGroup.markP2SHAddressAsUsed(a); } } catch (ScriptException e) { // Just means we didn't understand the output of this transaction: ignore it. log.warn("Could not parse tx output script: {}", e.toString()); } } } finally { keyChainGroupLock.unlock(); } }
@VisibleForTesting boolean isTxConsistent(final Transaction tx, final boolean isSpent) { boolean isActuallySpent = true; for (TransactionOutput o : tx.getOutputs()) { if (o.isAvailableForSpending()) { if (o.isMineOrWatched(this)) isActuallySpent = false; if (o.getSpentBy() != null) { log.error("isAvailableForSpending != spentBy"); return false; } } else { if (o.getSpentBy() == null) { log.error("isAvailableForSpending != spentBy"); return false; } } } return isActuallySpent == isSpent; }
/** * Returns all the outputs that match addresses or scripts added via {@link #addWatchedAddress(Address)} or * {@link #addWatchedScripts(java.util.List)}. * @param excludeImmatureCoinbases Whether to ignore outputs that are unspendable due to being immature. */ public List<TransactionOutput> getWatchedOutputs(boolean excludeImmatureCoinbases) { lock.lock(); keyChainGroupLock.lock(); try { LinkedList<TransactionOutput> candidates = Lists.newLinkedList(); for (Transaction tx : Iterables.concat(unspent.values(), pending.values())) { if (excludeImmatureCoinbases && !tx.isMature()) continue; for (TransactionOutput output : tx.getOutputs()) { if (!output.isAvailableForSpending()) continue; try { Script scriptPubKey = output.getScriptPubKey(); if (!watchedScripts.contains(scriptPubKey)) continue; candidates.add(output); } catch (ScriptException e) { // Ignore } } } return candidates; } finally { keyChainGroupLock.unlock(); lock.unlock(); } }
/** * Returns the balance of this wallet as calculated by the provided balanceType. */ public Coin getBalance(BalanceType balanceType) { lock.lock(); try { if (balanceType == BalanceType.AVAILABLE || balanceType == BalanceType.AVAILABLE_SPENDABLE) { List<TransactionOutput> candidates = calculateAllSpendCandidates(true, balanceType == BalanceType.AVAILABLE_SPENDABLE); CoinSelection selection = coinSelector.select(NetworkParameters.MAX_MONEY, candidates); return selection.valueGathered; } else if (balanceType == BalanceType.ESTIMATED || balanceType == BalanceType.ESTIMATED_SPENDABLE) { List<TransactionOutput> all = calculateAllSpendCandidates(false, balanceType == BalanceType.ESTIMATED_SPENDABLE); Coin value = Coin.ZERO; for (TransactionOutput out : all) value = value.add(out.getValue()); return value; } else { throw new AssertionError("Unknown balance type"); // Unreachable. } } finally { lock.unlock(); } }
/** * Returns the amount of bitcoin ever received via output. <b>This is not the balance!</b> If an output spends from a * transaction whose inputs are also to our wallet, the input amounts are deducted from the outputs contribution, with a minimum of zero * contribution. The idea behind this is we avoid double counting money sent to us. * @return the total amount of satoshis received, regardless of whether it was spent or not. */ public Coin getTotalReceived() { Coin total = Coin.ZERO; // Include outputs to us if they were not just change outputs, ie the inputs to us summed to less // than the outputs to us. for (Transaction tx: transactions.values()) { Coin txTotal = Coin.ZERO; for (TransactionOutput output : tx.getOutputs()) { if (output.isMine(this)) { txTotal = txTotal.add(output.getValue()); } } for (TransactionInput in : tx.getInputs()) { TransactionOutput prevOut = in.getConnectedOutput(); if (prevOut != null && prevOut.isMine(this)) { txTotal = txTotal.subtract(prevOut.getValue()); } } if (txTotal.isPositive()) { total = total.add(txTotal); } } return total; }
/** * Returns a list of all outputs that are being tracked by this wallet either from the {@link UTXOProvider} * (in this case the existence or not of private keys is ignored), or the wallets internal storage (the default) * taking into account the flags. * * @param excludeImmatureCoinbases Whether to ignore coinbase outputs that we will be able to spend in future once they mature. * @param excludeUnsignable Whether to ignore outputs that we are tracking but don't have the keys to sign for. */ public List<TransactionOutput> calculateAllSpendCandidates(boolean excludeImmatureCoinbases, boolean excludeUnsignable) { lock.lock(); try { List<TransactionOutput> candidates; if (vUTXOProvider == null) { candidates = new ArrayList<>(myUnspents.size()); for (TransactionOutput output : myUnspents) { if (excludeUnsignable && !canSignFor(output.getScriptPubKey())) continue; Transaction transaction = checkNotNull(output.getParentTransaction()); if (excludeImmatureCoinbases && !transaction.isMature()) continue; candidates.add(output); } } else { candidates = calculateAllSpendCandidatesFromUTXOProvider(excludeImmatureCoinbases); } return candidates; } finally { lock.unlock(); } }
private void calcBloomOutPointsLocked() { // TODO: This could be done once and then kept up to date. bloomOutPoints.clear(); Set<Transaction> all = new HashSet<>(); all.addAll(unspent.values()); all.addAll(spent.values()); all.addAll(pending.values()); for (Transaction tx : all) { for (TransactionOutput out : tx.getOutputs()) { try { if (isTxOutputBloomFilterable(out)) bloomOutPoints.add(out.getOutPointFor()); } catch (ScriptException e) { // If it is ours, we parsed the script correctly, so this shouldn't happen. throw new RuntimeException(e); } } } }
private int estimateBytesForSigning(CoinSelection selection) { int size = 0; for (TransactionOutput output : selection.gathered) { try { Script script = output.getScriptPubKey(); ECKey key = null; Script redeemScript = null; if (script.isSentToAddress()) { key = findKeyFromPubHash(script.getPubKeyHash()); checkNotNull(key, "Coin selection includes unspendable outputs"); } else if (script.isPayToScriptHash()) { redeemScript = findRedeemDataFromScriptHash(script.getPubKeyHash()).redeemScript; checkNotNull(redeemScript, "Coin selection includes unspendable outputs"); } size += script.getNumberOfBytesRequiredToSpend(key, redeemScript); } catch (ScriptException e) { // If this happens it means an output script in a wallet tx could not be understood. That should never // happen, if it does it means the wallet has got into an inconsistent state. throw new IllegalStateException(e); } } return size; }
/** * Construct a SendRequest for a CPFP (child-pays-for-parent) transaction. The resulting transaction is already * completed, so you should directly proceed to signing and broadcasting/committing the transaction. CPFP is * currently only supported by a few miners, so use with care. */ public static SendRequest childPaysForParent(Wallet wallet, Transaction parentTransaction, Coin feeRaise) { TransactionOutput outputToSpend = null; for (final TransactionOutput output : parentTransaction.getOutputs()) { if (output.isMine(wallet) && output.isAvailableForSpending() && output.getValue().isGreaterThan(feeRaise)) { outputToSpend = output; break; } } // TODO spend another confirmed output of own wallet if needed checkNotNull(outputToSpend, "Can't find adequately sized output that spends to us"); final Transaction tx = new Transaction(parentTransaction.getParams()); tx.addInput(outputToSpend); tx.addOutput(outputToSpend.getValue().subtract(feeRaise), wallet.freshAddress(KeyPurpose.CHANGE)); tx.setPurpose(Transaction.Purpose.RAISE_FEE); final SendRequest req = forTx(tx); req.completed = true; return req; }
@Override public CoinSelection select(Coin target, List<TransactionOutput> candidates) { ArrayList<TransactionOutput> selected = new ArrayList<>(); // Sort the inputs by age*value so we get the highest "coindays" spent. // TODO: Consider changing the wallets internal format to track just outputs and keep them ordered. ArrayList<TransactionOutput> sortedOutputs = new ArrayList<>(candidates); // When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting // them in order to improve performance. // TODO: Take in network parameters when instanatiated, and then test against the current network. Or just have a boolean parameter for "give me everything" if (!target.equals(NetworkParameters.MAX_MONEY)) { sortOutputs(sortedOutputs); } // Now iterate over the sorted outputs until we have got as close to the target as possible or a little // bit over (excessive value will be change). long total = 0; for (TransactionOutput output : sortedOutputs) { if (total >= target.value) break; // Only pick chain-included transactions, or transactions that are ours and pending. if (!shouldSelect(output.getParentTransaction())) continue; selected.add(output); total += output.getValue().value; } // Total may be lower than target here, if the given candidates were insufficient to create to requested // transaction. return new CoinSelection(Coin.valueOf(total), selected); }
@VisibleForTesting static void sortOutputs(ArrayList<TransactionOutput> outputs) { Collections.sort(outputs, new Comparator<TransactionOutput>() { @Override public int compare(TransactionOutput a, TransactionOutput b) { int depth1 = a.getParentTransactionDepthInBlocks(); int depth2 = b.getParentTransactionDepthInBlocks(); Coin aValue = a.getValue(); Coin bValue = b.getValue(); BigInteger aCoinDepth = BigInteger.valueOf(aValue.value).multiply(BigInteger.valueOf(depth1)); BigInteger bCoinDepth = BigInteger.valueOf(bValue.value).multiply(BigInteger.valueOf(depth2)); int c1 = bCoinDepth.compareTo(aCoinDepth); if (c1 != 0) return c1; // The "coin*days" destroyed are equal, sort by value alone to get the lowest transaction size. int c2 = bValue.compareTo(aValue); if (c2 != 0) return c2; // They are entirely equivalent (possibly pending) so sort by hash to ensure a total ordering. BigInteger aHash = a.getParentTransactionHash().toBigInteger(); BigInteger bHash = b.getParentTransactionHash().toBigInteger(); return aHash.compareTo(bHash); } }); }
private List<TransactionOutput> getUnlockedUnspentOutputs() { final List<TransactionOutput> outputs = new ArrayList<>(); final List<TransactionOutput> candidates = wallet.calculateAllSpendCandidates(false, false); final long currentTimeSec = org.bitcoinj.core.Utils.currentTimeSeconds(); for (TransactionOutput txOut : candidates) { byte[] addressHash = txOut.getScriptPubKey().getPubKeyHash(); TimeLockedAddress tla = findTimeLockedAddressByHash(addressHash); if (tla != null) { long lockTime = tla.getLockTime(); if (BitcoinUtils.isAfterLockTime(currentTimeSec, lockTime)) { outputs.add(txOut); Log.d(TAG, "getUnlockedUnspentOutputs - unlocked output: " + txOut); } } } return outputs; }
private List<TransactionSignature> signTransaction(Transaction tx) throws CoinbleskException { final List<TransactionInput> inputs = tx.getInputs(); final List<TransactionSignature> signatures = new ArrayList<>(inputs.size()); for (int i = 0; i < inputs.size(); ++i) { TransactionInput txIn = inputs.get(i); TransactionOutput prevTxOut = txIn.getConnectedOutput(); byte[] sentToHash = prevTxOut.getScriptPubKey().getPubKeyHash(); TimeLockedAddress tla = findTimeLockedAddressByHash(sentToHash); if (tla == null) { throw new CoinbleskException(String.format(Locale.US, "Could not sign input (index=%d, pubKeyHash=%s)", i, org.bitcoinj.core.Utils.HEX.encode(sentToHash))); } byte[] redeemScript = tla.createRedeemScript().getProgram(); TransactionSignature signature = tx.calculateSignature( i, multisigClientKey, redeemScript, Transaction.SigHash.ALL, false); signatures.add(signature); } return signatures; }
private String sentToAddressOfTx(TransactionWrapper txWrapper) { // positive amount (for our wallet) = incoming tx boolean isIncoming = txWrapper.getAmount().isPositive(); List<TransactionOutput> outputs = txWrapper.getTransaction().getOutputs(); // due to space limitations we only display if we have a common 2 outputs tx if (outputs.size() <= 2) { for (TransactionOutput o : outputs) { // ignore: // tx to me, output not, i.e. change to sender // tx from me, output mine, i.e. change to me boolean cont = (isIncoming && !o.isMineOrWatched(walletServiceBinder.getWallet())) || (!isIncoming && o.isMineOrWatched(walletServiceBinder.getWallet())); if (cont) { continue; } Address addr = o.getScriptPubKey().getToAddress(appConfig.getNetworkParameters()); return addr.toBase58(); } } return null; }
private synchronized void refreshOutputs() { adapter.getItems().clear(); adapter.getItems().addAll(walletServiceBinder.getUnspentInstantOutputs()); Collections.sort(adapter.getItems(), new Comparator<TransactionOutput>() { @Override public int compare(TransactionOutput lhs, TransactionOutput rhs) { Sha256Hash lhsHash = lhs.getParentTransactionHash(); Sha256Hash rhsHash = lhs.getParentTransactionHash(); if (lhsHash != null && rhsHash != null) { int hashCmp = lhsHash.toString().compareTo(rhsHash.toString()); if (hashCmp != 0) return hashCmp; else return Long.compare(lhs.getIndex(), rhs.getIndex()); } else if (lhsHash != null) return -1; else if (rhsHash != null) return 1; else return 0; } }); adapter.notifyDataSetChanged(); }
/** * Returns a list of all outputs that are being tracked by this wallet either from the {@link UTXOProvider} * (in this case the existence or not of private keys is ignored), or the wallets internal storage (the default) * taking into account the flags. * * @param excludeImmatureCoinbases Whether to ignore coinbase outputs that we will be able to spend in future once they mature. * @param excludeUnsignable Whether to ignore outputs that we are tracking but don't have the keys to sign for. */ public List<TransactionOutput> calculateAllSpendCandidates(boolean excludeImmatureCoinbases, boolean excludeUnsignable) { lock.lock(); try { List<TransactionOutput> candidates; if (vUTXOProvider == null) { candidates = new ArrayList<TransactionOutput>(myUnspents.size()); for (TransactionOutput output : myUnspents) { if (excludeUnsignable && !canSignFor(output.getScriptPubKey())) continue; Transaction transaction = checkNotNull(output.getParentTransaction()); if (excludeImmatureCoinbases && !transaction.isMature()) continue; candidates.add(output); } } else { candidates = calculateAllSpendCandidatesFromUTXOProvider(excludeImmatureCoinbases); } return candidates; } finally { lock.unlock(); } }
private void calcBloomOutPointsLocked() { // TODO: This could be done once and then kept up to date. bloomOutPoints.clear(); Set<Transaction> all = new HashSet<Transaction>(); all.addAll(unspent.values()); all.addAll(spent.values()); all.addAll(pending.values()); for (Transaction tx : all) { for (TransactionOutput out : tx.getOutputs()) { try { if (isTxOutputBloomFilterable(out)) bloomOutPoints.add(out.getOutPointFor()); } catch (ScriptException e) { // If it is ours, we parsed the script correctly, so this shouldn't happen. throw new RuntimeException(e); } } } }
@Override public CoinSelection select(Coin target, List<TransactionOutput> candidates) { ArrayList<TransactionOutput> selected = new ArrayList<TransactionOutput>(); // Sort the inputs by age*value so we get the highest "coindays" spent. // TODO: Consider changing the wallets internal format to track just outputs and keep them ordered. ArrayList<TransactionOutput> sortedOutputs = new ArrayList<TransactionOutput>(candidates); // When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting // them in order to improve performance. // TODO: Take in network parameters when instanatiated, and then test against the current network. Or just have a boolean parameter for "give me everything" if (!target.equals(NetworkParameters.MAX_MONEY)) { sortOutputs(sortedOutputs); } // Now iterate over the sorted outputs until we have got as close to the target as possible or a little // bit over (excessive value will be change). long total = 0; for (TransactionOutput output : sortedOutputs) { if (total >= target.value) break; // Only pick chain-included transactions, or transactions that are ours and pending. if (!shouldSelect(output.getParentTransaction())) continue; selected.add(output); total += output.getValue().value; } // Total may be lower than target here, if the given candidates were insufficient to create to requested // transaction. return new CoinSelection(Coin.valueOf(total), selected); }
@Test public void isConsistent_duplicates() throws Exception { // This test ensures that isConsistent catches duplicate transactions, eg, because we submitted the same block // twice (this is not allowed). Transaction tx = createFakeTx(PARAMS, COIN, myAddress); TransactionOutput output = new TransactionOutput(PARAMS, tx, valueOf(0, 5), OTHER_ADDRESS); tx.addOutput(output); wallet.receiveFromBlock(tx, null, BlockChain.NewBlockType.BEST_CHAIN, 0); assertTrue(wallet.isConsistent()); Transaction txClone = PARAMS.getDefaultSerializer().makeTransaction(tx.bitcoinSerialize()); try { wallet.receiveFromBlock(txClone, null, BlockChain.NewBlockType.BEST_CHAIN, 0); fail("Illegal argument not thrown when it should have been."); } catch (IllegalStateException ex) { // expected } }
@Test public void transactions() throws Exception { // This test covers a bug in which Transaction.getValueSentFromMe was calculating incorrectly. Transaction tx = createFakeTx(PARAMS, COIN, myAddress); // Now add another output (ie, change) that goes to some other address. TransactionOutput output = new TransactionOutput(PARAMS, tx, valueOf(0, 5), OTHER_ADDRESS); tx.addOutput(output); // Note that tx is no longer valid: it spends more than it imports. However checking transactions balance // correctly isn't possible in SPV mode because value is a property of outputs not inputs. Without all // transactions you can't check they add up. sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, tx); // Now the other guy creates a transaction which spends that change. Transaction tx2 = new Transaction(PARAMS); tx2.addInput(output); tx2.addOutput(new TransactionOutput(PARAMS, tx2, valueOf(0, 5), myAddress)); // tx2 doesn't send any coins from us, even though the output is in the wallet. assertEquals(ZERO, tx2.getValueSentFromMe(wallet)); }
@Test public void bounce() throws Exception { // This test covers bug 64 (False double spends). Check that if we create a spend and it's immediately sent // back to us, this isn't considered as a double spend. Coin coin1 = COIN; sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, coin1); // Send half to some other guy. Sending only half then waiting for a confirm is important to ensure the tx is // in the unspent pool, not pending or spent. Coin coinHalf = valueOf(0, 50); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(1, wallet.getTransactions(true).size()); Transaction outbound1 = wallet.createSend(OTHER_ADDRESS, coinHalf); wallet.commitTx(outbound1); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, outbound1); assertTrue(outbound1.getWalletOutputs(wallet).size() <= 1); //the change address at most // That other guy gives us the coins right back. Transaction inbound2 = new Transaction(PARAMS); inbound2.addOutput(new TransactionOutput(PARAMS, inbound2, coinHalf, myAddress)); assertTrue(outbound1.getWalletOutputs(wallet).size() >= 1); inbound2.addInput(outbound1.getOutputs().get(0)); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, inbound2); assertEquals(coin1, wallet.getBalance()); }
private static Transaction shuffleTransaction (Transaction transaction) { Transaction shuffledTransaction = new Transaction(Constants.getNetwork()); List<TransactionInput> shuffledInputs = new ArrayList<>(transaction.getInputs()); List<TransactionOutput> shuffledOutputs = new ArrayList<>(transaction.getOutputs()); Collections.shuffle(shuffledInputs); Collections.shuffle(shuffledOutputs); for (TransactionInput input : shuffledInputs) { shuffledTransaction.addInput(input); } for (TransactionOutput output : shuffledOutputs) { shuffledTransaction.addOutput(output); } return shuffledTransaction; }
@Override public TransactionOutput getOutput(int index) { checkIndex(index); if (trimmedOutputs == null) { return super.getOutput(index); } else { TransactionOutput output = trimmedOutputs.get(index); // Trimmed outputs are empty if (output == null) { return EmptyTransactionOutput.get(); } else { return output; } } }
public List<TransactionOutput> getOutputs(boolean includeEmptyOutputs) { if (trimmedOutputs == null) { return super.getOutputs(); } else { ImmutableList.Builder<TransactionOutput> listBuilder = ImmutableList.builder(); TransactionOutput emptyOutput = EmptyTransactionOutput.get(); for (int i = 0; i < numberOfOutputs; i++) { TransactionOutput output = trimmedOutputs.get(i); // Trimmed outputs are empty if (output == null && includeEmptyOutputs) { listBuilder.add(emptyOutput); } else if (output != null) { listBuilder.add(output); } } return listBuilder.build(); } }
@Nullable public Value getRawTxFee(TransactionWatcherWallet wallet) { Preconditions.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; }
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); } } } } }
private Map<TrimmedOutPoint, OutPointOutput> getDummyUtxoSet() throws AddressMalformedException, JSONException { final HashMap<TrimmedOutPoint, OutPointOutput> unspentOutputs = new HashMap<>(); for (int i = 0; i < statuses.length; i++) { BitAddress address = (BitAddress) DOGE.newAddress(addresses.get(i)); JSONArray utxoArray = new JSONArray(unspent[i]); for (int j = 0; j < utxoArray.length(); j++) { JSONObject utxoObject = utxoArray.getJSONObject(j); //"[{\"tx_hash\": \"ef74da273e8a77e2d60b707414fb7e0ccb35c7b1b936800a49fe953195b1799f\", \"tx_pos\": 11, \"value\": 500000000, \"height\": 160267}]", TrimmedOutPoint outPoint = new TrimmedOutPoint(DOGE, utxoObject.getInt("tx_pos"), new Sha256Hash(utxoObject.getString("tx_hash"))); TransactionOutput output = new TransactionOutput(DOGE, null, Coin.valueOf(utxoObject.getLong("value")), address); OutPointOutput utxo = new OutPointOutput(outPoint, output, false); unspentOutputs.put(outPoint, utxo); } } return unspentOutputs; }
@Nullable public static Address getToAddressOfSent(final Transaction tx, final Wallet wallet) { for (final TransactionOutput output : tx.getOutputs()) { try { if (!output.isMine(wallet)) { final Script script = output.getScriptPubKey(); return script.getToAddress(Constants.NETWORK_PARAMETERS, true); } } catch (final ScriptException x) { // swallow } } return null; }
/** * Takes in a transaction and a private key and returns a signature (if possible) * as a Bytestring object. */ public Bytestring getSignature(org.bitcoinj.core.Transaction signTx, ECKey privKey) { org.bitcoinj.core.Transaction copyTx = signTx; for (int i = 0; i < copyTx.getInputs().size(); i++) { TransactionInput input = copyTx.getInput(i); TransactionOutput connectedOutput = input.getConnectedOutput(); Sha256Hash hash = copyTx.hashForSignature(i, connectedOutput.getScriptPubKey(), org.bitcoinj.core.Transaction.SigHash.ALL, false); ECKey.ECDSASignature ecSig = privKey.sign(hash); TransactionSignature txSig = new TransactionSignature(ecSig, org.bitcoinj.core.Transaction.SigHash.ALL, false); byte[] originalScript = input.getScriptBytes().clone(); Script inputScript = ScriptBuilder.createInputScript(txSig, ECKey.fromPublicOnly(privKey.getPubKey())); input.setScriptSig(inputScript); try { input.verify(connectedOutput); return new Bytestring(inputScript.getProgram()); } catch (VerificationException e) { input.setScriptSig(this.bytestringToInputScript(new Bytestring(originalScript))); } } return null; }