From 86046b71225ba249c1efd08dad5f91609001fbea Mon Sep 17 00:00:00 2001
From: Matt Corallo
Date: Fri, 17 May 2013 14:42:44 +0200
Subject: [PATCH] Add comments on dust output values and constants/util to calc
them
---
.../com/google/bitcoin/core/Transaction.java | 9 +++++++
.../bitcoin/core/TransactionOutput.java | 13 ++++++++++
.../java/com/google/bitcoin/core/Wallet.java | 26 ++++++++++++++++++-
3 files changed, 47 insertions(+), 1 deletion(-)
diff --git a/core/src/main/java/com/google/bitcoin/core/Transaction.java b/core/src/main/java/com/google/bitcoin/core/Transaction.java
index 6292118e..d7ef7e9a 100644
--- a/core/src/main/java/com/google/bitcoin/core/Transaction.java
+++ b/core/src/main/java/com/google/bitcoin/core/Transaction.java
@@ -57,6 +57,15 @@ public class Transaction extends ChildMessage implements Serializable {
/** How many bytes a transaction can be before it won't be relayed anymore. */
public static final int MAX_STANDARD_TX_SIZE = 100 * 1024;
+ /** If fee is lower than this value (in satoshis), a default reference client will treat it as if there were no fee */
+ public static final BigInteger REFERENCE_DEFAULT_MIN_TX_FEE = BigInteger.valueOf(10000);
+
+ /**
+ * Any standard (ie pay-to-address) output smaller than this value (in satoshis) will most likely be rejected by the network.
+ * This is calculated by assuming a standard output will be 34 bytes, and then using the formula used in
+ * {@link TransactionOutput#getMinNonDustValue(BigInteger)}.
+ */
+ public static final BigInteger MIN_NONDUST_OUTPUT = BigInteger.valueOf(5461);
// These are serialized in both bitcoin and java serialization.
private long version;
diff --git a/core/src/main/java/com/google/bitcoin/core/TransactionOutput.java b/core/src/main/java/com/google/bitcoin/core/TransactionOutput.java
index d62bb445..9d8bfe78 100644
--- a/core/src/main/java/com/google/bitcoin/core/TransactionOutput.java
+++ b/core/src/main/java/com/google/bitcoin/core/TransactionOutput.java
@@ -172,6 +172,19 @@ public class TransactionOutput extends ChildMessage implements Serializable {
throw new RuntimeException("Output linked to wrong parent transaction?");
}
+ /**
+ * Gets the minimum value for a txout of this size to be considered non-dust by a reference client (and thus relayed).
+ * See: CTxOut::IsDust() in the reference client.
+ *
+ * @param feePerKbRequired The fee required per kilobyte. Note that this is the same as the reference client's -minrelaytxfee * 3
+ * If you want a safe default, use {@link Transaction#REFERENCE_DEFAULT_MIN_TX_FEE}*3
+ */
+ public BigInteger getMinNonDustValue(BigInteger feePerKbRequired) {
+ // Note we skip the *3 as that should be considered in the parameter and we add one to account for loss of precision
+ // (1/1000 chance we require too much fee, 999/1000 chance we get the exact right value...)
+ return feePerKbRequired.multiply(BigInteger.valueOf(this.bitcoinSerialize().length + 148)).divide(BigInteger.valueOf(1000)).add(BigInteger.ONE);
+ }
+
/**
* Sets this objects availableForSpending flag to false and the spentBy pointer to the given input.
* If the input is null, it means this output was signed over to somebody else rather than one of our own keys.
diff --git a/core/src/main/java/com/google/bitcoin/core/Wallet.java b/core/src/main/java/com/google/bitcoin/core/Wallet.java
index 0962c2d0..4f141fe9 100644
--- a/core/src/main/java/com/google/bitcoin/core/Wallet.java
+++ b/core/src/main/java/com/google/bitcoin/core/Wallet.java
@@ -1650,7 +1650,11 @@ public class Wallet implements Serializable, BlockChainListener {
/**
* 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.
+ * 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(BigInteger)}) to avoid the whole transaction being rejected
+ * because one output is dust.
*/
public Transaction tx;
@@ -1683,6 +1687,12 @@ public class Wallet implements Serializable, BlockChainListener {
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, BigInteger value) {
SendRequest req = new Wallet.SendRequest();
req.tx = new Transaction(destination.getParameters());
@@ -1690,6 +1700,14 @@ public class Wallet implements Serializable, BlockChainListener {
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(BigInteger)} afterwards or you risk having the transaction
+ * rejected by the network. Note that using {@link SendRequest#to(Address, java.math.BigInteger)} 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, BigInteger value) {
SendRequest req = new SendRequest();
req.tx = new Transaction(params);
@@ -1723,6 +1741,9 @@ public class Wallet implements Serializable, BlockChainListener {
* prevent this, but that should only occur once the transaction has been accepted by the network. This implies
* you cannot have more than one outstanding sending tx at once.
*
+ * You MUST ensure that nanocoins is smaller than {@link Transaction#MIN_NONDUST_OUTPUT} or the transaction will
+ * almost certainly be rejected by the network as dust.
+ *
* @param address The Bitcoin address to send the money to.
* @param nanocoins How much currency to send, in nanocoins.
* @return either the created Transaction or null if there are insufficient coins.
@@ -1771,6 +1792,9 @@ public class Wallet implements Serializable, BlockChainListener {
* successfully broadcast. This means that even if the network hasn't heard about your transaction you won't be
* able to spend those same coins again.
*
+ * You MUST ensure that value is smaller than {@link Transaction#MIN_NONDUST_OUTPUT} or the transaction will
+ * almost certainly be rejected by the network as dust.
+ *
* @param peerGroup a PeerGroup to use for broadcast or null.
* @param to Which address to send coins to.
* @param value How much value to send. You can use Utils.toNanoCoins() to calculate this.