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

SendRequest: New .childPaysForParent() method that constructs a CPFP transaction.

This commit is contained in:
Andreas Schildbach 2016-03-01 14:49:49 +01:00
parent 85f7c39aa5
commit 2ab367b4e4
2 changed files with 51 additions and 0 deletions

View File

@ -33,6 +33,7 @@ import org.bitcoinj.signers.*;
import org.bitcoinj.store.*; import org.bitcoinj.store.*;
import org.bitcoinj.utils.*; import org.bitcoinj.utils.*;
import org.bitcoinj.wallet.*; import org.bitcoinj.wallet.*;
import org.bitcoinj.wallet.KeyChain.KeyPurpose;
import org.bitcoinj.wallet.Protos.Wallet.*; import org.bitcoinj.wallet.Protos.Wallet.*;
import org.bitcoinj.wallet.WalletTransaction.*; import org.bitcoinj.wallet.WalletTransaction.*;
import org.slf4j.*; import org.slf4j.*;
@ -3835,6 +3836,32 @@ public class Wallet extends BaseTaggableObject
return req; return req;
} }
/**
* Construct a SendRequest for a CPFP (child-pays-for-parent) transaction. The resulting transaction is already
* completed, so you should directly proceed to signing and broadcasting/committing the transaction. CPFP is
* currently only supported by a few miners, so use with care.
*/
public static SendRequest childPaysForParent(Wallet wallet, Transaction parentTransaction, Coin feeRaise) {
TransactionOutput outputToSpend = null;
for (final TransactionOutput output : parentTransaction.getOutputs()) {
if (output.isMine(wallet) && output.isAvailableForSpending()
&& output.getValue().isGreaterThan(feeRaise)) {
outputToSpend = output;
break;
}
}
// TODO spend another confirmed output of own wallet if needed
checkNotNull(outputToSpend, "Can't find adequately sized output that spends to us");
final Transaction tx = new Transaction(parentTransaction.getParams());
tx.addInput(outputToSpend);
tx.addOutput(outputToSpend.getValue().subtract(feeRaise), wallet.freshAddress(KeyPurpose.CHANGE));
tx.setPurpose(Transaction.Purpose.RAISE_FEE);
final SendRequest req = forTx(tx);
req.completed = true;
return req;
}
public static SendRequest toCLTVPaymentChannel(NetworkParameters params, Date releaseTime, ECKey from, ECKey to, Coin value) { public static SendRequest toCLTVPaymentChannel(NetworkParameters params, Date releaseTime, ECKey from, ECKey to, Coin value) {
long time = releaseTime.getTime() / 1000L; long time = releaseTime.getTime() / 1000L;
checkArgument(time >= Transaction.LOCKTIME_THRESHOLD, "Release time was too small"); checkArgument(time >= Transaction.LOCKTIME_THRESHOLD, "Release time was too small");

View File

@ -22,6 +22,7 @@ import org.bitcoinj.core.listeners.WalletCoinsReceivedEventListener;
import org.bitcoinj.core.listeners.WalletCoinsSentEventListener; import org.bitcoinj.core.listeners.WalletCoinsSentEventListener;
import org.bitcoinj.core.listeners.TransactionConfidenceEventListener; import org.bitcoinj.core.listeners.TransactionConfidenceEventListener;
import org.bitcoinj.core.TransactionConfidence.ConfidenceType; import org.bitcoinj.core.TransactionConfidence.ConfidenceType;
import org.bitcoinj.core.Wallet.BalanceType;
import org.bitcoinj.core.Wallet.SendRequest; import org.bitcoinj.core.Wallet.SendRequest;
import org.bitcoinj.crypto.*; import org.bitcoinj.crypto.*;
import org.bitcoinj.script.Script; import org.bitcoinj.script.Script;
@ -3062,6 +3063,29 @@ public class WalletTest extends TestWithWallet {
assertEquals(outputValue, request.tx.getOutput(0).getValue()); assertEquals(outputValue, request.tx.getOutput(0).getValue());
} }
@Test
public void childPaysForParent() throws Exception {
// Receive confirmed balance to play with.
Transaction toMe = createFakeTxWithoutChangeAddress(PARAMS, COIN, myAddress);
wallet.receiveFromBlock(toMe, createFakeBlock(blockStore, toMe).storedBlock,
AbstractBlockChain.NewBlockType.BEST_CHAIN, 0);
assertEquals(Coin.COIN, wallet.getBalance(BalanceType.ESTIMATED_SPENDABLE));
assertEquals(Coin.COIN, wallet.getBalance(BalanceType.AVAILABLE_SPENDABLE));
// Receive unconfirmed coin without fee.
Transaction toMeWithoutFee = createFakeTxWithoutChangeAddress(PARAMS, COIN, myAddress);
wallet.receivePending(toMeWithoutFee, null);
assertEquals(Coin.COIN.multiply(2), wallet.getBalance(BalanceType.ESTIMATED_SPENDABLE));
assertEquals(Coin.COIN, wallet.getBalance(BalanceType.AVAILABLE_SPENDABLE));
// Craft a child-pays-for-parent transaction.
final Coin feeRaise = MILLICOIN;
final SendRequest sendRequest = SendRequest.childPaysForParent(wallet, toMeWithoutFee, feeRaise);
wallet.signTransaction(sendRequest);
wallet.commitTx(sendRequest.tx);
assertEquals(Transaction.Purpose.RAISE_FEE, sendRequest.tx.getPurpose());
assertEquals(Coin.COIN.multiply(2).subtract(feeRaise), wallet.getBalance(BalanceType.ESTIMATED_SPENDABLE));
assertEquals(Coin.COIN, wallet.getBalance(BalanceType.AVAILABLE_SPENDABLE));
}
@Test @Test
public void keyRotationRandom() throws Exception { public void keyRotationRandom() throws Exception {
Utils.setMockClock(); Utils.setMockClock();