3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-13 02:35:52 +00:00

Wallet: Get rid of the concept of a "base fee". Fee is always expressed as a rate in Coin per 1000 bytes.

SendRequest.fee was removed; use SendRequest.feePerKb for requesting a specific fee rate. If the field
was read for knowing the calculated fee, use SendRequest.tx.getFee() instead.

Also makes a couple of unit tests independent of fee and renames the wallet-tool option --fee to
--fee-per-kb.
This commit is contained in:
Andreas Schildbach 2016-03-07 17:38:08 +01:00
parent d6002e1ee4
commit c3b19bd5cd
5 changed files with 49 additions and 89 deletions

View File

@ -3708,22 +3708,6 @@ public class Wallet extends BaseTaggableObject
*/ */
public Address changeAddress = null; public Address changeAddress = null;
/**
* <p>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.</p>
*
* <p>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.</p>
*
* <p>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.</p>
*/
public Coin fee = null;
/** /**
* <p>A transaction can have a fee attached, which is defined as the difference between the input values * <p>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 * and output values. Any value taken in that is not provided to an output can be claimed by a miner. This
@ -3735,8 +3719,6 @@ public class Wallet extends BaseTaggableObject
* including the first. This is useful as as miners usually sort pending transactions by their fee per unit 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 * 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.</p> * definition, a kilobyte is defined as 1000 bytes, not 1024.</p>
*
* <p>You might also consider using a {@link SendRequest#fee} to set the fee added for the first kb of size.</p>
*/ */
public Coin feePerKb = DEFAULT_FEE_PER_KB; public Coin feePerKb = DEFAULT_FEE_PER_KB;
@ -3752,7 +3734,7 @@ public class Wallet extends BaseTaggableObject
* only set this to false if you know what you're doing.</p> * only set this to false if you know what you're doing.</p>
* *
* <p>Note that this does not enforce certain fee rules that only apply to transactions which are larger than * <p>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 fee and feePerKb of at least * 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}.</p> * {@link Transaction#REFERENCE_DEFAULT_MIN_TX_FEE}.</p>
*/ */
public boolean ensureMinRequiredFee = true; public boolean ensureMinRequiredFee = true;
@ -3885,7 +3867,6 @@ public class Wallet extends BaseTaggableObject
MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this).omitNullValues(); MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this).omitNullValues();
helper.add("emptyWallet", emptyWallet); helper.add("emptyWallet", emptyWallet);
helper.add("changeAddress", changeAddress); helper.add("changeAddress", changeAddress);
helper.add("fee", fee);
helper.add("feePerKb", feePerKb); helper.add("feePerKb", feePerKb);
helper.add("ensureMinRequiredFee", ensureMinRequiredFee); helper.add("ensureMinRequiredFee", ensureMinRequiredFee);
helper.add("signInputs", signInputs); helper.add("signInputs", signInputs);
@ -4184,9 +4165,8 @@ public class Wallet extends BaseTaggableObject
req.tx.addInput(output); req.tx.addInput(output);
if (req.emptyWallet) { if (req.emptyWallet) {
final Coin baseFee = req.fee == null ? Coin.ZERO : req.fee;
final Coin feePerKb = req.feePerKb == null ? Coin.ZERO : req.feePerKb; final Coin feePerKb = req.feePerKb == null ? Coin.ZERO : req.feePerKb;
if (!adjustOutputDownwardsForFee(req.tx, bestCoinSelection, baseFee, feePerKb, req.ensureMinRequiredFee)) if (!adjustOutputDownwardsForFee(req.tx, bestCoinSelection, feePerKb, req.ensureMinRequiredFee))
throw new CouldNotAdjustDownwards(); throw new CouldNotAdjustDownwards();
} }
@ -4226,7 +4206,6 @@ public class Wallet extends BaseTaggableObject
req.tx.setExchangeRate(req.exchangeRate); req.tx.setExchangeRate(req.exchangeRate);
req.tx.setMemo(req.memo); req.tx.setMemo(req.memo);
req.completed = true; req.completed = true;
req.fee = calculatedFee;
log.info(" completed: {}", req.tx); log.info(" completed: {}", req.tx);
} finally { } finally {
lock.unlock(); lock.unlock();
@ -4290,10 +4269,10 @@ public class Wallet extends BaseTaggableObject
} }
/** Reduce the value of the first output of a transaction to pay the given feePerKb as appropriate for its size. */ /** 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 baseFee, private boolean adjustOutputDownwardsForFee(Transaction tx, CoinSelection coinSelection, Coin feePerKb,
Coin feePerKb, boolean ensureMinRequiredFee) { boolean ensureMinRequiredFee) {
final int size = tx.unsafeBitcoinSerialize().length + estimateBytesForSigning(coinSelection); final int size = tx.unsafeBitcoinSerialize().length + estimateBytesForSigning(coinSelection);
Coin fee = baseFee.add(feePerKb.multiply(size).divide(1000)); Coin fee = feePerKb.multiply(size).divide(1000);
if (ensureMinRequiredFee && fee.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) if (ensureMinRequiredFee && fee.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0)
fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
TransactionOutput output = tx.getOutput(0); TransactionOutput output = tx.getOutput(0);
@ -5045,12 +5024,12 @@ public class Wallet extends BaseTaggableObject
while (true) { while (true) {
resetTxInputs(req, originalInputs); resetTxInputs(req, originalInputs);
Coin fees = req.fee == null ? Coin.ZERO : req.fee; Coin fees;
if (lastCalculatedSize > 0) { if (lastCalculatedSize > 0) {
// If the size is exactly 1000 bytes then we'll over-pay, but this should be rare. // If the size is exactly 1000 bytes then we'll over-pay, but this should be rare.
fees = fees.add(req.feePerKb.multiply((lastCalculatedSize / 1000) + 1)); fees = req.feePerKb.multiply((lastCalculatedSize / 1000) + 1);
} else { } else {
fees = fees.add(req.feePerKb); // First time around the loop. fees = req.feePerKb; // First time around the loop.
} }
if (needAtLeastReferenceFee && fees.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) if (needAtLeastReferenceFee && fees.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0)
fees = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; fees = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
@ -5465,7 +5444,7 @@ public class Wallet extends BaseTaggableObject
} }
// When not signing, don't waste addresses. // When not signing, don't waste addresses.
rekeyTx.addOutput(toMove.valueGathered, sign ? freshReceiveAddress() : currentReceiveAddress()); rekeyTx.addOutput(toMove.valueGathered, sign ? freshReceiveAddress() : currentReceiveAddress());
if (!adjustOutputDownwardsForFee(rekeyTx, toMove, Coin.ZERO, Transaction.REFERENCE_DEFAULT_MIN_TX_FEE, true)) { if (!adjustOutputDownwardsForFee(rekeyTx, toMove, Transaction.REFERENCE_DEFAULT_MIN_TX_FEE, true)) {
log.error("Failed to adjust rekey tx for fees."); log.error("Failed to adjust rekey tx for fees.");
return null; return null;
} }

View File

@ -333,7 +333,7 @@ public abstract class AbstractFullPrunedBlockChainTest {
Wallet.SendRequest req = Wallet.SendRequest.to(address2, amount2); Wallet.SendRequest req = Wallet.SendRequest.to(address2, amount2);
wallet.completeTx(req); wallet.completeTx(req);
wallet.commitTx(req.tx); wallet.commitTx(req.tx);
Coin fee = req.fee; Coin fee = Coin.ZERO;
// There should be one pending tx (our spend). // There should be one pending tx (our spend).
assertEquals("Wrong number of PENDING.4", 1, wallet.getPoolSize(WalletTransaction.Pool.PENDING)); assertEquals("Wrong number of PENDING.4", 1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));

View File

@ -214,7 +214,6 @@ public class WalletTest extends TestWithWallet {
Coin v2 = valueOf(0, 50); Coin v2 = valueOf(0, 50);
SendRequest req = SendRequest.to(destination, v2); SendRequest req = SendRequest.to(destination, v2);
req.fee = CENT;
wallet.completeTx(req); wallet.completeTx(req);
Transaction t2 = req.tx; Transaction t2 = req.tx;
@ -231,7 +230,7 @@ public class WalletTest extends TestWithWallet {
assertEquals("Wrong number of PENDING", 2, wallet.getPoolSize(Pool.PENDING)); assertEquals("Wrong number of PENDING", 2, wallet.getPoolSize(Pool.PENDING));
assertEquals("Wrong number of UNSPENT", 0, wallet.getPoolSize(Pool.UNSPENT)); assertEquals("Wrong number of UNSPENT", 0, wallet.getPoolSize(Pool.UNSPENT));
assertEquals("Wrong number of ALL", 3, wallet.getTransactions(true).size()); assertEquals("Wrong number of ALL", 3, wallet.getTransactions(true).size());
assertEquals(valueOf(0, 59), wallet.getBalance(Wallet.BalanceType.ESTIMATED)); assertEquals(valueOf(0, 60), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
// Now we have another incoming pending // Now we have another incoming pending
return t; return t;
@ -250,7 +249,7 @@ public class WalletTest extends TestWithWallet {
assertEquals("Wrong number of PENDING", 1, wallet.getPoolSize(WalletTransaction.Pool.PENDING)); assertEquals("Wrong number of PENDING", 1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
assertEquals("Wrong number of UNSPENT", 0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals("Wrong number of UNSPENT", 0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals("Wrong number of ALL", 2, wallet.getTransactions(true).size()); assertEquals("Wrong number of ALL", 2, wallet.getTransactions(true).size());
assertEquals(valueOf(0, 49), wallet.getBalance(Wallet.BalanceType.ESTIMATED)); assertEquals(valueOf(0, 50), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
} }
@Test @Test
@ -259,13 +258,12 @@ public class WalletTest extends TestWithWallet {
Transaction t = cleanupCommon(destination); Transaction t = cleanupCommon(destination);
// Now we have another incoming pending. Spend everything. // Now we have another incoming pending. Spend everything.
Coin v3 = valueOf(0, 58); Coin v3 = valueOf(0, 60);
SendRequest req = SendRequest.to(destination, v3); SendRequest req = SendRequest.to(destination, v3);
// Force selection of the incoming coin so that we can spend it // Force selection of the incoming coin so that we can spend it
req.coinSelector = new TestCoinSelector(); req.coinSelector = new TestCoinSelector();
req.fee = CENT;
wallet.completeTx(req); wallet.completeTx(req);
wallet.commitTx(req.tx); wallet.commitTx(req.tx);
@ -307,7 +305,6 @@ public class WalletTest extends TestWithWallet {
// Prepare to send. // Prepare to send.
Coin v2 = valueOf(0, 50); Coin v2 = valueOf(0, 50);
req = Wallet.SendRequest.to(destination, v2); req = Wallet.SendRequest.to(destination, v2);
req.fee = CENT;
if (encryptedWallet != null) { if (encryptedWallet != null) {
KeyCrypter keyCrypter = encryptedWallet.getKeyCrypter(); KeyCrypter keyCrypter = encryptedWallet.getKeyCrypter();
@ -327,7 +324,6 @@ public class WalletTest extends TestWithWallet {
// Try to create a send with a fee but the wrong password (this should fail). // Try to create a send with a fee but the wrong password (this should fail).
req = Wallet.SendRequest.to(destination, v2); req = Wallet.SendRequest.to(destination, v2);
req.aesKey = wrongAesKey; req.aesKey = wrongAesKey;
req.fee = CENT;
req.ensureMinRequiredFee = false; req.ensureMinRequiredFee = false;
try { try {
@ -343,7 +339,6 @@ public class WalletTest extends TestWithWallet {
// Create a send with a fee with the correct password (this should succeed). // Create a send with a fee with the correct password (this should succeed).
req = Wallet.SendRequest.to(destination, v2); req = Wallet.SendRequest.to(destination, v2);
req.aesKey = aesKey; req.aesKey = aesKey;
req.fee = CENT;
req.ensureMinRequiredFee = false; req.ensureMinRequiredFee = false;
} }
@ -411,7 +406,7 @@ public class WalletTest extends TestWithWallet {
assertEquals("Wrong number of tx outputs",2, t.getOutputs().size()); assertEquals("Wrong number of tx outputs",2, t.getOutputs().size());
assertEquals(destination, t.getOutput(0).getScriptPubKey().getToAddress(PARAMS)); assertEquals(destination, t.getOutput(0).getScriptPubKey().getToAddress(PARAMS));
assertEquals(wallet.currentChangeAddress(), t.getOutputs().get(1).getScriptPubKey().getToAddress(PARAMS)); assertEquals(wallet.currentChangeAddress(), t.getOutputs().get(1).getScriptPubKey().getToAddress(PARAMS));
assertEquals(valueOf(0, 49), t.getOutputs().get(1).getValue()); assertEquals(valueOf(0, 50), t.getOutputs().get(1).getValue());
// Check the script runs and signatures verify. // Check the script runs and signatures verify.
t.getInputs().get(0).verify(); t.getInputs().get(0).verify();
} }
@ -439,7 +434,7 @@ public class WalletTest extends TestWithWallet {
private Wallet spendUnconfirmedChange(Wallet wallet, Transaction t2, KeyParameter aesKey) throws Exception { private Wallet spendUnconfirmedChange(Wallet wallet, Transaction t2, KeyParameter aesKey) throws Exception {
if (wallet.getTransactionSigners().size() == 1) // don't bother reconfiguring the p2sh wallet if (wallet.getTransactionSigners().size() == 1) // don't bother reconfiguring the p2sh wallet
wallet = roundTrip(wallet); wallet = roundTrip(wallet);
Coin v3 = valueOf(0, 49); Coin v3 = valueOf(0, 50);
assertEquals(v3, wallet.getBalance()); assertEquals(v3, wallet.getBalance());
Wallet.SendRequest req = Wallet.SendRequest.to(new ECKey().toAddress(PARAMS), valueOf(0, 48)); Wallet.SendRequest req = Wallet.SendRequest.to(new ECKey().toAddress(PARAMS), valueOf(0, 48));
req.aesKey = aesKey; req.aesKey = aesKey;
@ -2362,7 +2357,7 @@ public class WalletTest extends TestWithWallet {
// ...but not more fee than what we request // ...but not more fee than what we request
SendRequest request3 = SendRequest.to(notMyAddr, CENT.subtract(SATOSHI)); SendRequest request3 = SendRequest.to(notMyAddr, CENT.subtract(SATOSHI));
request3.fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(SATOSHI); request3.feePerKb = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(SATOSHI);
wallet.completeTx(request3); wallet.completeTx(request3);
assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(SATOSHI), request3.tx.getFee()); assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(SATOSHI), request3.tx.getFee());
Transaction spend3 = request3.tx; Transaction spend3 = request3.tx;
@ -2373,7 +2368,7 @@ public class WalletTest extends TestWithWallet {
// ...unless we need it // ...unless we need it
SendRequest request4 = SendRequest.to(notMyAddr, CENT.subtract(SATOSHI)); SendRequest request4 = SendRequest.to(notMyAddr, CENT.subtract(SATOSHI));
request4.fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.subtract(SATOSHI); request4.feePerKb = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.subtract(SATOSHI);
wallet.completeTx(request4); wallet.completeTx(request4);
assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE, request4.tx.getFee()); assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE, request4.tx.getFee());
Transaction spend4 = request4.tx; Transaction spend4 = request4.tx;
@ -2445,7 +2440,7 @@ public class WalletTest extends TestWithWallet {
SendRequest request11 = SendRequest.to(notMyAddr, COIN.subtract( SendRequest request11 = SendRequest.to(notMyAddr, COIN.subtract(
Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT).add(SATOSHI.multiply(2)))); Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT).add(SATOSHI.multiply(2))));
request11.fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(SATOSHI); request11.feePerKb = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(SATOSHI);
wallet.completeTx(request11); wallet.completeTx(request11);
assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(SATOSHI), request11.tx.getFee()); assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(SATOSHI), request11.tx.getFee());
Transaction spend11 = request11.tx; Transaction spend11 = request11.tx;
@ -2887,7 +2882,6 @@ public class WalletTest extends TestWithWallet {
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
request1.tx.addOutput(CENT, notMyAddr); request1.tx.addOutput(CENT, notMyAddr);
request1.tx.addOutput(new TransactionOutput(PARAMS, request1.tx, CENT, new byte[16])); request1.tx.addOutput(new TransactionOutput(PARAMS, request1.tx, CENT, new byte[16]));
request1.fee = SATOSHI;
request1.feePerKb = SATOSHI; request1.feePerKb = SATOSHI;
// We get a category 2 using COIN+CENT // We get a category 2 using COIN+CENT
// It spends COIN + 1(fee) and because its output is thus < CENT, we have to pay MIN_TX_FEE // It spends COIN + 1(fee) and because its output is thus < CENT, we have to pay MIN_TX_FEE
@ -3619,7 +3613,6 @@ public class WalletTest extends TestWithWallet {
// Send 3 BTC in a single transaction // Send 3 BTC in a single transaction
SendRequest req = SendRequest.to(notMyAddr,Coin.COIN.multiply(3)); SendRequest req = SendRequest.to(notMyAddr,Coin.COIN.multiply(3));
req.fee = CENT;
wallet.completeTx(req); wallet.completeTx(req);
StoredBlock block2 = createFakeBlock(blockStore, req.tx).storedBlock; StoredBlock block2 = createFakeBlock(blockStore, req.tx).storedBlock;
wallet.receiveFromBlock(req.tx, block2, AbstractBlockChain.NewBlockType.BEST_CHAIN, 0); wallet.receiveFromBlock(req.tx, block2, AbstractBlockChain.NewBlockType.BEST_CHAIN, 0);

View File

@ -219,7 +219,7 @@ public class WalletTool {
xpubkeysFlag = parser.accepts("xpubkeys").withRequiredArg(); xpubkeysFlag = parser.accepts("xpubkeys").withRequiredArg();
OptionSpec<String> outputFlag = parser.accepts("output").withRequiredArg(); OptionSpec<String> outputFlag = parser.accepts("output").withRequiredArg();
parser.accepts("value").withRequiredArg(); parser.accepts("value").withRequiredArg();
parser.accepts("fee").withRequiredArg(); OptionSpec<String> feePerKbOption = parser.accepts("fee-per-kb").withRequiredArg();
unixtimeFlag = parser.accepts("unixtime").withRequiredArg().ofType(Long.class); unixtimeFlag = parser.accepts("unixtime").withRequiredArg().ofType(Long.class);
OptionSpec<String> conditionFlag = parser.accepts("condition").withRequiredArg(); OptionSpec<String> conditionFlag = parser.accepts("condition").withRequiredArg();
parser.accepts("locktime").withRequiredArg(); parser.accepts("locktime").withRequiredArg();
@ -357,16 +357,15 @@ public class WalletTool {
System.err.println("--payment-request and --output cannot be used together."); System.err.println("--payment-request and --output cannot be used together.");
return; return;
} else if (options.has(outputFlag)) { } else if (options.has(outputFlag)) {
Coin fee = Coin.ZERO; Coin feePerKb = Coin.ZERO;
if (options.has("fee")) { if (options.has(feePerKbOption))
fee = parseCoin((String)options.valueOf("fee")); feePerKb = parseCoin((String) options.valueOf(feePerKbOption));
}
String lockTime = null; String lockTime = null;
if (options.has("locktime")) { if (options.has("locktime")) {
lockTime = (String) options.valueOf("locktime"); lockTime = (String) options.valueOf("locktime");
} }
boolean allowUnconfirmed = options.has("allow-unconfirmed"); boolean allowUnconfirmed = options.has("allow-unconfirmed");
send(outputFlag.values(options), fee, lockTime, allowUnconfirmed); send(outputFlag.values(options), feePerKb, lockTime, allowUnconfirmed);
} else if (options.has(paymentRequestLocation)) { } else if (options.has(paymentRequestLocation)) {
sendPaymentRequest(paymentRequestLocation.value(options), !options.has("no-pki")); sendPaymentRequest(paymentRequestLocation.value(options), !options.has("no-pki"));
} else { } else {
@ -379,10 +378,9 @@ public class WalletTool {
System.err.println("You must specify a --output=addr:value"); System.err.println("You must specify a --output=addr:value");
return; return;
} }
Coin fee = Coin.ZERO; Coin feePerKb = Coin.ZERO;
if (options.has("fee")) { if (options.has(feePerKbOption))
fee = parseCoin((String) options.valueOf("fee")); feePerKb = parseCoin((String) options.valueOf(feePerKbOption));
}
if (!options.has("locktime")) { if (!options.has("locktime")) {
System.err.println("You must specify a --locktime"); System.err.println("You must specify a --locktime");
return; return;
@ -393,39 +391,37 @@ public class WalletTool {
System.err.println("You must specify an address to refund money to after expiry: --refund-to=addr"); System.err.println("You must specify an address to refund money to after expiry: --refund-to=addr");
return; return;
} }
sendCLTVPaymentChannel(refundFlag.value(options), outputFlag.value(options), fee, lockTime, allowUnconfirmed); sendCLTVPaymentChannel(refundFlag.value(options), outputFlag.value(options), feePerKb, lockTime, allowUnconfirmed);
} break; } break;
case SETTLE_CLTVPAYMENTCHANNEL: { case SETTLE_CLTVPAYMENTCHANNEL: {
if (!options.has(outputFlag)) { if (!options.has(outputFlag)) {
System.err.println("You must specify a --output=addr:value"); System.err.println("You must specify a --output=addr:value");
return; return;
} }
Coin fee = Coin.ZERO; Coin feePerKb = Coin.ZERO;
if (options.has("fee")) { if (options.has(feePerKbOption))
fee = parseCoin((String) options.valueOf("fee")); feePerKb = parseCoin((String) options.valueOf(feePerKbOption));
}
boolean allowUnconfirmed = options.has("allow-unconfirmed"); boolean allowUnconfirmed = options.has("allow-unconfirmed");
if (!options.has(txHashFlag)) { if (!options.has(txHashFlag)) {
System.err.println("You must specify the transaction to spend: --txhash=tx-hash"); System.err.println("You must specify the transaction to spend: --txhash=tx-hash");
return; return;
} }
settleCLTVPaymentChannel(txHashFlag.value(options), outputFlag.value(options), fee, allowUnconfirmed); settleCLTVPaymentChannel(txHashFlag.value(options), outputFlag.value(options), feePerKb, allowUnconfirmed);
} break; } break;
case REFUND_CLTVPAYMENTCHANNEL: { case REFUND_CLTVPAYMENTCHANNEL: {
if (!options.has(outputFlag)) { if (!options.has(outputFlag)) {
System.err.println("You must specify a --output=addr:value"); System.err.println("You must specify a --output=addr:value");
return; return;
} }
Coin fee = Coin.ZERO; Coin feePerKb = Coin.ZERO;
if (options.has("fee")) { if (options.has(feePerKbOption))
fee = parseCoin((String) options.valueOf("fee")); feePerKb = parseCoin((String) options.valueOf(feePerKbOption));
}
boolean allowUnconfirmed = options.has("allow-unconfirmed"); boolean allowUnconfirmed = options.has("allow-unconfirmed");
if (!options.has(txHashFlag)) { if (!options.has(txHashFlag)) {
System.err.println("You must specify the transaction to spend: --txhash=tx-hash"); System.err.println("You must specify the transaction to spend: --txhash=tx-hash");
return; return;
} }
refundCLTVPaymentChannel(txHashFlag.value(options), outputFlag.value(options), fee, allowUnconfirmed); refundCLTVPaymentChannel(txHashFlag.value(options), outputFlag.value(options), feePerKb, allowUnconfirmed);
} break; } break;
case ENCRYPT: encrypt(); break; case ENCRYPT: encrypt(); break;
case DECRYPT: decrypt(); break; case DECRYPT: decrypt(); break;
@ -569,7 +565,7 @@ public class WalletTool {
} }
} }
private static void send(List<String> outputs, Coin fee, String lockTimeStr, boolean allowUnconfirmed) throws VerificationException { private static void send(List<String> outputs, Coin feePerKb, String lockTimeStr, boolean allowUnconfirmed) throws VerificationException {
try { try {
// Convert the input strings to outputs. // Convert the input strings to outputs.
Transaction t = new Transaction(params); Transaction t = new Transaction(params);
@ -600,7 +596,7 @@ public class WalletTool {
log.info("Emptying out wallet, recipient may get less than what you expect"); log.info("Emptying out wallet, recipient may get less than what you expect");
req.emptyWallet = true; req.emptyWallet = true;
} }
req.fee = fee; req.feePerKb = feePerKb;
if (allowUnconfirmed) { if (allowUnconfirmed) {
wallet.allowSpendingUnconfirmedTransactions(); wallet.allowSpendingUnconfirmedTransactions();
} }
@ -687,7 +683,7 @@ public class WalletTool {
} }
} }
private static void sendCLTVPaymentChannel(String refund, String output, Coin fee, String lockTimeStr, boolean allowUnconfirmed) throws VerificationException { private static void sendCLTVPaymentChannel(String refund, String output, Coin feePerKb, String lockTimeStr, boolean allowUnconfirmed) throws VerificationException {
try { try {
// Convert the input strings to outputs. // Convert the input strings to outputs.
ECKey outputKey, refundKey; ECKey outputKey, refundKey;
@ -731,7 +727,7 @@ public class WalletTool {
log.info("Emptying out wallet, recipient may get less than what you expect"); log.info("Emptying out wallet, recipient may get less than what you expect");
req.emptyWallet = true; req.emptyWallet = true;
} }
req.fee = fee; req.feePerKb = feePerKb;
if (allowUnconfirmed) { if (allowUnconfirmed) {
wallet.allowSpendingUnconfirmedTransactions(); wallet.allowSpendingUnconfirmedTransactions();
} }
@ -773,12 +769,8 @@ public class WalletTool {
/** /**
* Settles a CLTV payment channel transaction given that we own both private keys (ie. for testing). * Settles a CLTV payment channel transaction given that we own both private keys (ie. for testing).
* @param txHash
* @param output
* @param fee
* @param allowUnconfirmed
*/ */
private static void settleCLTVPaymentChannel(String txHash, String output, Coin fee, boolean allowUnconfirmed) { private static void settleCLTVPaymentChannel(String txHash, String output, Coin feePerKb, boolean allowUnconfirmed) {
try { try {
OutputSpec outputSpec; OutputSpec outputSpec;
Coin value; Coin value;
@ -802,7 +794,7 @@ public class WalletTool {
Wallet.SendRequest req = outputSpec.isAddress() ? Wallet.SendRequest req = outputSpec.isAddress() ?
Wallet.SendRequest.to(outputSpec.addr, value) : Wallet.SendRequest.to(outputSpec.addr, value) :
Wallet.SendRequest.to(params, outputSpec.key, value); Wallet.SendRequest.to(params, outputSpec.key, value);
req.fee = fee; req.feePerKb = feePerKb;
Transaction lockTimeVerify = wallet.getTransaction(Sha256Hash.wrap(txHash)); Transaction lockTimeVerify = wallet.getTransaction(Sha256Hash.wrap(txHash));
if (lockTimeVerify == null) { if (lockTimeVerify == null) {
@ -820,7 +812,7 @@ public class WalletTool {
return; return;
} }
if (!req.fee.add(value).equals(lockTimeVerifyOutput.getValue())) { if (!value.equals(lockTimeVerifyOutput.getValue())) {
System.err.println("You must spend all the money in the input transaction"); System.err.println("You must spend all the money in the input transaction");
} }
@ -880,12 +872,8 @@ public class WalletTool {
/** /**
* Refunds a CLTV payment channel transaction after the lock time has expired. * Refunds a CLTV payment channel transaction after the lock time has expired.
* @param txHash
* @param output
* @param fee
* @param allowUnconfirmed
*/ */
private static void refundCLTVPaymentChannel(String txHash, String output, Coin fee, boolean allowUnconfirmed) { private static void refundCLTVPaymentChannel(String txHash, String output, Coin feePerKb, boolean allowUnconfirmed) {
try { try {
OutputSpec outputSpec; OutputSpec outputSpec;
Coin value; Coin value;
@ -909,7 +897,7 @@ public class WalletTool {
Wallet.SendRequest req = outputSpec.isAddress() ? Wallet.SendRequest req = outputSpec.isAddress() ?
Wallet.SendRequest.to(outputSpec.addr, value) : Wallet.SendRequest.to(outputSpec.addr, value) :
Wallet.SendRequest.to(params, outputSpec.key, value); Wallet.SendRequest.to(params, outputSpec.key, value);
req.fee = fee; req.feePerKb = feePerKb;
Transaction lockTimeVerify = wallet.getTransaction(Sha256Hash.wrap(txHash)); Transaction lockTimeVerify = wallet.getTransaction(Sha256Hash.wrap(txHash));
if (lockTimeVerify == null) { if (lockTimeVerify == null) {
@ -929,7 +917,7 @@ public class WalletTool {
req.tx.setLockTime(lockTimeVerifyOutput.getScriptPubKey().getCLTVPaymentChannelExpiry().longValue()); req.tx.setLockTime(lockTimeVerifyOutput.getScriptPubKey().getCLTVPaymentChannelExpiry().longValue());
if (!req.fee.add(value).equals(lockTimeVerifyOutput.getValue())) { if (!value.equals(lockTimeVerifyOutput.getValue())) {
System.err.println("You must spend all the money in the input transaction"); System.err.println("You must spend all the money in the input transaction");
} }

View File

@ -48,7 +48,7 @@ Usage: wallet-tool --flags action-name
--payment-request=http://merchant.com/pay.php?123 --payment-request=http://merchant.com/pay.php?123
Other options include: Other options include:
--fee=0.01 sets the tx fee --fee-per-kb=0.0005 sets the tx fee
--locktime=1234 sets the lock time to block 1234 --locktime=1234 sets the lock time to block 1234
--locktime=2013/01/01 sets the lock time to 1st Jan 2013 --locktime=2013/01/01 sets the lock time to 1st Jan 2013
--allow-unconfirmed will let you create spends of pending non-change outputs. --allow-unconfirmed will let you create spends of pending non-change outputs.
@ -72,14 +72,14 @@ Usage: wallet-tool --flags action-name
Options: Options:
--output=pubkey:value sets the amount to lock and the recipient --output=pubkey:value sets the amount to lock and the recipient
--refund-to=pubkey sets "our" public key --refund-to=pubkey sets "our" public key
--fee=value sets the mining fee --fee-per-kb=value sets the network fee
--locktime=YYYY/MM/DD sets the expiry time for the channel --locktime=YYYY/MM/DD sets the expiry time for the channel
settle-cltvpaymentchannel settle-cltvpaymentchannel
Creates and broadcasts a transaction settling a previous micropayment channel. Creates and broadcasts a transaction settling a previous micropayment channel.
This tool, for testing, requires the presence of both private keys. This tool, for testing, requires the presence of both private keys.
Options: Options:
--output=pubkey:value sets the destination for the money --output=pubkey:value sets the destination for the money
--fee=value sets the mining fee --fee-per-kb=value sets the network fee
--txhash=hash sets the transaction to spend --txhash=hash sets the transaction to spend
refund-cltvpaymentchannel refund-cltvpaymentchannel
Creates and broadcasts a transaction refunding a previous micropayment channel. Creates and broadcasts a transaction refunding a previous micropayment channel.
@ -87,7 +87,7 @@ Usage: wallet-tool --flags action-name
the created transaction won't be accepted into the mempool until that point. the created transaction won't be accepted into the mempool until that point.
Options: Options:
--output=pubkey:value sets the destination for the money --output=pubkey:value sets the destination for the money
--fee=value sets the mining fee --fee-per-kb=value sets the network fee
--txhash=hash sets the transaction to spend --txhash=hash sets the transaction to spend
>>> GENERAL OPTIONS >>> GENERAL OPTIONS