Teach SendRequest and Wallet to send to native segwit addresses.

WalletTool and FakeTxBuilder can do it, too.
This commit is contained in:
Andreas Schildbach
2018-02-23 20:03:27 +01:00
parent 2c768bfe07
commit 52afdc629e
8 changed files with 44 additions and 25 deletions

View File

@@ -900,7 +900,7 @@ public class Block extends Message {
* Returns a solved block that builds on top of this one. This exists for unit tests. * Returns a solved block that builds on top of this one. This exists for unit tests.
*/ */
@VisibleForTesting @VisibleForTesting
public Block createNextBlock(LegacyAddress to, long version, long time, int blockHeight) { public Block createNextBlock(Address to, long version, long time, int blockHeight) {
return createNextBlock(to, version, null, time, pubkeyForTesting, FIFTY_COINS, blockHeight); return createNextBlock(to, version, null, time, pubkeyForTesting, FIFTY_COINS, blockHeight);
} }
@@ -910,7 +910,7 @@ public class Block extends Message {
* *
* @param height block height, if known, or -1 otherwise. * @param height block height, if known, or -1 otherwise.
*/ */
Block createNextBlock(@Nullable final LegacyAddress to, final long version, Block createNextBlock(@Nullable final Address to, final long version,
@Nullable TransactionOutPoint prevOut, final long time, @Nullable TransactionOutPoint prevOut, final long time,
final byte[] pubKey, final Coin coinbaseValue, final byte[] pubKey, final Coin coinbaseValue,
final int height) { final int height) {
@@ -958,17 +958,17 @@ public class Block extends Message {
} }
@VisibleForTesting @VisibleForTesting
public Block createNextBlock(@Nullable LegacyAddress to, TransactionOutPoint prevOut) { public Block createNextBlock(@Nullable Address to, TransactionOutPoint prevOut) {
return createNextBlock(to, BLOCK_VERSION_GENESIS, prevOut, getTimeSeconds() + 5, pubkeyForTesting, FIFTY_COINS, BLOCK_HEIGHT_UNKNOWN); return createNextBlock(to, BLOCK_VERSION_GENESIS, prevOut, getTimeSeconds() + 5, pubkeyForTesting, FIFTY_COINS, BLOCK_HEIGHT_UNKNOWN);
} }
@VisibleForTesting @VisibleForTesting
public Block createNextBlock(@Nullable LegacyAddress to, Coin value) { public Block createNextBlock(@Nullable Address to, Coin value) {
return createNextBlock(to, BLOCK_VERSION_GENESIS, null, getTimeSeconds() + 5, pubkeyForTesting, value, BLOCK_HEIGHT_UNKNOWN); return createNextBlock(to, BLOCK_VERSION_GENESIS, null, getTimeSeconds() + 5, pubkeyForTesting, value, BLOCK_HEIGHT_UNKNOWN);
} }
@VisibleForTesting @VisibleForTesting
public Block createNextBlock(@Nullable LegacyAddress to) { public Block createNextBlock(@Nullable Address to) {
return createNextBlock(to, FIFTY_COINS); return createNextBlock(to, FIFTY_COINS);
} }

View File

@@ -884,7 +884,7 @@ public class Transaction extends ChildMessage {
/** /**
* Creates an output based on the given address and value, adds it to this transaction, and returns the new output. * Creates an output based on the given address and value, adds it to this transaction, and returns the new output.
*/ */
public TransactionOutput addOutput(Coin value, LegacyAddress address) { public TransactionOutput addOutput(Coin value, Address address) {
return addOutput(new TransactionOutput(params, this, value, address)); return addOutput(new TransactionOutput(params, this, value, address));
} }

View File

@@ -84,9 +84,9 @@ public class TransactionOutput extends ChildMessage {
/** /**
* Creates an output that sends 'value' to the given address (public key hash). The amount should be created with * Creates an output that sends 'value' to the given address (public key hash). The amount should be created with
* something like {@link Coin#valueOf(int, int)}. Typically you would use * something like {@link Coin#valueOf(int, int)}. Typically you would use
* {@link Transaction#addOutput(Coin, LegacyAddress)} instead of creating a TransactionOutput directly. * {@link Transaction#addOutput(Coin, Address)} instead of creating a TransactionOutput directly.
*/ */
public TransactionOutput(NetworkParameters params, @Nullable Transaction parent, Coin value, LegacyAddress to) { public TransactionOutput(NetworkParameters params, @Nullable Transaction parent, Coin value, Address to) {
this(params, parent, value, ScriptBuilder.createOutputScript(to).getProgram()); this(params, parent, value, ScriptBuilder.createOutputScript(to).getProgram());
} }

View File

@@ -24,6 +24,7 @@ import java.math.BigInteger;
import java.util.Date; import java.util.Date;
import org.bitcoin.protocols.payments.Protos.PaymentDetails; import org.bitcoin.protocols.payments.Protos.PaymentDetails;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Context; import org.bitcoinj.core.Context;
@@ -163,7 +164,7 @@ public class SendRequest {
* <p>Be very careful when value is smaller than {@link Transaction#MIN_NONDUST_OUTPUT} as the transaction will * <p>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.</p> * likely be rejected by the network in this case.</p>
*/ */
public static SendRequest to(LegacyAddress destination, Coin value) { public static SendRequest to(Address destination, Coin value) {
SendRequest req = new SendRequest(); SendRequest req = new SendRequest();
final NetworkParameters parameters = destination.getParameters(); final NetworkParameters parameters = destination.getParameters();
checkNotNull(parameters, "Address is for an unknown network"); checkNotNull(parameters, "Address is for an unknown network");
@@ -177,7 +178,7 @@ public class SendRequest {
* *
* <p>Be careful to check the output's value is reasonable using * <p>Be careful to check the output's value is reasonable using
* {@link TransactionOutput#getMinNonDustValue(Coin)} afterwards or you risk having the transaction * {@link TransactionOutput#getMinNonDustValue(Coin)} afterwards or you risk having the transaction
* rejected by the network. Note that using {@link SendRequest#to(LegacyAddress, Coin)} will result * rejected by the network. Note that using {@link SendRequest#to(Address, Coin)} will result
* in a smaller output, and thus the ability to use a smaller output value without rejection.</p> * in a smaller output, and thus the ability to use a smaller output value without rejection.</p>
*/ */
public static SendRequest to(NetworkParameters params, ECKey destination, Coin value) { public static SendRequest to(NetworkParameters params, ECKey destination, Coin value) {
@@ -194,7 +195,7 @@ public class SendRequest {
return req; return req;
} }
public static SendRequest emptyWallet(LegacyAddress destination) { public static SendRequest emptyWallet(Address destination) {
SendRequest req = new SendRequest(); SendRequest req = new SendRequest();
final NetworkParameters parameters = destination.getParameters(); final NetworkParameters parameters = destination.getParameters();
checkNotNull(parameters, "Address is for an unknown network"); checkNotNull(parameters, "Address is for an unknown network");

View File

@@ -25,6 +25,7 @@ import com.google.common.util.concurrent.*;
import com.google.protobuf.*; import com.google.protobuf.*;
import net.jcip.annotations.*; import net.jcip.annotations.*;
import org.bitcoinj.core.listeners.*; import org.bitcoinj.core.listeners.*;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.AbstractBlockChain; import org.bitcoinj.core.AbstractBlockChain;
import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.BlockChain; import org.bitcoinj.core.BlockChain;
@@ -3779,11 +3780,11 @@ public class Wallet extends BaseTaggableObject
* {@link Wallet#currentChangeAddress()}, so you must have added at least one key.</p> * {@link Wallet#currentChangeAddress()}, so you must have added at least one key.</p>
* *
* <p>If you just want to send money quickly, you probably want * <p>If you just want to send money quickly, you probably want
* {@link Wallet#sendCoins(TransactionBroadcaster, LegacyAddress, Coin)} instead. That will create the sending * {@link Wallet#sendCoins(TransactionBroadcaster, Address, Coin)} instead. That will create the sending
* transaction, commit to the wallet and broadcast it to the network all in one go. This method is lower level * transaction, commit to the wallet and broadcast it to the network all in one go. This method is lower level
* and lets you see the proposed transaction before anything is done with it.</p> * and lets you see the proposed transaction before anything is done with it.</p>
* *
* <p>This is a helper method that is equivalent to using {@link SendRequest#to(LegacyAddress, Coin)} * <p>This is a helper method that is equivalent to using {@link SendRequest#to(Address, Coin)}
* followed by {@link Wallet#completeTx(SendRequest)} and returning the requests transaction object. * followed by {@link Wallet#completeTx(SendRequest)} and returning the requests transaction object.
* Note that this means a fee may be automatically added if required, if you want more control over the process, * Note that this means a fee may be automatically added if required, if you want more control over the process,
* just do those two steps yourself.</p> * just do those two steps yourself.</p>
@@ -3805,7 +3806,7 @@ public class Wallet extends BaseTaggableObject
* @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process. * @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process.
* @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction. * @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction.
*/ */
public Transaction createSend(LegacyAddress address, Coin value) throws InsufficientMoneyException { public Transaction createSend(Address address, Coin value) throws InsufficientMoneyException {
SendRequest req = SendRequest.to(address, value); SendRequest req = SendRequest.to(address, value);
if (params.getId().equals(NetworkParameters.ID_UNITTESTNET)) if (params.getId().equals(NetworkParameters.ID_UNITTESTNET))
req.shuffleOutputs = false; req.shuffleOutputs = false;
@@ -3864,7 +3865,7 @@ public class Wallet extends BaseTaggableObject
* @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process. * @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process.
* @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction. * @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction.
*/ */
public SendResult sendCoins(TransactionBroadcaster broadcaster, LegacyAddress to, Coin value) throws InsufficientMoneyException { public SendResult sendCoins(TransactionBroadcaster broadcaster, Address to, Coin value) throws InsufficientMoneyException {
SendRequest request = SendRequest.to(to, value); SendRequest request = SendRequest.to(to, value);
return sendCoins(broadcaster, request); return sendCoins(broadcaster, request);
} }

View File

@@ -17,7 +17,23 @@
package org.bitcoinj.testing; package org.bitcoinj.testing;
import org.bitcoinj.core.*; import org.bitcoinj.core.Address;
import org.bitcoinj.core.Block;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.MessageSerializer;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.ProtocolException;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.StoredBlock;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.TransactionInput;
import org.bitcoinj.core.TransactionOutPoint;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.core.Utils;
import org.bitcoinj.core.VerificationException;
import org.bitcoinj.crypto.TransactionSignature; import org.bitcoinj.crypto.TransactionSignature;
import org.bitcoinj.script.ScriptBuilder; import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.store.BlockStore; import org.bitcoinj.store.BlockStore;
@@ -64,7 +80,7 @@ public class FakeTxBuilder {
* Create a fake TX of sufficient realism to exercise the unit tests. Two outputs, one to us, one to somewhere * Create a fake TX of sufficient realism to exercise the unit tests. Two outputs, one to us, one to somewhere
* else to simulate change. There is one random input. * else to simulate change. There is one random input.
*/ */
public static Transaction createFakeTxWithChangeAddress(NetworkParameters params, Coin value, LegacyAddress to, LegacyAddress changeOutput) { public static Transaction createFakeTxWithChangeAddress(NetworkParameters params, Coin value, Address to, Address changeOutput) {
Transaction t = new Transaction(params); Transaction t = new Transaction(params);
TransactionOutput outputToMe = new TransactionOutput(params, t, value, to); TransactionOutput outputToMe = new TransactionOutput(params, t, value, to);
t.addOutput(outputToMe); t.addOutput(outputToMe);
@@ -86,7 +102,7 @@ public class FakeTxBuilder {
* Create a fake TX for unit tests, for use with unit tests that need greater control. One outputs, 2 random inputs, * Create a fake TX for unit tests, for use with unit tests that need greater control. One outputs, 2 random inputs,
* split randomly to create randomness. * split randomly to create randomness.
*/ */
public static Transaction createFakeTxWithoutChangeAddress(NetworkParameters params, Coin value, LegacyAddress to) { public static Transaction createFakeTxWithoutChangeAddress(NetworkParameters params, Coin value, Address to) {
Transaction t = new Transaction(params); Transaction t = new Transaction(params);
TransactionOutput outputToMe = new TransactionOutput(params, t, value, to); TransactionOutput outputToMe = new TransactionOutput(params, t, value, to);
t.addOutput(outputToMe); t.addOutput(outputToMe);
@@ -122,7 +138,7 @@ public class FakeTxBuilder {
* Create a fake TX of sufficient realism to exercise the unit tests. Two outputs, one to us, one to somewhere * Create a fake TX of sufficient realism to exercise the unit tests. Two outputs, one to us, one to somewhere
* else to simulate change. There is one random input. * else to simulate change. There is one random input.
*/ */
public static Transaction createFakeTx(NetworkParameters params, Coin value, LegacyAddress to) { public static Transaction createFakeTx(NetworkParameters params, Coin value, Address to) {
return createFakeTxWithChangeAddress(params, value, to, LegacyAddress.fromKey(params, new ECKey())); return createFakeTxWithChangeAddress(params, value, to, LegacyAddress.fromKey(params, new ECKey()));
} }
@@ -151,7 +167,7 @@ public class FakeTxBuilder {
* Transaction[0] is a feeder transaction, supplying BTC to Transaction[1] * Transaction[0] is a feeder transaction, supplying BTC to Transaction[1]
*/ */
public static Transaction[] createFakeTx(NetworkParameters params, Coin value, public static Transaction[] createFakeTx(NetworkParameters params, Coin value,
LegacyAddress to, LegacyAddress from) { Address to, Address from) {
// Create fake TXes of sufficient realism to exercise the unit tests. This transaction send BTC from the // Create fake TXes of sufficient realism to exercise the unit tests. This transaction send BTC from the
// from address, to the to address with to one to somewhere else to simulate change. // from address, to the to address with to one to somewhere else to simulate change.
Transaction t = new Transaction(params); Transaction t = new Transaction(params);
@@ -200,7 +216,7 @@ public class FakeTxBuilder {
* Creates two transactions that spend the same (fake) output. t1 spends to "to". t2 spends somewhere else. * Creates two transactions that spend the same (fake) output. t1 spends to "to". t2 spends somewhere else.
* The fake output goes to the same address as t2. * The fake output goes to the same address as t2.
*/ */
public static DoubleSpends createFakeDoubleSpendTxns(NetworkParameters params, LegacyAddress to) { public static DoubleSpends createFakeDoubleSpendTxns(NetworkParameters params, Address to) {
DoubleSpends doubleSpends = new DoubleSpends(); DoubleSpends doubleSpends = new DoubleSpends();
Coin value = COIN; Coin value = COIN;
LegacyAddress someBadGuy = LegacyAddress.fromKey(params, new ECKey()); LegacyAddress someBadGuy = LegacyAddress.fromKey(params, new ECKey());
@@ -290,7 +306,7 @@ public class FakeTxBuilder {
return createFakeBlock(blockStore, Block.BLOCK_VERSION_GENESIS, Utils.currentTimeSeconds(), 0, transactions); return createFakeBlock(blockStore, Block.BLOCK_VERSION_GENESIS, Utils.currentTimeSeconds(), 0, transactions);
} }
public static Block makeSolvedTestBlock(BlockStore blockStore, LegacyAddress coinsTo) throws BlockStoreException { public static Block makeSolvedTestBlock(BlockStore blockStore, Address coinsTo) throws BlockStoreException {
Block b = blockStore.getChainHead().getHeader().createNextBlock(coinsTo); Block b = blockStore.getChainHead().getHeader().createNextBlock(coinsTo);
b.solve(); b.solve();
return b; return b;
@@ -307,7 +323,7 @@ public class FakeTxBuilder {
return b; return b;
} }
public static Block makeSolvedTestBlock(Block prev, LegacyAddress to, Transaction... transactions) throws BlockStoreException { public static Block makeSolvedTestBlock(Block prev, Address to, Transaction... transactions) throws BlockStoreException {
Block b = prev.createNextBlock(to); Block b = prev.createNextBlock(to);
// Coinbase tx already exists. // Coinbase tx already exists.
for (Transaction tx : transactions) { for (Transaction tx : transactions) {

View File

@@ -680,7 +680,7 @@ public class WalletTool {
static class OutputSpec { static class OutputSpec {
public final Coin value; public final Coin value;
public final LegacyAddress addr; public final Address addr;
public final ECKey key; public final ECKey key;
public OutputSpec(String spec) throws IllegalArgumentException { public OutputSpec(String spec) throws IllegalArgumentException {
@@ -700,7 +700,7 @@ public class WalletTool {
addr = null; addr = null;
} else { } else {
// Treat as an address. // Treat as an address.
addr = LegacyAddress.fromBase58(params, destination); addr = Address.fromString(params, destination);
key = null; key = null;
} }
} }

View File

@@ -38,6 +38,7 @@ Usage: wallet-tool --flags action-name
You can repeat --output=address:value multiple times. You can repeat --output=address:value multiple times.
There is a magic value ALL which empties the wallet to that address, e.g.: There is a magic value ALL which empties the wallet to that address, e.g.:
--output=1GthXFQMktFLWdh5EPNGqbq3H6WdG8zsWj:ALL --output=1GthXFQMktFLWdh5EPNGqbq3H6WdG8zsWj:ALL
The output destination can also be a native segwit address.
If the output destination starts with 04 and is 65 or 33 bytes long it will be If the output destination starts with 04 and is 65 or 33 bytes long it will be
treated as a public key instead of an address and the send will use treated as a public key instead of an address and the send will use
<key> CHECKSIG as the script. <key> CHECKSIG as the script.