From 7dd1bfd9c3028266a5f8d1954cbf259ee3907e9b Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 21 Apr 2017 14:20:55 -0500 Subject: [PATCH] Add fee to SendRequest again Cherry pick https://github.com/bisq-network/bitcoinj/commit/0dcd8ac92f9bd1d39e7004893dca5fd83ae7caf7 --- .../java/org/bitcoinj/wallet/SendRequest.java | 17 +++++++++++++ .../main/java/org/bitcoinj/wallet/Wallet.java | 25 +++++++++++++++---- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/wallet/SendRequest.java b/core/src/main/java/org/bitcoinj/wallet/SendRequest.java index ef0046320db..f157365cb95 100644 --- a/core/src/main/java/org/bitcoinj/wallet/SendRequest.java +++ b/core/src/main/java/org/bitcoinj/wallet/SendRequest.java @@ -77,6 +77,22 @@ public class SendRequest { */ 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 constant fee (in satoshis) which will be added to the transaction. It is recommended that it be + * at least {@link Transaction#REFERENCE_DEFAULT_MIN_TX_FEE} if it is set, as default Bitcoin Core will + * otherwise simply treat the transaction as if there were no fee at all.

+ * + *

You might also consider adding a {@link SendRequest#feePerKb} to set the fee per kb of transaction size + * (rounded down to the nearest kb) as that is how transactions are sorted when added to a block by miners.

+ */ + public Coin fee = 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 @@ -256,6 +272,7 @@ public String toString() { MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this).omitNullValues(); helper.add("emptyWallet", emptyWallet); helper.add("changeAddress", changeAddress); + helper.add("fee", fee); helper.add("feePerKb", feePerKb); helper.add("ensureMinRequiredFee", ensureMinRequiredFee); helper.add("signInputs", signInputs); diff --git a/core/src/main/java/org/bitcoinj/wallet/Wallet.java b/core/src/main/java/org/bitcoinj/wallet/Wallet.java index 6a732fe8560..139a4272642 100644 --- a/core/src/main/java/org/bitcoinj/wallet/Wallet.java +++ b/core/src/main/java/org/bitcoinj/wallet/Wallet.java @@ -3990,8 +3990,9 @@ public void completeTx(SendRequest req) throws InsufficientMoneyException { req.tx.addInput(output); if (req.emptyWallet) { + final Coin baseFee = req.fee == null ? Coin.ZERO : req.fee; final Coin feePerKb = req.feePerKb == null ? Coin.ZERO : req.feePerKb; - if (!adjustOutputDownwardsForFee(req.tx, bestCoinSelection, feePerKb, req.ensureMinRequiredFee)) + if (!adjustOutputDownwardsForFee(req.tx, bestCoinSelection, baseFee, feePerKb, req.ensureMinRequiredFee)) throw new CouldNotAdjustDownwards(); } @@ -4013,6 +4014,12 @@ public void completeTx(SendRequest req) throws InsufficientMoneyException { if (size > Transaction.MAX_STANDARD_TX_SIZE) throw new ExceededMaxTransactionSize(); + final Coin calculatedFee = req.tx.getFee(); + if (calculatedFee != null) + log.info(" with a fee of {}/kB, {} for {} bytes", + calculatedFee.multiply(1000).divide(size).toFriendlyString(), calculatedFee.toFriendlyString(), + size); + // Label the transaction as being self created. We can use this later to spend its change output even before // the transaction is confirmed. We deliberately won't bother notifying listeners here as there's not much // point - the user isn't interested in a confidence transition they made themselves. @@ -4025,6 +4032,7 @@ public void completeTx(SendRequest req) throws InsufficientMoneyException { req.tx.setExchangeRate(req.exchangeRate); req.tx.setMemo(req.memo); req.completed = true; + req.fee = calculatedFee; log.info(" completed: {}", req.tx); } finally { lock.unlock(); @@ -4088,10 +4096,10 @@ public void signTransaction(SendRequest req) { } /** Reduce the value of the first output of a transaction to pay the given feePerKb as appropriate for its size. */ - private boolean adjustOutputDownwardsForFee(Transaction tx, CoinSelection coinSelection, Coin feePerKb, + private boolean adjustOutputDownwardsForFee(Transaction tx, CoinSelection coinSelection, Coin baseFee, Coin feePerKb, boolean ensureMinRequiredFee) { final int size = tx.unsafeBitcoinSerialize().length + estimateBytesForSigning(coinSelection); - Coin fee = feePerKb.multiply(size).divide(1000); + Coin fee = baseFee.add(feePerKb.multiply(size).divide(1000)); if (ensureMinRequiredFee && fee.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; TransactionOutput output = tx.getOutput(0); @@ -4837,7 +4845,14 @@ public FeeCalculation calculateFee(SendRequest req, Coin value, List 0) { + // If the size is exactly 1000 bytes then we'll over-pay, but this should be rare. + fees = fees.add(req.feePerKb.multiply(lastCalculatedSize).divide(1000)); + } else { + fees = fees.add(req.feePerKb); // First time around the loop. + } + if (needAtLeastReferenceFee && fees.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) fees = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; @@ -5252,7 +5267,7 @@ private Transaction rekeyOneBatch(long timeSecs, @Nullable KeyParameter aesKey, } // When not signing, don't waste addresses. rekeyTx.addOutput(toMove.valueGathered, sign ? freshReceiveAddress() : currentReceiveAddress()); - if (!adjustOutputDownwardsForFee(rekeyTx, toMove, Transaction.DEFAULT_TX_FEE, true)) { + if (!adjustOutputDownwardsForFee(rekeyTx, toMove, Coin.ZERO, Transaction.DEFAULT_TX_FEE, true)) { log.error("Failed to adjust rekey tx for fees."); return null; }