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.