mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-12 10:15:52 +00:00
Implement the ability to explicitly set a fee in the Wallet.SendRequest. Does not yet auto-calculate the correct fees. Resolves issue 45. Resolves issue 245.
This commit is contained in:
parent
915a2adb10
commit
572f2a4f4e
@ -1276,6 +1276,16 @@ public class Wallet implements Serializable {
|
||||
*/
|
||||
public Address changeAddress;
|
||||
|
||||
/**
|
||||
* 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. Some transactions require a fee due to their structure - currently bitcoinj does not
|
||||
* correctly calculate this! As of late 2012 most transactions require no fee.
|
||||
*/
|
||||
public BigInteger fee = BigInteger.ZERO;
|
||||
|
||||
// Tracks if this has been passed to wallet.completeTx already: just a safety check.
|
||||
private boolean completed;
|
||||
|
||||
@ -1436,14 +1446,15 @@ public class Wallet implements Serializable {
|
||||
*/
|
||||
public synchronized boolean completeTx(SendRequest req) {
|
||||
Preconditions.checkArgument(!req.completed, "Given SendRequest has already been completed.");
|
||||
// Calculate the transaction total
|
||||
BigInteger nanocoins = BigInteger.ZERO;
|
||||
// Calculate the amount of value we need to import.
|
||||
BigInteger value = BigInteger.ZERO;
|
||||
for (TransactionOutput output : req.tx.getOutputs()) {
|
||||
nanocoins = nanocoins.add(output.getValue());
|
||||
value = value.add(output.getValue());
|
||||
}
|
||||
value = value.add(req.fee);
|
||||
|
||||
log.info("Completing send tx with {} outputs totalling {}",
|
||||
req.tx.getOutputs().size(), bitcoinValueToFriendlyString(nanocoins));
|
||||
req.tx.getOutputs().size(), bitcoinValueToFriendlyString(value));
|
||||
|
||||
// To send money to somebody else, we need to do gather up transactions with unspent outputs until we have
|
||||
// sufficient value. Many coin selection algorithms are possible, we use a simple but suboptimal one.
|
||||
@ -1461,18 +1472,18 @@ public class Wallet implements Serializable {
|
||||
gathered.add(output);
|
||||
valueGathered = valueGathered.add(output.getValue());
|
||||
}
|
||||
if (valueGathered.compareTo(nanocoins) >= 0) break;
|
||||
if (valueGathered.compareTo(value) >= 0) break;
|
||||
}
|
||||
// Can we afford this?
|
||||
if (valueGathered.compareTo(nanocoins) < 0) {
|
||||
if (valueGathered.compareTo(value) < 0) {
|
||||
log.info("Insufficient value in wallet for send, missing " +
|
||||
bitcoinValueToFriendlyString(nanocoins.subtract(valueGathered)));
|
||||
bitcoinValueToFriendlyString(value.subtract(valueGathered)));
|
||||
// TODO: Should throw an exception here.
|
||||
return false;
|
||||
}
|
||||
checkState(gathered.size() > 0);
|
||||
req.tx.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.NOT_SEEN_IN_CHAIN);
|
||||
BigInteger change = valueGathered.subtract(nanocoins);
|
||||
BigInteger change = valueGathered.subtract(value);
|
||||
if (change.compareTo(BigInteger.ZERO) > 0) {
|
||||
// The value of the inputs is greater than what we want to send. Just like in real life then,
|
||||
// we need to take back some coins ... this is called "change". Add another output that sends the change
|
||||
|
@ -58,18 +58,25 @@ public class WalletTest {
|
||||
|
||||
@Test
|
||||
public void basicSpending() throws Exception {
|
||||
// We'll set up a wallet that receives a coin, then sends a coin of lesser value and keeps the change.
|
||||
// We'll set up a wallet that receives a coin, then sends a coin of lesser value and keeps the change. We
|
||||
// will attach a small fee. Because the Bitcoin protocol makes it difficult to determine the fee of an
|
||||
// arbitrary transaction in isolation, we'll check that the fee was set by examining the size of the change.
|
||||
|
||||
// Receive some money.
|
||||
BigInteger v1 = Utils.toNanoCoins(1, 0);
|
||||
Transaction t1 = createFakeTx(params, v1, myAddress);
|
||||
|
||||
wallet.receiveFromBlock(t1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
||||
assertEquals(v1, wallet.getBalance());
|
||||
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
|
||||
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL));
|
||||
|
||||
ECKey k2 = new ECKey();
|
||||
// Create a send with a fee.
|
||||
Address destination = new ECKey().toAddress(params);
|
||||
BigInteger v2 = toNanoCoins(0, 50);
|
||||
Transaction t2 = wallet.createSend(k2.toAddress(params), v2);
|
||||
Wallet.SendRequest req = Wallet.SendRequest.to(destination, v2);
|
||||
req.fee = toNanoCoins(0, 1);
|
||||
wallet.completeTx(req);
|
||||
Transaction t2 = req.tx;
|
||||
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
|
||||
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL));
|
||||
|
||||
@ -77,6 +84,10 @@ public class WalletTest {
|
||||
assertEquals(1, t2.getInputs().size());
|
||||
assertEquals(myAddress, t2.getInputs().get(0).getScriptSig().getFromAddress());
|
||||
assertEquals(t2.getConfidence().getConfidenceType(), TransactionConfidence.ConfidenceType.NOT_SEEN_IN_CHAIN);
|
||||
assertEquals(2, t2.getOutputs().size());
|
||||
assertEquals(destination, t2.getOutputs().get(0).getScriptPubKey().getToAddress());
|
||||
assertEquals(wallet.getChangeAddress(), t2.getOutputs().get(1).getScriptPubKey().getToAddress());
|
||||
assertEquals(toNanoCoins(0, 49), t2.getOutputs().get(1).getValue());
|
||||
|
||||
// We have NOT proven that the signature is correct!
|
||||
|
||||
|
@ -88,7 +88,7 @@ public class WalletTool {
|
||||
" --output=1GthXFQMktFLWdh5EPNGqbq3H6WdG8zsWj:1.245\n" +
|
||||
" If the output destination starts with 04 and is 65 bytes (130 chars) it will be\n" +
|
||||
" treated as a public key instead of an address and the send will use \n" +
|
||||
" <key> CHECKSIG as the script.\n" +
|
||||
" <key> CHECKSIG as the script. You can also specify a --fee=0.01\n" +
|
||||
|
||||
"\n>>> WAITING\n" +
|
||||
"You can wait for the condition specified by the --waitfor flag to become true. Transactions and new\n" +
|
||||
@ -229,6 +229,7 @@ public class WalletTool {
|
||||
parser.accepts("peers").withRequiredArg();
|
||||
OptionSpec<String> outputFlag = parser.accepts("output").withRequiredArg();
|
||||
parser.accepts("value").withRequiredArg();
|
||||
parser.accepts("fee").withRequiredArg();
|
||||
conditionFlag = parser.accepts("condition").withRequiredArg();
|
||||
options = parser.parse(args);
|
||||
|
||||
@ -321,7 +322,11 @@ public class WalletTool {
|
||||
System.err.println("You must specify at least one --output=addr:value.");
|
||||
return;
|
||||
}
|
||||
send(outputFlag.values(options));
|
||||
BigInteger fee = BigInteger.ZERO;
|
||||
if (options.has("fee")) {
|
||||
fee = Utils.toNanoCoins((String)options.valueOf("fee"));
|
||||
}
|
||||
send(outputFlag.values(options), fee);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -343,7 +348,7 @@ public class WalletTool {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
private static void send(List<String> outputs) {
|
||||
private static void send(List<String> outputs, BigInteger fee) {
|
||||
try {
|
||||
// Convert the input strings to outputs.
|
||||
Transaction t = new Transaction(params);
|
||||
@ -377,6 +382,7 @@ public class WalletTool {
|
||||
}
|
||||
}
|
||||
Wallet.SendRequest req = Wallet.SendRequest.forTx(t);
|
||||
req.fee = fee;
|
||||
if (!wallet.completeTx(req)) {
|
||||
System.err.println("Insufficient funds: have " + wallet.getBalance());
|
||||
return;
|
||||
|
Loading…
x
Reference in New Issue
Block a user