Receive to and send from native segwit addresses

- Hierarchical-deterministic derivation of native segwit addresses.
- Receive payments to native segwit addresses.
- Spend and sign payments from native segwit addresses.
- Watch-only wallets with native segwit addresses (zpub/vpub).
- WalletAppKit, Wallet-tool and Wallet-template are taught to deal with segwit-enabled wallets.

Be aware this adds a new field in the wallet protobuf: output_script_type in Key, which keeps track
of the script type of DeterministicKeyChains. Protobufs will be migrated; old DeterministicKeyChains
are assumed to be of type P2PKH.

Includes some code by Fabrice Drouin.
This commit is contained in:
Andreas Schildbach
2018-04-21 09:57:02 +02:00
parent 691a3b1de8
commit bfe2a195b6
58 changed files with 1776 additions and 349 deletions

View File

@@ -24,6 +24,8 @@ import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.protocols.payments.PaymentProtocol;
import org.bitcoinj.protocols.payments.PaymentProtocolException;
import org.bitcoinj.protocols.payments.PaymentSession;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.Script.ScriptType;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.script.ScriptException;
import org.bitcoinj.script.ScriptPattern;
@@ -65,6 +67,7 @@ import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Peer;
import org.bitcoinj.core.PeerAddress;
import org.bitcoinj.core.PeerGroup;
import org.bitcoinj.core.SegwitAddress;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.StoredBlock;
import org.bitcoinj.core.Transaction;
@@ -121,6 +124,7 @@ public class WalletTool {
private static OptionSpec<Date> dateFlag;
private static OptionSpec<Long> unixtimeFlag;
private static OptionSpec<String> seedFlag, watchFlag;
private static OptionSpec<Script.ScriptType> outputScriptTypeFlag;
private static OptionSpec<String> xpubkeysFlag;
private static NetworkParameters params;
@@ -233,6 +237,7 @@ public class WalletTool {
OptionSpec<String> walletFileName = parser.accepts("wallet").withRequiredArg().defaultsTo("wallet");
seedFlag = parser.accepts("seed").withRequiredArg();
watchFlag = parser.accepts("watchkey").withRequiredArg();
outputScriptTypeFlag = parser.accepts("output-script-type").withRequiredArg().ofType(Script.ScriptType.class);
OptionSpec<NetworkEnum> netFlag = parser.accepts("net").withRequiredArg().ofType(NetworkEnum.class).defaultsTo(NetworkEnum.MAIN);
dateFlag = parser.accepts("date").withRequiredArg().ofType(Date.class)
.withValuesConvertedBy(DateConverter.datePattern("yyyy/MM/dd"));
@@ -1313,6 +1318,7 @@ public class WalletTool {
return;
}
long creationTimeSecs = getCreationTimeSeconds();
ScriptType outputScriptType = options.valueOf(outputScriptTypeFlag);
if (creationTimeSecs == 0)
creationTimeSecs = MnemonicCode.BIP39_STANDARDISATION_TIME_SECS;
if (options.has(seedFlag)) {
@@ -1337,11 +1343,11 @@ public class WalletTool {
// not reached - all subclasses handled above
throw new RuntimeException(e);
}
wallet = Wallet.fromSeed(params, seed);
wallet = Wallet.fromSeed(params, seed, outputScriptType);
} else if (options.has(watchFlag)) {
wallet = Wallet.fromWatchingKeyB58(params, options.valueOf(watchFlag), creationTimeSecs);
} else {
wallet = new Wallet(params);
wallet = Wallet.createDeterministic(params, outputScriptType);
}
if (password != null)
wallet.encrypt(password);
@@ -1448,7 +1454,10 @@ public class WalletTool {
if (!key.isCompressed())
System.out.println("WARNING: Importing an uncompressed key");
wallet.importKey(key);
System.out.println(LegacyAddress.fromKey(params, key) + " " + key);
System.out.print("Addresses: " + LegacyAddress.fromKey(params, key));
if (key.isCompressed())
System.out.print("," + SegwitAddress.fromKey(params, key));
System.out.println();
}
/**
@@ -1489,7 +1498,7 @@ public class WalletTool {
key = wallet.findKeyFromPubKey(HEX.decode(pubKey));
} else {
try {
Address address = LegacyAddress.fromBase58(wallet.getParams(), addr);
Address address = Address.fromString(wallet.getParams(), addr);
key = wallet.findKeyFromAddress(address);
} catch (AddressFormatException e) {
System.err.println(addr + " does not parse as a Bitcoin address of the right network parameters.");
@@ -1508,8 +1517,8 @@ public class WalletTool {
}
private static void currentReceiveAddr() {
ECKey key = wallet.currentReceiveKey();
System.out.println(LegacyAddress.fromKey(params, key) + " " + key);
Address address = wallet.currentReceiveAddress();
System.out.println(address);
}
private static void dumpWallet() throws BlockStoreException {

View File

@@ -12,7 +12,8 @@ Usage: wallet-tool --flags action-name
Will complain and require --force if the wallet already exists.
If --seed is present, it should specify either a mnemonic code or hex/base58 raw seed bytes.
If --watchkey is present, it creates a watching wallet using the specified base58 xpub.
If --seed or --watchkey is combined with either --date or --unixtime, use that as a birthdate for
If --seed or --watchkey is combined with either --date or --unixtime, use that as a birthdate for.
If --output-script-type, use that for deriving addresses.
the wallet. See the set-creation-time action for the meaning of these flags.
marry Makes the wallet married with other parties, requiring multisig to spend funds.
External public keys for other signing parties must be specified with --xpubkeys (comma separated).