From 4e29e4fb00f8378390aa8a45aa722614a8ba7e98 Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Fri, 1 Apr 2016 21:53:21 +0200 Subject: [PATCH] Wallet: Make SendRequest a top level class. --- .../channels/PaymentChannelClientState.java | 3 +- .../channels/PaymentChannelServerState.java | 7 +- .../channels/PaymentChannelV1ClientState.java | 3 +- .../channels/PaymentChannelV1ServerState.java | 3 +- .../channels/PaymentChannelV2ClientState.java | 3 +- .../channels/PaymentChannelV2ServerState.java | 3 +- .../protocols/payments/PaymentSession.java | 8 +- .../bitcoinj/wallet/EncryptableKeyChain.java | 2 +- .../java/org/bitcoinj/wallet/SendRequest.java | 267 ++++++++++++++++++ .../main/java/org/bitcoinj/wallet/Wallet.java | 231 +-------------- .../AbstractFullPrunedBlockChainTest.java | 3 +- .../core/TransactionBroadcastTest.java | 3 +- .../bitcoinj/core/TransactionOutputTest.java | 4 +- .../channels/PaymentChannelStateTest.java | 10 +- .../java/org/bitcoinj/wallet/WalletTest.java | 41 ++- .../java/org/bitcoinj/tools/TestFeeLevel.java | 3 +- .../java/org/bitcoinj/tools/WalletTool.java | 19 +- .../wallettemplate/SendMoneyController.java | 7 +- 18 files changed, 335 insertions(+), 285 deletions(-) create mode 100644 core/src/main/java/org/bitcoinj/wallet/SendRequest.java diff --git a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelClientState.java b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelClientState.java index 0a42bce0..dd4e0b6c 100644 --- a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelClientState.java +++ b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelClientState.java @@ -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) { } /** diff --git a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelServerState.java b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelServerState.java index 7dbf92cc..a5c9a785 100644 --- a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelServerState.java +++ b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelServerState.java @@ -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"); diff --git a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV1ClientState.java b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV1ClientState.java index 28add3b8..57d89887 100644 --- a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV1ClientState.java +++ b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV1ClientState.java @@ -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. diff --git a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV1ServerState.java b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV1ServerState.java index a026569c..3bf1df3f 100644 --- a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV1ServerState.java +++ b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV1ServerState.java @@ -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 diff --git a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV2ClientState.java b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV2ClientState.java index 13882361..c35470dd 100644 --- a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV2ClientState.java +++ b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV2ClientState.java @@ -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. diff --git a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV2ServerState.java b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV2ServerState.java index 85d35d10..f15fcc54 100644 --- a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV2ServerState.java +++ b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelV2ServerState.java @@ -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 diff --git a/core/src/main/java/org/bitcoinj/protocols/payments/PaymentSession.java b/core/src/main/java/org/bitcoinj/protocols/payments/PaymentSession.java index 1dbeffa8..8988f89c 100644 --- a/core/src/main/java/org/bitcoinj/protocols/payments/PaymentSession.java +++ b/core/src/main/java/org/bitcoinj/protocols/payments/PaymentSession.java @@ -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); } /** diff --git a/core/src/main/java/org/bitcoinj/wallet/EncryptableKeyChain.java b/core/src/main/java/org/bitcoinj/wallet/EncryptableKeyChain.java index 56dab273..4ad8e117 100644 --- a/core/src/main/java/org/bitcoinj/wallet/EncryptableKeyChain.java +++ b/core/src/main/java/org/bitcoinj/wallet/EncryptableKeyChain.java @@ -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) diff --git a/core/src/main/java/org/bitcoinj/wallet/SendRequest.java b/core/src/main/java/org/bitcoinj/wallet/SendRequest.java new file mode 100644 index 00000000..ef004632 --- /dev/null +++ b/core/src/main/java/org/bitcoinj/wallet/SendRequest.java @@ -0,0 +1,267 @@ +/* + * 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 { + /** + *

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.

+ * + *

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.

+ * + *

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).

+ */ + 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. If null this will be chosen for you. + */ + public Address changeAddress = null; + + /** + *

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.

+ * + *

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.

+ */ + public Coin feePerKb = Context.get().getFeePerKb(); + + /** + *

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.

+ * + *

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}.

+ */ + 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() {} + + /** + *

Creates a new SendRequest to the given address for the given value.

+ * + *

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.

+ */ + 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; + } + + /** + *

Creates a new SendRequest to the given pubkey for the given value.

+ * + *

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.

+ */ + 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 diff --git a/core/src/main/java/org/bitcoinj/wallet/Wallet.java b/core/src/main/java/org/bitcoinj/wallet/Wallet.java index 7081521f..89b0b708 100644 --- a/core/src/main/java/org/bitcoinj/wallet/Wallet.java +++ b/core/src/main/java/org/bitcoinj/wallet/Wallet.java @@ -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 { - /** - *

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.

- * - *

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.

- * - *

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).

- */ - 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. If null this will be chosen for you. - */ - public Address changeAddress = null; - - /** - *

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.

- * - *

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.

- */ - public Coin feePerKb = Context.get().getFeePerKb(); - - /** - *

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.

- * - *

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}.

- */ - 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() {} - - /** - *

Creates a new SendRequest to the given address for the given value.

- * - *

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.

- */ - 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; - } - - /** - *

Creates a new SendRequest to the given pubkey for the given value.

- * - *

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.

- */ - 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(); - } - } - /** *

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.

@@ -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.

* - *

This is a helper method that is equivalent to using {@link Wallet.SendRequest#to(Address, Coin)} + *

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.

@@ -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(); diff --git a/core/src/test/java/org/bitcoinj/core/AbstractFullPrunedBlockChainTest.java b/core/src/test/java/org/bitcoinj/core/AbstractFullPrunedBlockChainTest.java index 839c817d..aa93a188 100644 --- a/core/src/test/java/org/bitcoinj/core/AbstractFullPrunedBlockChainTest.java +++ b/core/src/test/java/org/bitcoinj/core/AbstractFullPrunedBlockChainTest.java @@ -25,6 +25,7 @@ import org.bitcoinj.store.BlockStoreException; import org.bitcoinj.store.FullPrunedBlockStore; import org.bitcoinj.utils.BlockFileLoader; import org.bitcoinj.utils.BriefLogFormatter; +import org.bitcoinj.wallet.SendRequest; import org.bitcoinj.wallet.Wallet; import org.bitcoinj.wallet.WalletTransaction; import org.junit.Before; @@ -327,7 +328,7 @@ public abstract class AbstractFullPrunedBlockChainTest { ECKey toKey2 = new ECKey(); Coin amount2 = amount.divide(2); Address address2 = new Address(PARAMS, toKey2.getPubKeyHash()); - Wallet.SendRequest req = Wallet.SendRequest.to(address2, amount2); + SendRequest req = SendRequest.to(address2, amount2); wallet.completeTx(req); wallet.commitTx(req.tx); Coin fee = Coin.ZERO; diff --git a/core/src/test/java/org/bitcoinj/core/TransactionBroadcastTest.java b/core/src/test/java/org/bitcoinj/core/TransactionBroadcastTest.java index 084e953a..820303e1 100644 --- a/core/src/test/java/org/bitcoinj/core/TransactionBroadcastTest.java +++ b/core/src/test/java/org/bitcoinj/core/TransactionBroadcastTest.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.*; import org.bitcoinj.core.listeners.TransactionConfidenceEventListener; import org.bitcoinj.testing.*; import org.bitcoinj.utils.*; +import org.bitcoinj.wallet.SendRequest; import org.bitcoinj.wallet.Wallet; import org.junit.*; import org.junit.runner.*; @@ -244,7 +245,7 @@ public class TransactionBroadcastTest extends TestWithPeerGroup { // Do the same thing with an offline transaction. peerGroup.removeWallet(wallet); - Wallet.SendRequest req = Wallet.SendRequest.to(dest, valueOf(2, 0)); + SendRequest req = SendRequest.to(dest, valueOf(2, 0)); Transaction t3 = checkNotNull(wallet.sendCoinsOffline(req)); assertNull(outbound(p1)); // Nothing sent. // Add the wallet to the peer group (simulate initialization). Transactions should be announced. diff --git a/core/src/test/java/org/bitcoinj/core/TransactionOutputTest.java b/core/src/test/java/org/bitcoinj/core/TransactionOutputTest.java index 5d448325..5c637d6c 100644 --- a/core/src/test/java/org/bitcoinj/core/TransactionOutputTest.java +++ b/core/src/test/java/org/bitcoinj/core/TransactionOutputTest.java @@ -19,7 +19,7 @@ import org.bitcoinj.params.MainNetParams; import org.bitcoinj.script.Script; import org.bitcoinj.script.ScriptBuilder; import org.bitcoinj.testing.TestWithWallet; -import org.bitcoinj.wallet.Wallet; +import org.bitcoinj.wallet.SendRequest; import org.hamcrest.CoreMatchers; import org.junit.After; import org.junit.Before; @@ -55,7 +55,7 @@ public class TransactionOutputTest extends TestWithWallet { Script scriptPubKey = ScriptBuilder.createMultiSigOutputScript(2, keys); multiSigTransaction.addOutput(Coin.COIN, scriptPubKey); - Wallet.SendRequest req = Wallet.SendRequest.forTx(multiSigTransaction); + SendRequest req = SendRequest.forTx(multiSigTransaction); this.wallet.completeTx(req); TransactionOutput multiSigTransactionOutput = multiSigTransaction.getOutput(0); diff --git a/core/src/test/java/org/bitcoinj/protocols/channels/PaymentChannelStateTest.java b/core/src/test/java/org/bitcoinj/protocols/channels/PaymentChannelStateTest.java index 7cb4cf8e..708b472b 100644 --- a/core/src/test/java/org/bitcoinj/protocols/channels/PaymentChannelStateTest.java +++ b/core/src/test/java/org/bitcoinj/protocols/channels/PaymentChannelStateTest.java @@ -20,8 +20,8 @@ import org.bitcoinj.core.*; import org.bitcoinj.script.Script; import org.bitcoinj.script.ScriptBuilder; import org.bitcoinj.testing.TestWithWallet; +import org.bitcoinj.wallet.SendRequest; import org.bitcoinj.wallet.Wallet; -import org.bitcoinj.wallet.Wallet.SendRequest; import com.google.common.collect.Lists; import com.google.common.util.concurrent.ListenableFuture; @@ -340,7 +340,7 @@ public class PaymentChannelStateTest extends TestWithWallet { // we can broadcast the refund and get our balance back. // Spend the client wallet's one coin - Transaction spendCoinTx = wallet.sendCoinsOffline(Wallet.SendRequest.to(new ECKey().toAddress(PARAMS), COIN)); + Transaction spendCoinTx = wallet.sendCoinsOffline(SendRequest.to(new ECKey().toAddress(PARAMS), COIN)); assertEquals(Coin.ZERO, wallet.getBalance()); chain.add(makeSolvedTestBlock(blockStore.getChainHead().getHeader(), spendCoinTx, createFakeTx(PARAMS, CENT, myAddress))); assertEquals(CENT, wallet.getBalance()); @@ -691,7 +691,7 @@ public class PaymentChannelStateTest extends TestWithWallet { Context.propagate(new Context(PARAMS, 100, Coin.ZERO, true)); // Spend the client wallet's one coin - final SendRequest request = Wallet.SendRequest.to(new ECKey().toAddress(PARAMS), COIN); + final SendRequest request = SendRequest.to(new ECKey().toAddress(PARAMS), COIN); request.ensureMinRequiredFee = false; wallet.sendCoinsOffline(request); assertEquals(Coin.ZERO, wallet.getBalance()); @@ -819,7 +819,7 @@ public class PaymentChannelStateTest extends TestWithWallet { case VERSION_1: clientState = new PaymentChannelV1ClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), CENT, EXPIRE_TIME) { @Override - protected void editContractSendRequest(Wallet.SendRequest req) { + protected void editContractSendRequest(SendRequest req) { req.coinSelector = wallet.getCoinSelector(); } }; @@ -828,7 +828,7 @@ public class PaymentChannelStateTest extends TestWithWallet { case VERSION_2: clientState = new PaymentChannelV2ClientState(wallet, myKey, ECKey.fromPublicOnly(serverKey.getPubKey()), CENT, EXPIRE_TIME) { @Override - protected void editContractSendRequest(Wallet.SendRequest req) { + protected void editContractSendRequest(SendRequest req) { req.coinSelector = wallet.getCoinSelector(); } }; diff --git a/core/src/test/java/org/bitcoinj/wallet/WalletTest.java b/core/src/test/java/org/bitcoinj/wallet/WalletTest.java index 9c991c0d..e164f622 100644 --- a/core/src/test/java/org/bitcoinj/wallet/WalletTest.java +++ b/core/src/test/java/org/bitcoinj/wallet/WalletTest.java @@ -60,7 +60,6 @@ import com.google.common.collect.Lists; import com.google.common.util.concurrent.ListenableFuture; import com.google.protobuf.ByteString; import org.bitcoinj.wallet.Protos.Wallet.EncryptionType; -import org.bitcoinj.wallet.Wallet.SendRequest; import org.junit.After; import org.junit.Before; import org.junit.Ignore; @@ -307,7 +306,7 @@ public class WalletTest extends TestWithWallet { // Try to send too much and fail. Coin vHuge = valueOf(10, 0); - Wallet.SendRequest req = Wallet.SendRequest.to(destination, vHuge); + SendRequest req = SendRequest.to(destination, vHuge); try { wallet.completeTx(req); fail(); @@ -317,7 +316,7 @@ public class WalletTest extends TestWithWallet { // Prepare to send. Coin v2 = valueOf(0, 50); - req = Wallet.SendRequest.to(destination, v2); + req = SendRequest.to(destination, v2); if (encryptedWallet != null) { KeyCrypter keyCrypter = encryptedWallet.getKeyCrypter(); @@ -334,7 +333,7 @@ public class WalletTest extends TestWithWallet { assertEquals("Wrong number of ALL", 1, wallet.getTransactions(true).size()); // Try to create a send with a fee but the wrong password (this should fail). - req = Wallet.SendRequest.to(destination, v2); + req = SendRequest.to(destination, v2); req.aesKey = wrongAesKey; try { @@ -348,7 +347,7 @@ public class WalletTest extends TestWithWallet { assertEquals("Wrong number of ALL", 1, wallet.getTransactions(true).size()); // Create a send with a fee with the correct password (this should succeed). - req = Wallet.SendRequest.to(destination, v2); + req = SendRequest.to(destination, v2); req.aesKey = aesKey; } @@ -446,7 +445,7 @@ public class WalletTest extends TestWithWallet { wallet = roundTrip(wallet); Coin v3 = valueOf(0, 50); assertEquals(v3, wallet.getBalance()); - Wallet.SendRequest req = Wallet.SendRequest.to(OTHER_ADDRESS, valueOf(0, 48)); + SendRequest req = SendRequest.to(OTHER_ADDRESS, valueOf(0, 48)); req.aesKey = aesKey; req.shuffleOutputs = false; wallet.completeTx(req); @@ -2106,7 +2105,7 @@ public class WalletTest extends TestWithWallet { assertEquals(key.getPubKeyPoint(), encryptedWallet.getImportedKeys().get(0).getPubKeyPoint()); sendMoneyToWallet(encryptedWallet, AbstractBlockChain.NewBlockType.BEST_CHAIN, Coin.COIN, key.toAddress(PARAMS)); assertEquals(Coin.COIN, encryptedWallet.getBalance()); - SendRequest req = Wallet.SendRequest.emptyWallet(OTHER_ADDRESS); + SendRequest req = SendRequest.emptyWallet(OTHER_ADDRESS); req.aesKey = checkNotNull(encryptedWallet.getKeyCrypter()).deriveKey(PASSWORD1); encryptedWallet.sendCoinsOffline(req); } @@ -2141,7 +2140,7 @@ public class WalletTest extends TestWithWallet { for (int i = 0; i < 3100; i++) { tx.addOutput(v, new Address(PARAMS, bits)); } - Wallet.SendRequest req = Wallet.SendRequest.forTx(tx); + SendRequest req = SendRequest.forTx(tx); wallet.completeTx(req); } @@ -2153,7 +2152,7 @@ public class WalletTest extends TestWithWallet { Coin messagePrice = Coin.ZERO; Script script = ScriptBuilder.createOpReturnScript("hello world!".getBytes()); tx.addOutput(messagePrice, script); - SendRequest request = Wallet.SendRequest.forTx(tx); + SendRequest request = SendRequest.forTx(tx); request.ensureMinRequiredFee = true; wallet.completeTx(request); } @@ -2166,7 +2165,7 @@ public class WalletTest extends TestWithWallet { Coin messagePrice = CENT; Script script = ScriptBuilder.createOpReturnScript("hello world!".getBytes()); tx.addOutput(messagePrice, script); - SendRequest request = Wallet.SendRequest.forTx(tx); + SendRequest request = SendRequest.forTx(tx); wallet.completeTx(request); } @@ -2179,7 +2178,7 @@ public class WalletTest extends TestWithWallet { Script script = ScriptBuilder.createOpReturnScript("hello world!".getBytes()); tx.addOutput(CENT, OTHER_ADDRESS); tx.addOutput(messagePrice, script); - SendRequest request = Wallet.SendRequest.forTx(tx); + SendRequest request = SendRequest.forTx(tx); wallet.completeTx(request); } @@ -2193,7 +2192,7 @@ public class WalletTest extends TestWithWallet { Script script2 = ScriptBuilder.createOpReturnScript("hello world 2!".getBytes()); tx.addOutput(messagePrice, script1); tx.addOutput(messagePrice, script2); - SendRequest request = Wallet.SendRequest.forTx(tx); + SendRequest request = SendRequest.forTx(tx); request.ensureMinRequiredFee = true; wallet.completeTx(request); } @@ -2203,7 +2202,7 @@ public class WalletTest extends TestWithWallet { // Tests sending dust, should throw DustySendRequested. Transaction tx = new Transaction(PARAMS); tx.addOutput(Transaction.MIN_NONDUST_OUTPUT.subtract(SATOSHI), OTHER_ADDRESS); - SendRequest request = Wallet.SendRequest.forTx(tx); + SendRequest request = SendRequest.forTx(tx); request.ensureMinRequiredFee = true; wallet.completeTx(request); } @@ -2217,7 +2216,7 @@ public class WalletTest extends TestWithWallet { tx.addOutput(c, OTHER_ADDRESS); tx.addOutput(c, OTHER_ADDRESS); tx.addOutput(c, OTHER_ADDRESS); - SendRequest request = Wallet.SendRequest.forTx(tx); + SendRequest request = SendRequest.forTx(tx); wallet.completeTx(request); } @@ -2228,7 +2227,7 @@ public class WalletTest extends TestWithWallet { Transaction tx = new Transaction(PARAMS); tx.addOutput(Coin.ZERO, ScriptBuilder.createOpReturnScript("hello world!".getBytes())); tx.addOutput(Coin.SATOSHI, OTHER_ADDRESS); - SendRequest request = Wallet.SendRequest.forTx(tx); + SendRequest request = SendRequest.forTx(tx); request.ensureMinRequiredFee = true; wallet.completeTx(request); } @@ -2240,7 +2239,7 @@ public class WalletTest extends TestWithWallet { Transaction tx = new Transaction(PARAMS); tx.addOutput(Coin.CENT, ScriptBuilder.createOpReturnScript("hello world!".getBytes())); tx.addOutput(Transaction.MIN_NONDUST_OUTPUT.subtract(SATOSHI), OTHER_ADDRESS); - SendRequest request = Wallet.SendRequest.forTx(tx); + SendRequest request = SendRequest.forTx(tx); request.ensureMinRequiredFee = true; wallet.completeTx(request); } @@ -3148,7 +3147,7 @@ public class WalletTest extends TestWithWallet { for (TransactionInput input : req.tx.getInputs()) input.clearScriptBytes(); Wallet watching = Wallet.fromWatchingKey(PARAMS, wallet.getWatchingKey().dropParent().dropPrivateBytes()); - watching.completeTx(Wallet.SendRequest.forTx(req.tx)); + watching.completeTx(SendRequest.forTx(req.tx)); } @Test @@ -3175,7 +3174,7 @@ public class WalletTest extends TestWithWallet { myAddress = wallet.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, COIN, myAddress); - Wallet.SendRequest req = Wallet.SendRequest.emptyWallet(OTHER_ADDRESS); + SendRequest req = SendRequest.emptyWallet(OTHER_ADDRESS); wallet.completeTx(req); } @@ -3185,7 +3184,7 @@ public class WalletTest extends TestWithWallet { myAddress = wallet.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, COIN, myAddress); - Wallet.SendRequest req = Wallet.SendRequest.emptyWallet(OTHER_ADDRESS); + SendRequest req = SendRequest.emptyWallet(OTHER_ADDRESS); req.missingSigsMode = missSigMode; wallet.completeTx(req); TransactionInput input = req.tx.getInput(0); @@ -3214,7 +3213,7 @@ public class WalletTest extends TestWithWallet { Transaction t2 = sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, CENT, pub); Transaction t3 = sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, CENT, priv2); - Wallet.SendRequest req = Wallet.SendRequest.emptyWallet(OTHER_ADDRESS); + SendRequest req = SendRequest.emptyWallet(OTHER_ADDRESS); req.missingSigsMode = missSigMode; wallet.completeTx(req); byte[] dummySig = TransactionSignature.dummy().encodeToBitcoin(); @@ -3397,7 +3396,7 @@ public class WalletTest extends TestWithWallet { Address myAddress = wallet.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); sendMoneyToWallet(wallet, AbstractBlockChain.NewBlockType.BEST_CHAIN, COIN, myAddress); - Wallet.SendRequest req = Wallet.SendRequest.emptyWallet(OTHER_ADDRESS); + SendRequest req = SendRequest.emptyWallet(OTHER_ADDRESS); req.missingSigsMode = Wallet.MissingSigsMode.USE_DUMMY_SIG; wallet.completeTx(req); } diff --git a/tools/src/main/java/org/bitcoinj/tools/TestFeeLevel.java b/tools/src/main/java/org/bitcoinj/tools/TestFeeLevel.java index 615ab713..1ca5036d 100644 --- a/tools/src/main/java/org/bitcoinj/tools/TestFeeLevel.java +++ b/tools/src/main/java/org/bitcoinj/tools/TestFeeLevel.java @@ -20,6 +20,7 @@ import org.bitcoinj.core.listeners.PeerDisconnectedEventListener; import org.bitcoinj.kits.WalletAppKit; import org.bitcoinj.params.MainNetParams; import org.bitcoinj.utils.BriefLogFormatter; +import org.bitcoinj.wallet.SendRequest; import org.bitcoinj.wallet.Wallet; import java.io.File; @@ -74,7 +75,7 @@ public class TestFeeLevel { value = value.subtract(outputValue); } transaction.addOutput(value, kit.wallet().freshReceiveAddress()); - Wallet.SendRequest request = Wallet.SendRequest.forTx(transaction); + SendRequest request = SendRequest.forTx(transaction); request.feePerKb = feeToTest; request.ensureMinRequiredFee = false; kit.wallet().completeTx(request); diff --git a/tools/src/main/java/org/bitcoinj/tools/WalletTool.java b/tools/src/main/java/org/bitcoinj/tools/WalletTool.java index c0a82637..3339caaf 100644 --- a/tools/src/main/java/org/bitcoinj/tools/WalletTool.java +++ b/tools/src/main/java/org/bitcoinj/tools/WalletTool.java @@ -51,6 +51,7 @@ import org.bitcoinj.core.listeners.BlocksDownloadedEventListener; import org.bitcoinj.core.listeners.DownloadProgressTracker; import org.bitcoinj.wallet.MarriedKeyChain; import org.bitcoinj.wallet.Protos; +import org.bitcoinj.wallet.SendRequest; import org.bitcoinj.wallet.Wallet; import org.bitcoinj.wallet.WalletExtension; import org.bitcoinj.wallet.WalletProtobufSerializer; @@ -594,7 +595,7 @@ public class WalletTool { return; } } - Wallet.SendRequest req = Wallet.SendRequest.forTx(t); + SendRequest req = SendRequest.forTx(t); if (t.getOutputs().size() == 1 && t.getOutput(0).getValue().equals(wallet.getBalance())) { log.info("Emptying out wallet, recipient may get less than what you expect"); req.emptyWallet = true; @@ -726,7 +727,7 @@ public class WalletTool { throw new RuntimeException(e); } - Wallet.SendRequest req = Wallet.SendRequest.toCLTVPaymentChannel(params, BigInteger.valueOf(lockTime), refundKey, outputKey, value); + SendRequest req = SendRequest.toCLTVPaymentChannel(params, BigInteger.valueOf(lockTime), refundKey, outputKey, value); if (req.tx.getOutputs().size() == 1 && req.tx.getOutput(0).getValue().equals(wallet.getBalance())) { log.info("Emptying out wallet, recipient may get less than what you expect"); req.emptyWallet = true; @@ -796,9 +797,9 @@ public class WalletTool { return; } - Wallet.SendRequest req = outputSpec.isAddress() ? - Wallet.SendRequest.to(outputSpec.addr, value) : - Wallet.SendRequest.to(params, outputSpec.key, value); + SendRequest req = outputSpec.isAddress() ? + SendRequest.to(outputSpec.addr, value) : + SendRequest.to(params, outputSpec.key, value); if (feePerKb != null) req.feePerKb = feePerKb; @@ -900,9 +901,9 @@ public class WalletTool { return; } - Wallet.SendRequest req = outputSpec.isAddress() ? - Wallet.SendRequest.to(outputSpec.addr, value) : - Wallet.SendRequest.to(params, outputSpec.key, value); + SendRequest req = outputSpec.isAddress() ? + SendRequest.to(outputSpec.addr, value) : + SendRequest.to(params, outputSpec.key, value); if (feePerKb != null) req.feePerKb = feePerKb; @@ -1057,7 +1058,7 @@ public class WalletTool { System.out.println("Pki-Verified Name: " + session.pkiVerificationData.displayName); System.out.println("PKI data verified by: " + session.pkiVerificationData.rootAuthorityName); } - final Wallet.SendRequest req = session.getSendRequest(); + final SendRequest req = session.getSendRequest(); if (password != null) { req.aesKey = passwordToKey(true); if (req.aesKey == null) diff --git a/wallettemplate/src/main/java/wallettemplate/SendMoneyController.java b/wallettemplate/src/main/java/wallettemplate/SendMoneyController.java index c68506c4..f9782d2b 100644 --- a/wallettemplate/src/main/java/wallettemplate/SendMoneyController.java +++ b/wallettemplate/src/main/java/wallettemplate/SendMoneyController.java @@ -16,6 +16,7 @@ package wallettemplate; import javafx.scene.layout.HBox; import org.bitcoinj.core.*; +import org.bitcoinj.wallet.SendRequest; import org.bitcoinj.wallet.Wallet; import com.google.common.util.concurrent.FutureCallback; @@ -66,11 +67,11 @@ public class SendMoneyController { try { Coin amount = Coin.parseCoin(amountEdit.getText()); Address destination = Address.fromBase58(Main.params, address.getText()); - Wallet.SendRequest req; + SendRequest req; if (amount.equals(Main.bitcoin.wallet().getBalance())) - req = Wallet.SendRequest.emptyWallet(destination); + req = SendRequest.emptyWallet(destination); else - req = Wallet.SendRequest.to(destination, amount); + req = SendRequest.to(destination, amount); req.aesKey = aesKey; sendResult = Main.bitcoin.wallet().sendCoins(req); Futures.addCallback(sendResult.broadcastComplete, new FutureCallback() {