Commit 4e29e4fb authored by Andreas Schildbach's avatar Andreas Schildbach

Wallet: Make SendRequest a top level class.

parent 1e66b9a8
......@@ -26,6 +26,7 @@ import org.bitcoinj.core.*;
import org.bitcoinj.crypto.TransactionSignature;
import org.bitcoinj.script.Script;
import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.SendRequest;
import org.bitcoinj.wallet.Wallet;
import org.bitcoinj.wallet.listeners.WalletCoinsReceivedEventListener;
import org.slf4j.Logger;
......@@ -239,7 +240,7 @@ public abstract class PaymentChannelClientState {
* channel. For example if you want it to only use specific coins, you can adjust the coin selector here.
* The default implementation does nothing.
*/
protected void editContractSendRequest(Wallet.SendRequest req) {
protected void editContractSendRequest(SendRequest req) {
}
/**
......
......@@ -16,6 +16,7 @@
package org.bitcoinj.protocols.channels;
import org.bitcoinj.wallet.SendRequest;
import org.bitcoinj.wallet.Wallet;
import com.google.common.collect.ImmutableList;
......@@ -215,13 +216,13 @@ public abstract class PaymentChannelServerState {
}
// Create a payment transaction with valueToMe going back to us
protected synchronized Wallet.SendRequest makeUnsignedChannelContract(Coin valueToMe) {
protected synchronized SendRequest makeUnsignedChannelContract(Coin valueToMe) {
Transaction tx = new Transaction(wallet.getParams());
if (!getTotalValue().subtract(valueToMe).equals(Coin.ZERO)) {
tx.addOutput(getTotalValue().subtract(valueToMe), getClientKey().toAddress(wallet.getParams()));
}
tx.addInput(contract.getOutput(0));
return Wallet.SendRequest.forTx(tx);
return SendRequest.forTx(tx);
}
/**
......@@ -248,7 +249,7 @@ public abstract class PaymentChannelServerState {
if (newValueToMe.compareTo(bestValueToMe) < 0)
throw new ValueOutOfRangeException("Attempt to roll back payment on the channel.");
Wallet.SendRequest req = makeUnsignedChannelContract(newValueToMe);
SendRequest req = makeUnsignedChannelContract(newValueToMe);
if (!fullyUsedUp && refundSize.isLessThan(req.tx.getOutput(0).getMinNonDustValue()))
throw new ValueOutOfRangeException("Attempt to refund negative value or value too small to be accepted by the network");
......
......@@ -23,6 +23,7 @@ import org.bitcoinj.crypto.TransactionSignature;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.wallet.AllowUnconfirmedCoinSelector;
import org.bitcoinj.wallet.SendRequest;
import org.bitcoinj.wallet.Wallet;
import org.spongycastle.crypto.params.KeyParameter;
import com.google.common.annotations.VisibleForTesting;
......@@ -136,7 +137,7 @@ public class PaymentChannelV1ClientState extends PaymentChannelClientState {
TransactionOutput multisigOutput = template.addOutput(totalValue, ScriptBuilder.createMultiSigOutputScript(2, keys));
if (multisigOutput.isDust())
throw new ValueOutOfRangeException("totalValue too small to use");
Wallet.SendRequest req = Wallet.SendRequest.forTx(template);
SendRequest req = SendRequest.forTx(template);
req.coinSelector = AllowUnconfirmedCoinSelector.get();
editContractSendRequest(req);
req.shuffleOutputs = false; // TODO: Fix things so shuffling is usable.
......
......@@ -21,6 +21,7 @@ import org.bitcoinj.core.*;
import org.bitcoinj.crypto.TransactionSignature;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.wallet.SendRequest;
import org.bitcoinj.wallet.Wallet;
import com.google.common.util.concurrent.FutureCallback;
......@@ -210,7 +211,7 @@ public class PaymentChannelV1ServerState extends PaymentChannelServerState {
}
Transaction tx = null;
try {
Wallet.SendRequest req = makeUnsignedChannelContract(bestValueToMe);
SendRequest req = makeUnsignedChannelContract(bestValueToMe);
tx = req.tx;
// Provide a throwaway signature so that completeTx won't complain out about unsigned inputs it doesn't
// know how to sign. Note that this signature does actually have to be valid, so we can't use a dummy
......
......@@ -24,6 +24,7 @@ import org.bitcoinj.crypto.TransactionSignature;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.wallet.AllowUnconfirmedCoinSelector;
import org.bitcoinj.wallet.SendRequest;
import org.bitcoinj.wallet.Wallet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -110,7 +111,7 @@ public class PaymentChannelV2ClientState extends PaymentChannelClientState {
ScriptBuilder.createP2SHOutputScript(redeemScript));
if (transactionOutput.isDust())
throw new ValueOutOfRangeException("totalValue too small to use");
Wallet.SendRequest req = Wallet.SendRequest.forTx(template);
SendRequest req = SendRequest.forTx(template);
req.coinSelector = AllowUnconfirmedCoinSelector.get();
editContractSendRequest(req);
req.shuffleOutputs = false; // TODO: Fix things so shuffling is usable.
......
......@@ -26,6 +26,7 @@ import org.bitcoinj.core.*;
import org.bitcoinj.crypto.TransactionSignature;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.wallet.SendRequest;
import org.bitcoinj.wallet.Wallet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -165,7 +166,7 @@ public class PaymentChannelV2ServerState extends PaymentChannelServerState {
}
Transaction tx = null;
try {
Wallet.SendRequest req = makeUnsignedChannelContract(bestValueToMe);
SendRequest req = makeUnsignedChannelContract(bestValueToMe);
tx = req.tx;
// Provide a throwaway signature so that completeTx won't complain out about unsigned inputs it doesn't
// know how to sign. Note that this signature does actually have to be valid, so we can't use a dummy
......
......@@ -20,7 +20,7 @@ import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.protocols.payments.PaymentProtocol.PkiVerificationData;
import org.bitcoinj.uri.BitcoinURI;
import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.Wallet;
import org.bitcoinj.wallet.SendRequest;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.util.concurrent.ListenableFuture;
......@@ -293,13 +293,13 @@ public class PaymentSession {
}
/**
* Returns a {@link Wallet.SendRequest} suitable for broadcasting to the network.
* Returns a {@link SendRequest} suitable for broadcasting to the network.
*/
public Wallet.SendRequest getSendRequest() {
public SendRequest getSendRequest() {
Transaction tx = new Transaction(params);
for (Protos.Output output : paymentDetails.getOutputsList())
tx.addOutput(new TransactionOutput(params, tx, Coin.valueOf(output.getAmount()), output.getScript().toByteArray()));
return Wallet.SendRequest.forTx(tx).fromPaymentDetails(paymentDetails);
return SendRequest.forTx(tx).fromPaymentDetails(paymentDetails);
}
/**
......
......@@ -50,7 +50,7 @@ public interface EncryptableKeyChain extends KeyChain {
/**
* Decrypt the key chain with the given AES key and whatever {@link KeyCrypter} is already set. Note that if you
* just want to spend money from an encrypted wallet, don't decrypt the whole thing first. Instead, set the
* {@link org.bitcoinj.wallet.Wallet.SendRequest#aesKey} field before asking the wallet to build the send.
* {@link org.bitcoinj.wallet.SendRequest#aesKey} field before asking the wallet to build the send.
*
* @param aesKey AES key to use (normally created using KeyCrypter#deriveKey and cached as it is time consuming to
* create from a password)
......
/*
* Copyright 2013 Google Inc.
* Copyright 2014 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.wallet;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.math.BigInteger;
import java.util.Date;
import org.bitcoin.protocols.payments.Protos.PaymentDetails;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Context;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.utils.ExchangeRate;
import org.bitcoinj.wallet.KeyChain.KeyPurpose;
import org.bitcoinj.wallet.Wallet.MissingSigsMode;
import org.spongycastle.crypto.params.KeyParameter;
import com.google.common.base.MoreObjects;
/**
* A SendRequest gives the wallet information about precisely how to send money to a recipient or set of recipients.
* Static methods are provided to help you create SendRequests and there are a few helper methods on the wallet that
* just simplify the most common use cases. You may wish to customize a SendRequest if you want to attach a fee or
* modify the change address.
*/
public class SendRequest {
/**
* <p>A transaction, probably incomplete, that describes the outline of what you want to do. This typically will
* mean it has some outputs to the intended destinations, but no inputs or change address (and therefore no
* fees) - the wallet will calculate all that for you and update tx later.</p>
*
* <p>Be careful when adding outputs that you check the min output value
* ({@link TransactionOutput#getMinNonDustValue(Coin)}) to avoid the whole transaction being rejected
* because one output is dust.</p>
*
* <p>If there are already inputs to the transaction, make sure their out point has a connected output,
* otherwise their value will be added to fee. Also ensure they are either signed or are spendable by a wallet
* key, otherwise the behavior of {@link Wallet#completeTx(Wallet.SendRequest)} is undefined (likely
* RuntimeException).</p>
*/
public Transaction tx;
/**
* When emptyWallet is set, all coins selected by the coin selector are sent to the first output in tx
* (its value is ignored and set to {@link org.bitcoinj.wallet.Wallet#getBalance()} - the fees required
* for the transaction). Any additional outputs are removed.
*/
public boolean emptyWallet = false;
/**
* "Change" means the difference between the value gathered by a transactions inputs (the size of which you
* don't really control as it depends on who sent you money), and the value being sent somewhere else. The
* change address should be selected from this wallet, normally. <b>If null this will be chosen for you.</b>
*/
public Address changeAddress = null;
/**
* <p>A transaction can have a fee attached, which is defined as the difference between the input values
* and output values. Any value taken in that is not provided to an output can be claimed by a miner. This
* is how mining is incentivized in later years of the Bitcoin system when inflation drops. It also provides
* a way for people to prioritize their transactions over others and is used as a way to make denial of service
* attacks expensive.</p>
*
* <p>This is a dynamic fee (in satoshis) which will be added to the transaction for each kilobyte in size
* including the first. This is useful as as miners usually sort pending transactions by their fee per unit size
* when choosing which transactions to add to a block. Note that, to keep this equivalent to Bitcoin Core
* definition, a kilobyte is defined as 1000 bytes, not 1024.</p>
*/
public Coin feePerKb = Context.get().getFeePerKb();
/**
* <p>Requires that there be enough fee for a default Bitcoin Core to at least relay the transaction.
* (ie ensure the transaction will not be outright rejected by the network). Defaults to true, you should
* only set this to false if you know what you're doing.</p>
*
* <p>Note that this does not enforce certain fee rules that only apply to transactions which are larger than
* 26,000 bytes. If you get a transaction which is that large, you should set a feePerKb of at least
* {@link Transaction#REFERENCE_DEFAULT_MIN_TX_FEE}.</p>
*/
public boolean ensureMinRequiredFee = Context.get().isEnsureMinRequiredFee();
/**
* If true (the default), the inputs will be signed.
*/
public boolean signInputs = true;
/**
* The AES key to use to decrypt the private keys before signing.
* If null then no decryption will be performed and if decryption is required an exception will be thrown.
* You can get this from a password by doing wallet.getKeyCrypter().deriveKey(password).
*/
public KeyParameter aesKey = null;
/**
* If not null, the {@link org.bitcoinj.wallet.CoinSelector} to use instead of the wallets default. Coin selectors are
* responsible for choosing which transaction outputs (coins) in a wallet to use given the desired send value
* amount.
*/
public CoinSelector coinSelector = null;
/**
* If true (the default), the outputs will be shuffled during completion to randomize the location of the change
* output, if any. This is normally what you want for privacy reasons but in unit tests it can be annoying
* so it can be disabled here.
*/
public boolean shuffleOutputs = true;
/**
* Specifies what to do with missing signatures left after completing this request. Default strategy is to
* throw an exception on missing signature ({@link MissingSigsMode#THROW}).
* @see MissingSigsMode
*/
public MissingSigsMode missingSigsMode = MissingSigsMode.THROW;
/**
* If not null, this exchange rate is recorded with the transaction during completion.
*/
public ExchangeRate exchangeRate = null;
/**
* If not null, this memo is recorded with the transaction during completion. It can be used to record the memo
* of the payment request that initiated the transaction.
*/
public String memo = null;
// Tracks if this has been passed to wallet.completeTx already: just a safety check.
boolean completed;
private SendRequest() {}
/**
* <p>Creates a new SendRequest to the given address for the given value.</p>
*
* <p>Be very careful when value is smaller than {@link Transaction#MIN_NONDUST_OUTPUT} as the transaction will
* likely be rejected by the network in this case.</p>
*/
public static SendRequest to(Address destination, Coin value) {
SendRequest req = new SendRequest();
final NetworkParameters parameters = destination.getParameters();
checkNotNull(parameters, "Address is for an unknown network");
req.tx = new Transaction(parameters);
req.tx.addOutput(value, destination);
return req;
}
/**
* <p>Creates a new SendRequest to the given pubkey for the given value.</p>
*
* <p>Be careful to check the output's value is reasonable using
* {@link TransactionOutput#getMinNonDustValue(Coin)} afterwards or you risk having the transaction
* rejected by the network. Note that using {@link SendRequest#to(Address, Coin)} will result
* in a smaller output, and thus the ability to use a smaller output value without rejection.</p>
*/
public static SendRequest to(NetworkParameters params, ECKey destination, Coin value) {
SendRequest req = new SendRequest();
req.tx = new Transaction(params);
req.tx.addOutput(value, destination);
return req;
}
/** Simply wraps a pre-built incomplete transaction provided by you. */
public static SendRequest forTx(Transaction tx) {
SendRequest req = new SendRequest();
req.tx = tx;
return req;
}
public static SendRequest emptyWallet(Address destination) {
SendRequest req = new SendRequest();
final NetworkParameters parameters = destination.getParameters();
checkNotNull(parameters, "Address is for an unknown network");
req.tx = new Transaction(parameters);
req.tx.addOutput(Coin.ZERO, destination);
req.emptyWallet = true;
return req;
}
/**
* 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;
}
public static SendRequest toCLTVPaymentChannel(NetworkParameters params, Date releaseTime, ECKey from, ECKey to, Coin value) {
long time = releaseTime.getTime() / 1000L;
checkArgument(time >= Transaction.LOCKTIME_THRESHOLD, "Release time was too small");
return toCLTVPaymentChannel(params, BigInteger.valueOf(time), from, to, value);
}
public static SendRequest toCLTVPaymentChannel(NetworkParameters params, int releaseBlock, ECKey from, ECKey to, Coin value) {
checkArgument(0 <= releaseBlock && releaseBlock < Transaction.LOCKTIME_THRESHOLD, "Block number was too large");
return toCLTVPaymentChannel(params, BigInteger.valueOf(releaseBlock), from, to, value);
}
public static SendRequest toCLTVPaymentChannel(NetworkParameters params, BigInteger time, ECKey from, ECKey to, Coin value) {
SendRequest req = new SendRequest();
Script output = ScriptBuilder.createCLTVPaymentChannelOutput(time, from, to);
req.tx = new Transaction(params);
req.tx.addOutput(value, output);
return req;
}
/** Copy data from payment request. */
public SendRequest fromPaymentDetails(PaymentDetails paymentDetails) {
if (paymentDetails.hasMemo())
this.memo = paymentDetails.getMemo();
return this;
}
@Override
public String toString() {
// print only the user-settable fields
MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this).omitNullValues();
helper.add("emptyWallet", emptyWallet);
helper.add("changeAddress", changeAddress);
helper.add("feePerKb", feePerKb);
helper.add("ensureMinRequiredFee", ensureMinRequiredFee);
helper.add("signInputs", signInputs);
helper.add("aesKey", aesKey != null ? "set" : null); // careful to not leak the key
helper.add("coinSelector", coinSelector);
helper.add("shuffleOutputs", shuffleOutputs);
return helper.toString();
}
}
\ No newline at end of file
......@@ -62,7 +62,6 @@ import org.bitcoinj.crypto.*;
import org.bitcoinj.script.*;
import org.bitcoinj.signers.*;
import org.bitcoinj.utils.*;
import org.bitcoinj.wallet.KeyChain.KeyPurpose;
import org.bitcoinj.wallet.Protos.Wallet.*;
import org.bitcoinj.wallet.WalletTransaction.*;
import org.bitcoinj.wallet.listeners.KeyChainEventListener;
......@@ -3708,232 +3707,6 @@ public class Wallet extends BaseTaggableObject
THROW
}
/**
* A SendRequest gives the wallet information about precisely how to send money to a recipient or set of recipients.
* Static methods are provided to help you create SendRequests and there are a few helper methods on the wallet that
* just simplify the most common use cases. You may wish to customize a SendRequest if you want to attach a fee or
* modify the change address.
*/
public static class SendRequest {
/**
* <p>A transaction, probably incomplete, that describes the outline of what you want to do. This typically will
* mean it has some outputs to the intended destinations, but no inputs or change address (and therefore no
* fees) - the wallet will calculate all that for you and update tx later.</p>
*
* <p>Be careful when adding outputs that you check the min output value
* ({@link TransactionOutput#getMinNonDustValue(Coin)}) to avoid the whole transaction being rejected
* because one output is dust.</p>
*
* <p>If there are already inputs to the transaction, make sure their out point has a connected output,
* otherwise their value will be added to fee. Also ensure they are either signed or are spendable by a wallet
* key, otherwise the behavior of {@link Wallet#completeTx(Wallet.SendRequest)} is undefined (likely
* RuntimeException).</p>
*/
public Transaction tx;
/**
* When emptyWallet is set, all coins selected by the coin selector are sent to the first output in tx
* (its value is ignored and set to {@link org.bitcoinj.wallet.Wallet#getBalance()} - the fees required
* for the transaction). Any additional outputs are removed.
*/
public boolean emptyWallet = false;
/**
* "Change" means the difference between the value gathered by a transactions inputs (the size of which you
* don't really control as it depends on who sent you money), and the value being sent somewhere else. The
* change address should be selected from this wallet, normally. <b>If null this will be chosen for you.</b>
*/
public Address changeAddress = null;
/**
* <p>A transaction can have a fee attached, which is defined as the difference between the input values
* and output values. Any value taken in that is not provided to an output can be claimed by a miner. This
* is how mining is incentivized in later years of the Bitcoin system when inflation drops. It also provides
* a way for people to prioritize their transactions over others and is used as a way to make denial of service
* attacks expensive.</p>
*
* <p>This is a dynamic fee (in satoshis) which will be added to the transaction for each kilobyte in size
* including the first. This is useful as as miners usually sort pending transactions by their fee per unit size
* when choosing which transactions to add to a block. Note that, to keep this equivalent to Bitcoin Core
* definition, a kilobyte is defined as 1000 bytes, not 1024.</p>
*/
public Coin feePerKb = Context.get().getFeePerKb();
/**
* <p>Requires that there be enough fee for a default Bitcoin Core to at least relay the transaction.
* (ie ensure the transaction will not be outright rejected by the network). Defaults to true, you should
* only set this to false if you know what you're doing.</p>
*
* <p>Note that this does not enforce certain fee rules that only apply to transactions which are larger than
* 26,000 bytes. If you get a transaction which is that large, you should set a feePerKb of at least
* {@link Transaction#REFERENCE_DEFAULT_MIN_TX_FEE}.</p>
*/
public boolean ensureMinRequiredFee = Context.get().isEnsureMinRequiredFee();
/**
* If true (the default), the inputs will be signed.
*/
public boolean signInputs = true;
/**
* The AES key to use to decrypt the private keys before signing.
* If null then no decryption will be performed and if decryption is required an exception will be thrown.
* You can get this from a password by doing wallet.getKeyCrypter().deriveKey(password).
*/
public KeyParameter aesKey = null;
/**
* If not null, the {@link org.bitcoinj.wallet.CoinSelector} to use instead of the wallets default. Coin selectors are
* responsible for choosing which transaction outputs (coins) in a wallet to use given the desired send value
* amount.
*/
public CoinSelector coinSelector = null;
/**
* If true (the default), the outputs will be shuffled during completion to randomize the location of the change
* output, if any. This is normally what you want for privacy reasons but in unit tests it can be annoying
* so it can be disabled here.
*/
public boolean shuffleOutputs = true;
/**
* Specifies what to do with missing signatures left after completing this request. Default strategy is to
* throw an exception on missing signature ({@link MissingSigsMode#THROW}).
* @see MissingSigsMode
*/
public MissingSigsMode missingSigsMode = MissingSigsMode.THROW;
/**
* If not null, this exchange rate is recorded with the transaction during completion.
*/
public ExchangeRate exchangeRate = null;
/**
* If not null, this memo is recorded with the transaction during completion. It can be used to record the memo
* of the payment request that initiated the transaction.
*/
public String memo = null;
// Tracks if this has been passed to wallet.completeTx already: just a safety check.
private boolean completed;
private SendRequest() {}
/**
* <p>Creates a new SendRequest to the given address for the given value.</p>
*
* <p>Be very careful when value is smaller than {@link Transaction#MIN_NONDUST_OUTPUT} as the transaction will
* likely be rejected by the network in this case.</p>
*/
public static SendRequest to(Address destination, Coin value) {
SendRequest req = new SendRequest();
final NetworkParameters parameters = destination.getParameters();
checkNotNull(parameters, "Address is for an unknown network");
req.tx = new Transaction(parameters);
req.tx.addOutput(value, destination);
return req;
}
/**
* <p>Creates a new SendRequest to the given pubkey for the given value.</p>
*
* <p>Be careful to check the output's value is reasonable using
* {@link TransactionOutput#getMinNonDustValue(Coin)} afterwards or you risk having the transaction
* rejected by the network. Note that using {@link SendRequest#to(Address, Coin)} will result
* in a smaller output, and thus the ability to use a smaller output value without rejection.</p>
*/
public static SendRequest to(NetworkParameters params, ECKey destination, Coin value) {
SendRequest req = new SendRequest();
req.tx = new Transaction(params);
req.tx.addOutput(value, destination);
return req;
}
/** Simply wraps a pre-built incomplete transaction provided by you. */
public static SendRequest forTx(Transaction tx) {
SendRequest req = new SendRequest();
req.tx = tx;
return req;
}
public static SendRequest emptyWallet(Address destination) {
SendRequest req = new SendRequest();
final NetworkParameters parameters = destination.getParameters();
checkNotNull(parameters, "Address is for an unknown network");
req.tx = new Transaction(parameters);
req.tx.addOutput(Coin.ZERO, destination);
req.emptyWallet = true;
return req;
}
/**
* 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;
}
public static SendRequest toCLTVPaymentChannel(NetworkParameters params, Date releaseTime, ECKey from, ECKey to, Coin value) {
long time = releaseTime.getTime() / 1000L;
checkArgument(time >= Transaction.LOCKTIME_THRESHOLD, "Release time was too small");
return toCLTVPaymentChannel(params, BigInteger.valueOf(time), from, to, value);
}
public static SendRequest toCLTVPaymentChannel(NetworkParameters params, int releaseBlock, ECKey from, ECKey to, Coin value) {
checkArgument(0 <= releaseBlock && releaseBlock < Transaction.LOCKTIME_THRESHOLD, "Block number was too large");
return toCLTVPaymentChannel(params, BigInteger.valueOf(releaseBlock), from, to, value);
}
public static SendRequest toCLTVPaymentChannel(NetworkParameters params, BigInteger time, ECKey from, ECKey to, Coin value) {
SendRequest req = new SendRequest();
Script output = ScriptBuilder.createCLTVPaymentChannelOutput(time, from, to);
req.tx = new Transaction(params);
req.tx.addOutput(value, output);
return req;
}
/** Copy data from payment request. */
public SendRequest fromPaymentDetails(PaymentDetails paymentDetails) {
if (paymentDetails.hasMemo())
this.memo = paymentDetails.getMemo();
return this;
}
@Override
public String toString() {
// print only the user-settable fields
MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this).omitNullValues();
helper.add("emptyWallet", emptyWallet);
helper.add("changeAddress", changeAddress);
helper.add("feePerKb", feePerKb);
helper.add("ensureMinRequiredFee", ensureMinRequiredFee);
helper.add("signInputs", signInputs);
helper.add("aesKey", aesKey != null ? "set" : null); // careful to not leak the key
helper.add("coinSelector", coinSelector);
helper.add("shuffleOutputs", shuffleOutputs);
return helper.toString();
}
}
/**
* <p>Statelessly creates a transaction that sends the given value to address. The change is sent to
* {@link Wallet#currentChangeAddress()}, so you must have added at least one key.</p>
......@@ -3943,7 +3716,7 @@ public class Wallet extends BaseTaggableObject
* transaction, commit to the wallet and broadcast it to the network all in one go. This method is lower level
* and lets you see the proposed transaction before anything is done with it.</p>
*
* <p>This is a helper method that is equivalent to using {@link Wallet.SendRequest#to(Address, Coin)}
* <p>This is a helper method that is equivalent to using {@link SendRequest#to(Address, Coin)}
* followed by {@link Wallet#completeTx(Wallet.SendRequest)} and returning the requests transaction object.
* Note that this means a fee may be automatically added if required, if you want more control over the process,
* just do those two steps yourself.</p>
......@@ -4481,7 +4254,7 @@ public class Wallet extends BaseTaggableObject
* A coin selector is responsible for choosing which outputs to spend when creating transactions. The default
* selector implements a policy of spending transactions that appeared in the best chain and pending transactions
* that were created by this wallet, but not others. You can override the coin selector for any given send
* operation by changing {@link Wallet.SendRequest#coinSelector}.
* operation by changing {@link SendRequest#coinSelector}.
*/
public void setCoinSelector(CoinSelector coinSelector) {
lock.lock();
......
......@@ -25,6 +25,7 @@ import org.bitcoinj.store.BlockStoreException;
import org.bitcoinj.store.FullPrunedBlockStore;
import org.bitcoinj.utils.BlockFileLoader;