mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-02-23 14:40:40 +01:00
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:
parent
691a3b1de8
commit
bfe2a195b6
58 changed files with 1776 additions and 349 deletions
|
@ -19,6 +19,8 @@
|
|||
package org.bitcoinj.core;
|
||||
|
||||
import org.bitcoinj.crypto.*;
|
||||
import org.bitcoinj.script.Script;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
|
@ -1288,10 +1290,15 @@ public class ECKey implements EncryptableItem {
|
|||
}
|
||||
|
||||
public void formatKeyWithAddress(boolean includePrivateKeys, @Nullable KeyParameter aesKey, StringBuilder builder,
|
||||
NetworkParameters params) {
|
||||
final Address address = LegacyAddress.fromKey(params, this);
|
||||
NetworkParameters params, Script.ScriptType outputScriptType) {
|
||||
builder.append(" addr:");
|
||||
builder.append(address.toString());
|
||||
if (outputScriptType != null) {
|
||||
builder.append(Address.fromKey(params, this, outputScriptType));
|
||||
} else {
|
||||
builder.append(LegacyAddress.fromKey(params, this));
|
||||
if (isCompressed())
|
||||
builder.append(',').append(SegwitAddress.fromKey(params, this));
|
||||
}
|
||||
if (!isCompressed())
|
||||
builder.append(" UNCOMPRESSED");
|
||||
builder.append(" hash160:");
|
||||
|
|
|
@ -81,8 +81,10 @@ public abstract class NetworkParameters {
|
|||
protected int interval;
|
||||
protected int targetTimespan;
|
||||
protected byte[] alertSigningKey;
|
||||
protected int bip32HeaderPub;
|
||||
protected int bip32HeaderPriv;
|
||||
protected int bip32HeaderP2PKHpub;
|
||||
protected int bip32HeaderP2PKHpriv;
|
||||
protected int bip32HeaderP2WPKHpub;
|
||||
protected int bip32HeaderP2WPKHpriv;
|
||||
|
||||
/** Used to check majorities for block version upgrade */
|
||||
protected int majorityEnforceBlockUpgrade;
|
||||
|
@ -370,16 +372,25 @@ public abstract class NetworkParameters {
|
|||
return alertSigningKey;
|
||||
}
|
||||
|
||||
/** Returns the 4 byte header for BIP32 (HD) wallet - public key part. */
|
||||
public int getBip32HeaderPub() {
|
||||
return bip32HeaderPub;
|
||||
/** Returns the 4 byte header for BIP32 wallet P2PKH - public key part. */
|
||||
public int getBip32HeaderP2PKHpub() {
|
||||
return bip32HeaderP2PKHpub;
|
||||
}
|
||||
|
||||
/** Returns the 4 byte header for BIP32 (HD) wallet - private key part. */
|
||||
public int getBip32HeaderPriv() {
|
||||
return bip32HeaderPriv;
|
||||
/** Returns the 4 byte header for BIP32 wallet P2PKH - private key part. */
|
||||
public int getBip32HeaderP2PKHpriv() {
|
||||
return bip32HeaderP2PKHpriv;
|
||||
}
|
||||
|
||||
/** Returns the 4 byte header for BIP32 wallet P2WPKH - public key part. */
|
||||
public int getBip32HeaderP2WPKHpub() {
|
||||
return bip32HeaderP2WPKHpub;
|
||||
}
|
||||
|
||||
/** Returns the 4 byte header for BIP32 wallet P2WPKH - private key part. */
|
||||
public int getBip32HeaderP2WPKHpriv() {
|
||||
return bip32HeaderP2WPKHpriv;
|
||||
}
|
||||
/**
|
||||
* Returns the number of coins that will be produced in total, on this
|
||||
* network. Where not applicable, a very large number of coins is returned
|
||||
|
|
|
@ -921,17 +921,29 @@ public class Transaction extends ChildMessage {
|
|||
SigHash sigHash, boolean anyoneCanPay) throws ScriptException {
|
||||
// Verify the API user didn't try to do operations out of order.
|
||||
checkState(!outputs.isEmpty(), "Attempting to sign tx without outputs.");
|
||||
TransactionInput input = new TransactionInput(params, this, new byte[]{}, prevOut);
|
||||
TransactionInput input = new TransactionInput(params, this, new byte[] {}, prevOut);
|
||||
addInput(input);
|
||||
Sha256Hash hash = hashForSignature(inputs.size() - 1, scriptPubKey, sigHash, anyoneCanPay);
|
||||
ECKey.ECDSASignature ecSig = sigKey.sign(hash);
|
||||
TransactionSignature txSig = new TransactionSignature(ecSig, sigHash, anyoneCanPay);
|
||||
if (ScriptPattern.isPayToPubKey(scriptPubKey))
|
||||
input.setScriptSig(ScriptBuilder.createInputScript(txSig));
|
||||
else if (ScriptPattern.isPayToPubKeyHash(scriptPubKey))
|
||||
input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey));
|
||||
else
|
||||
int inputIndex = inputs.size() - 1;
|
||||
if (ScriptPattern.isPayToPubKey(scriptPubKey)) {
|
||||
TransactionSignature signature = calculateSignature(inputIndex, sigKey, scriptPubKey, sigHash,
|
||||
anyoneCanPay);
|
||||
input.setScriptSig(ScriptBuilder.createInputScript(signature));
|
||||
input.setWitness(null);
|
||||
} else if (ScriptPattern.isPayToPubKeyHash(scriptPubKey)) {
|
||||
TransactionSignature signature = calculateSignature(inputIndex, sigKey, scriptPubKey, sigHash,
|
||||
anyoneCanPay);
|
||||
input.setScriptSig(ScriptBuilder.createInputScript(signature, sigKey));
|
||||
input.setWitness(null);
|
||||
} else if (ScriptPattern.isPayToWitnessPubKeyHash(scriptPubKey)) {
|
||||
Script scriptCode = new ScriptBuilder()
|
||||
.data(ScriptBuilder.createOutputScript(LegacyAddress.fromKey(params, sigKey)).getProgram()).build();
|
||||
TransactionSignature signature = calculateWitnessSignature(inputIndex, sigKey, scriptCode, input.getValue(),
|
||||
sigHash, anyoneCanPay);
|
||||
input.setScriptSig(ScriptBuilder.createEmpty());
|
||||
input.setWitness(TransactionWitness.redeemP2WPKH(signature, sigKey));
|
||||
} else {
|
||||
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2014 Giannis Dzegoutanis
|
||||
* Copyright 2019 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,6 +17,8 @@
|
|||
|
||||
package org.bitcoinj.core;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
import org.bitcoinj.wallet.WalletTransaction;
|
||||
|
@ -26,8 +29,13 @@ import java.util.Map;
|
|||
* This interface is used to abstract the {@link Wallet} and the {@link Transaction}
|
||||
*/
|
||||
public interface TransactionBag {
|
||||
/** Returns true if this wallet contains a public key which hashes to the given hash. */
|
||||
boolean isPubKeyHashMine(byte[] pubKeyHash);
|
||||
/**
|
||||
* Look for a public key which hashes to the given hash and (optionally) is used for a specific script type.
|
||||
* @param pubKeyHash hash of the public key to look for
|
||||
* @param scriptType only look for given usage (currently {@link Script.ScriptType#P2PKH} or {@link Script.ScriptType#P2WPKH}) or {@code null} if we don't care
|
||||
* @return true if hash was found
|
||||
*/
|
||||
boolean isPubKeyHashMine(byte[] pubKeyHash, @Nullable Script.ScriptType scriptType);
|
||||
|
||||
/** Returns true if this wallet is watching transactions for outputs with the script. */
|
||||
boolean isWatchedScript(Script script);
|
||||
|
|
|
@ -474,8 +474,8 @@ public class TransactionInput extends ChildMessage {
|
|||
throw new VerificationException("This input refers to a different output on the given tx.");
|
||||
}
|
||||
Script pubKey = output.getScriptPubKey();
|
||||
int myIndex = getParentTransaction().getInputs().indexOf(this);
|
||||
getScriptSig().correctlySpends(getParentTransaction(), getIndex(), pubKey);
|
||||
getScriptSig().correctlySpends(getParentTransaction(), getIndex(), getWitness(), getValue(), pubKey,
|
||||
Script.ALL_VERIFY_FLAGS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -129,7 +129,7 @@ public class TransactionOutPoint extends ChildMessage {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the ECKey identified in the connected output, for either P2PKH scripts or P2PK scripts.
|
||||
* Returns the ECKey identified in the connected output, for either P2PKH, P2WPKH or P2PK scripts.
|
||||
* For P2SH scripts you can use {@link #getConnectedRedeemData(KeyBag)} and then get the
|
||||
* key from RedeemData.
|
||||
* If the script form cannot be understood, throws ScriptException.
|
||||
|
@ -143,7 +143,10 @@ public class TransactionOutPoint extends ChildMessage {
|
|||
Script connectedScript = connectedOutput.getScriptPubKey();
|
||||
if (ScriptPattern.isPayToPubKeyHash(connectedScript)) {
|
||||
byte[] addressBytes = ScriptPattern.extractHashFromPayToPubKeyHash(connectedScript);
|
||||
return keyBag.findKeyFromPubKeyHash(addressBytes);
|
||||
return keyBag.findKeyFromPubKeyHash(addressBytes, Script.ScriptType.P2PKH);
|
||||
} else if (ScriptPattern.isPayToWitnessPubKeyHash(connectedScript)) {
|
||||
byte[] addressBytes = ScriptPattern.extractHashFromPayToWitnessHash(connectedScript);
|
||||
return keyBag.findKeyFromPubKeyHash(addressBytes, Script.ScriptType.P2WPKH);
|
||||
} else if (ScriptPattern.isPayToPubKey(connectedScript)) {
|
||||
byte[] pubkeyBytes = ScriptPattern.extractKeyFromPayToPubKey(connectedScript);
|
||||
return keyBag.findKeyFromPubKey(pubkeyBytes);
|
||||
|
@ -153,7 +156,7 @@ public class TransactionOutPoint extends ChildMessage {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the RedeemData identified in the connected output, for either P2PKH scripts, P2PK
|
||||
* Returns the RedeemData identified in the connected output, for either P2PKH, P2WPKH, P2PK
|
||||
* or P2SH scripts.
|
||||
* If the script forms cannot be understood, throws ScriptException.
|
||||
*
|
||||
|
@ -166,7 +169,10 @@ public class TransactionOutPoint extends ChildMessage {
|
|||
Script connectedScript = connectedOutput.getScriptPubKey();
|
||||
if (ScriptPattern.isPayToPubKeyHash(connectedScript)) {
|
||||
byte[] addressBytes = ScriptPattern.extractHashFromPayToPubKeyHash(connectedScript);
|
||||
return RedeemData.of(keyBag.findKeyFromPubKeyHash(addressBytes), connectedScript);
|
||||
return RedeemData.of(keyBag.findKeyFromPubKeyHash(addressBytes, Script.ScriptType.P2PKH), connectedScript);
|
||||
} else if (ScriptPattern.isPayToWitnessPubKeyHash(connectedScript)) {
|
||||
byte[] addressBytes = ScriptPattern.extractHashFromPayToWitnessHash(connectedScript);
|
||||
return RedeemData.of(keyBag.findKeyFromPubKeyHash(addressBytes, Script.ScriptType.P2WPKH), connectedScript);
|
||||
} else if (ScriptPattern.isPayToPubKey(connectedScript)) {
|
||||
byte[] pubkeyBytes = ScriptPattern.extractKeyFromPayToPubKey(connectedScript);
|
||||
return RedeemData.of(keyBag.findKeyFromPubKey(pubkeyBytes), connectedScript);
|
||||
|
|
|
@ -306,7 +306,11 @@ public class TransactionOutput extends ChildMessage {
|
|||
else if (ScriptPattern.isPayToScriptHash(script))
|
||||
return transactionBag.isPayToScriptHashMine(ScriptPattern.extractHashFromPayToScriptHash(script));
|
||||
else if (ScriptPattern.isPayToPubKeyHash(script))
|
||||
return transactionBag.isPubKeyHashMine(ScriptPattern.extractHashFromPayToPubKeyHash(script));
|
||||
return transactionBag.isPubKeyHashMine(ScriptPattern.extractHashFromPayToPubKeyHash(script),
|
||||
Script.ScriptType.P2PKH);
|
||||
else if (ScriptPattern.isPayToWitnessPubKeyHash(script))
|
||||
return transactionBag.isPubKeyHashMine(ScriptPattern.extractHashFromPayToWitnessHash(script),
|
||||
Script.ScriptType.P2WPKH);
|
||||
else
|
||||
return false;
|
||||
} catch (ScriptException e) {
|
||||
|
@ -325,7 +329,8 @@ public class TransactionOutput extends ChildMessage {
|
|||
Script script = getScriptPubKey();
|
||||
StringBuilder buf = new StringBuilder("TxOut of ");
|
||||
buf.append(Coin.valueOf(value).toFriendlyString());
|
||||
if (ScriptPattern.isPayToPubKeyHash(script) || ScriptPattern.isPayToScriptHash(script))
|
||||
if (ScriptPattern.isPayToPubKeyHash(script) || ScriptPattern.isPayToWitnessPubKeyHash(script)
|
||||
|| ScriptPattern.isPayToScriptHash(script))
|
||||
buf.append(" to ").append(script.getToAddress(params));
|
||||
else if (ScriptPattern.isPayToPubKey(script))
|
||||
buf.append(" to pubkey ").append(Utils.HEX.encode(ScriptPattern.extractKeyFromPayToPubKey(script)));
|
||||
|
|
|
@ -14,15 +14,33 @@
|
|||
|
||||
package org.bitcoinj.core;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bitcoinj.crypto.TransactionSignature;
|
||||
|
||||
public class TransactionWitness {
|
||||
public static final TransactionWitness EMPTY = new TransactionWitness(0);
|
||||
|
||||
/**
|
||||
* Creates the stack pushes necessary to redeem a P2WPKH output. If given signature is null, an empty push will be
|
||||
* used as a placeholder.
|
||||
*/
|
||||
public static TransactionWitness redeemP2WPKH(@Nullable TransactionSignature signature, ECKey pubKey) {
|
||||
checkArgument(pubKey.isCompressed(), "only compressed keys allowed");
|
||||
TransactionWitness witness = new TransactionWitness(2);
|
||||
witness.setPush(0, signature != null ? signature.encodeToBitcoin() : new byte[0]); // signature
|
||||
witness.setPush(1, pubKey.getPubKey()); // pubkey
|
||||
return witness;
|
||||
}
|
||||
|
||||
private final List<byte[]> pushes;
|
||||
|
||||
public TransactionWitness(int pushCount) {
|
||||
|
|
|
@ -37,8 +37,9 @@ public class ChildNumber implements Comparable<ChildNumber> {
|
|||
public static final int HARDENED_BIT = 0x80000000;
|
||||
|
||||
public static final ChildNumber ZERO = new ChildNumber(0);
|
||||
public static final ChildNumber ONE = new ChildNumber(1);
|
||||
public static final ChildNumber ZERO_HARDENED = new ChildNumber(0, true);
|
||||
public static final ChildNumber ONE = new ChildNumber(1);
|
||||
public static final ChildNumber ONE_HARDENED = new ChildNumber(1, true);
|
||||
|
||||
/** Integer i as per BIP 32 spec, including the MSB denoting derivation type (0 = public, 1 = private) **/
|
||||
private final int i;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package org.bitcoinj.crypto;
|
||||
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.script.Script;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
|
@ -460,17 +461,24 @@ public class DeterministicKey extends ECKey {
|
|||
return key;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public byte[] serializePublic(NetworkParameters params) {
|
||||
return serialize(params, true);
|
||||
return serialize(params, true, Script.ScriptType.P2PKH);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public byte[] serializePrivate(NetworkParameters params) {
|
||||
return serialize(params, false);
|
||||
return serialize(params, false, Script.ScriptType.P2PKH);
|
||||
}
|
||||
|
||||
private byte[] serialize(NetworkParameters params, boolean pub) {
|
||||
private byte[] serialize(NetworkParameters params, boolean pub, Script.ScriptType outputScriptType) {
|
||||
ByteBuffer ser = ByteBuffer.allocate(78);
|
||||
ser.putInt(pub ? params.getBip32HeaderPub() : params.getBip32HeaderPriv());
|
||||
if (outputScriptType == Script.ScriptType.P2PKH)
|
||||
ser.putInt(pub ? params.getBip32HeaderP2PKHpub() : params.getBip32HeaderP2PKHpriv());
|
||||
else if (outputScriptType == Script.ScriptType.P2WPKH)
|
||||
ser.putInt(pub ? params.getBip32HeaderP2WPKHpub() : params.getBip32HeaderP2WPKHpriv());
|
||||
else
|
||||
throw new IllegalStateException(outputScriptType.toString());
|
||||
ser.put((byte) getDepth());
|
||||
ser.putInt(getParentFingerprint());
|
||||
ser.putInt(getChildNumber().i());
|
||||
|
@ -480,12 +488,20 @@ public class DeterministicKey extends ECKey {
|
|||
return ser.array();
|
||||
}
|
||||
|
||||
public String serializePubB58(NetworkParameters params, Script.ScriptType outputScriptType) {
|
||||
return toBase58(serialize(params, true, outputScriptType));
|
||||
}
|
||||
|
||||
public String serializePrivB58(NetworkParameters params, Script.ScriptType outputScriptType) {
|
||||
return toBase58(serialize(params, false, outputScriptType));
|
||||
}
|
||||
|
||||
public String serializePubB58(NetworkParameters params) {
|
||||
return toBase58(serialize(params, true));
|
||||
return serializePubB58(params, Script.ScriptType.P2PKH);
|
||||
}
|
||||
|
||||
public String serializePrivB58(NetworkParameters params) {
|
||||
return toBase58(serialize(params, false));
|
||||
return serializePrivB58(params, Script.ScriptType.P2PKH);
|
||||
}
|
||||
|
||||
static String toBase58(byte[] ser) {
|
||||
|
@ -520,9 +536,10 @@ public class DeterministicKey extends ECKey {
|
|||
public static DeterministicKey deserialize(NetworkParameters params, byte[] serializedKey, @Nullable DeterministicKey parent) {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(serializedKey);
|
||||
int header = buffer.getInt();
|
||||
if (header != params.getBip32HeaderPriv() && header != params.getBip32HeaderPub())
|
||||
final boolean pub = header == params.getBip32HeaderP2PKHpub() || header == params.getBip32HeaderP2WPKHpub();
|
||||
final boolean priv = header == params.getBip32HeaderP2PKHpriv() || header == params.getBip32HeaderP2WPKHpriv();
|
||||
if (!(pub || priv))
|
||||
throw new IllegalArgumentException("Unknown header bytes: " + toBase58(serializedKey).substring(0, 4));
|
||||
boolean pub = header == params.getBip32HeaderPub();
|
||||
int depth = buffer.get() & 0xFF; // convert signed byte to positive int since depth cannot be negative
|
||||
final int parentFingerprint = buffer.getInt();
|
||||
final int i = buffer.getInt();
|
||||
|
@ -615,9 +632,8 @@ public class DeterministicKey extends ECKey {
|
|||
|
||||
@Override
|
||||
public void formatKeyWithAddress(boolean includePrivateKeys, @Nullable KeyParameter aesKey, StringBuilder builder,
|
||||
NetworkParameters params) {
|
||||
final Address address = LegacyAddress.fromKey(params, this);
|
||||
builder.append(" addr:").append(address);
|
||||
NetworkParameters params, Script.ScriptType outputScriptType) {
|
||||
builder.append(" addr:").append(Address.fromKey(params, this, outputScriptType).toString());
|
||||
builder.append(" hash160:").append(Utils.HEX.encode(getPubKeyHash()));
|
||||
builder.append(" (").append(getPathAsString()).append(")\n");
|
||||
if (includePrivateKeys) {
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.bitcoinj.core.*;
|
|||
import org.bitcoinj.crypto.DeterministicKey;
|
||||
import org.bitcoinj.net.discovery.*;
|
||||
import org.bitcoinj.protocols.channels.*;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.store.*;
|
||||
import org.bitcoinj.wallet.*;
|
||||
import org.slf4j.*;
|
||||
|
@ -63,8 +64,10 @@ import static com.google.common.base.Preconditions.*;
|
|||
public class WalletAppKit extends AbstractIdleService {
|
||||
protected static final Logger log = LoggerFactory.getLogger(WalletAppKit.class);
|
||||
|
||||
protected final String filePrefix;
|
||||
protected final NetworkParameters params;
|
||||
protected final Script.ScriptType preferredOutputScriptType;
|
||||
protected final KeyChainGroupStructure structure;
|
||||
protected final String filePrefix;
|
||||
protected volatile BlockChain vChain;
|
||||
protected volatile BlockStore vStore;
|
||||
protected volatile Wallet vWallet;
|
||||
|
@ -91,15 +94,26 @@ public class WalletAppKit extends AbstractIdleService {
|
|||
* Creates a new WalletAppKit, with a newly created {@link Context}. Files will be stored in the given directory.
|
||||
*/
|
||||
public WalletAppKit(NetworkParameters params, File directory, String filePrefix) {
|
||||
this(new Context(params), directory, filePrefix);
|
||||
this(new Context(params), Script.ScriptType.P2PKH, null, directory, filePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WalletAppKit, with a newly created {@link Context}. Files will be stored in the given directory.
|
||||
*/
|
||||
public WalletAppKit(NetworkParameters params, Script.ScriptType preferredOutputScriptType,
|
||||
@Nullable KeyChainGroupStructure structure, File directory, String filePrefix) {
|
||||
this(new Context(params), preferredOutputScriptType, structure, directory, filePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WalletAppKit, with the given {@link Context}. Files will be stored in the given directory.
|
||||
*/
|
||||
public WalletAppKit(Context context, File directory, String filePrefix) {
|
||||
public WalletAppKit(Context context, Script.ScriptType preferredOutputScriptType,
|
||||
@Nullable KeyChainGroupStructure structure, File directory, String filePrefix) {
|
||||
this.context = context;
|
||||
this.params = checkNotNull(context.getParams());
|
||||
this.preferredOutputScriptType = checkNotNull(preferredOutputScriptType);
|
||||
this.structure = structure != null ? structure : KeyChainGroupStructure.DEFAULT;
|
||||
this.directory = checkNotNull(directory);
|
||||
this.filePrefix = checkNotNull(filePrefix);
|
||||
}
|
||||
|
@ -434,13 +448,13 @@ public class WalletAppKit extends AbstractIdleService {
|
|||
}
|
||||
|
||||
protected Wallet createWallet() {
|
||||
KeyChainGroup.Builder kcg = KeyChainGroup.builder(params);
|
||||
KeyChainGroup.Builder kcg = KeyChainGroup.builder(params, structure);
|
||||
if (restoreFromSeed != null)
|
||||
kcg.addChain(DeterministicKeyChain.builder().seed(restoreFromSeed).build());
|
||||
kcg.addChain(DeterministicKeyChain.builder().seed(restoreFromSeed).outputScriptType(preferredOutputScriptType).build());
|
||||
else if (restoreFromKey != null)
|
||||
kcg.addChain(DeterministicKeyChain.builder().spend(restoreFromKey).build());
|
||||
kcg.addChain(DeterministicKeyChain.builder().spend(restoreFromKey).outputScriptType(preferredOutputScriptType).build());
|
||||
else
|
||||
kcg.fromRandom();
|
||||
kcg.fromRandom(preferredOutputScriptType);
|
||||
if (walletFactory != null) {
|
||||
return walletFactory.create(params, kcg.build());
|
||||
} else {
|
||||
|
|
|
@ -43,8 +43,10 @@ public class MainNetParams extends AbstractBitcoinNetParams {
|
|||
segwitAddressHrp = "bc";
|
||||
port = 8333;
|
||||
packetMagic = 0xf9beb4d9L;
|
||||
bip32HeaderPub = 0x0488B21E; //The 4 byte header that serializes in base58 to "xpub".
|
||||
bip32HeaderPriv = 0x0488ADE4; //The 4 byte header that serializes in base58 to "xprv"
|
||||
bip32HeaderP2PKHpub = 0x0488b21e; // The 4 byte header that serializes in base58 to "xpub".
|
||||
bip32HeaderP2PKHpriv = 0x0488ade4; // The 4 byte header that serializes in base58 to "xprv"
|
||||
bip32HeaderP2WPKHpub = 0x04b24746; // The 4 byte header that serializes in base58 to "zpub".
|
||||
bip32HeaderP2WPKHpriv = 0x04b2430c; // The 4 byte header that serializes in base58 to "zprv"
|
||||
|
||||
majorityEnforceBlockUpgrade = MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE;
|
||||
majorityRejectBlockOutdated = MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED;
|
||||
|
|
|
@ -45,8 +45,10 @@ public class RegTestParams extends AbstractBitcoinNetParams {
|
|||
checkState(genesisHash.equals("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"));
|
||||
dnsSeeds = null;
|
||||
addrSeeds = null;
|
||||
bip32HeaderPub = 0x043587CF;
|
||||
bip32HeaderPriv = 0x04358394;
|
||||
bip32HeaderP2PKHpub = 0x043587cf; // The 4 byte header that serializes in base58 to "tpub".
|
||||
bip32HeaderP2PKHpriv = 0x04358394; // The 4 byte header that serializes in base58 to "tprv"
|
||||
bip32HeaderP2WPKHpub = 0x045f1cf6; // The 4 byte header that serializes in base58 to "vpub".
|
||||
bip32HeaderP2WPKHpriv = 0x045f18bc; // The 4 byte header that serializes in base58 to "vprv"
|
||||
|
||||
// Difficulty adjustments are disabled for regtest.
|
||||
// By setting the block interval for difficulty adjustments to Integer.MAX_VALUE we make sure difficulty never
|
||||
|
|
|
@ -68,8 +68,10 @@ public class TestNet3Params extends AbstractBitcoinNetParams {
|
|||
"bitcoin-testnet.bloqseeds.net", // Bloq
|
||||
};
|
||||
addrSeeds = null;
|
||||
bip32HeaderPub = 0x043587CF;
|
||||
bip32HeaderPriv = 0x04358394;
|
||||
bip32HeaderP2PKHpub = 0x043587cf; // The 4 byte header that serializes in base58 to "tpub".
|
||||
bip32HeaderP2PKHpriv = 0x04358394; // The 4 byte header that serializes in base58 to "tprv"
|
||||
bip32HeaderP2WPKHpub = 0x045f1cf6; // The 4 byte header that serializes in base58 to "vpub".
|
||||
bip32HeaderP2WPKHpriv = 0x045f18bc; // The 4 byte header that serializes in base58 to "vprv"
|
||||
|
||||
majorityEnforceBlockUpgrade = TESTNET_MAJORITY_ENFORCE_BLOCK_UPGRADE;
|
||||
majorityRejectBlockOutdated = TESTNET_MAJORITY_REJECT_BLOCK_OUTDATED;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2019 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -47,8 +48,10 @@ public class UnitTestParams extends AbstractBitcoinNetParams {
|
|||
subsidyDecreaseBlockCount = 100;
|
||||
dnsSeeds = null;
|
||||
addrSeeds = null;
|
||||
bip32HeaderPub = 0x043587CF;
|
||||
bip32HeaderPriv = 0x04358394;
|
||||
bip32HeaderP2PKHpub = 0x043587cf; // The 4 byte header that serializes in base58 to "tpub".
|
||||
bip32HeaderP2PKHpriv = 0x04358394; // The 4 byte header that serializes in base58 to "tprv"
|
||||
bip32HeaderP2WPKHpub = 0x045f1cf6; // The 4 byte header that serializes in base58 to "vpub".
|
||||
bip32HeaderP2WPKHpriv = 0x045f18bc; // The 4 byte header that serializes in base58 to "vprv"
|
||||
|
||||
majorityEnforceBlockUpgrade = 3;
|
||||
majorityRejectBlockOutdated = 4;
|
||||
|
|
|
@ -387,6 +387,8 @@ public class Script {
|
|||
if (ScriptPattern.isPayToPubKeyHash(this)) {
|
||||
checkArgument(key != null, "Key required to create P2PKH input script");
|
||||
return ScriptBuilder.createInputScript(null, key);
|
||||
} else if (ScriptPattern.isPayToWitnessPubKeyHash(this)) {
|
||||
return ScriptBuilder.createEmpty();
|
||||
} else if (ScriptPattern.isPayToPubKey(this)) {
|
||||
return ScriptBuilder.createInputScript(null);
|
||||
} else if (ScriptPattern.isPayToScriptHash(this)) {
|
||||
|
@ -397,6 +399,18 @@ public class Script {
|
|||
}
|
||||
}
|
||||
|
||||
public TransactionWitness createEmptyWitness(ECKey key) {
|
||||
if (ScriptPattern.isPayToWitnessPubKeyHash(this)) {
|
||||
checkArgument(key != null, "Key required to create P2WPKH witness");
|
||||
return TransactionWitness.EMPTY;
|
||||
} else if (ScriptPattern.isPayToPubKey(this) || ScriptPattern.isPayToPubKeyHash(this)
|
||||
|| ScriptPattern.isPayToScriptHash(this)) {
|
||||
return null; // no witness
|
||||
} else {
|
||||
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Do not understand script type: " + this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the given scriptSig with the signature inserted in the given position.
|
||||
*/
|
||||
|
@ -607,6 +621,9 @@ public class Script {
|
|||
// scriptSig: <sig> <pubkey>
|
||||
int uncompressedPubKeySize = 65;
|
||||
return SIG_SIZE + (pubKey != null ? pubKey.getPubKey().length : uncompressedPubKeySize);
|
||||
} else if (ScriptPattern.isPayToWitnessPubKeyHash(this)) {
|
||||
// scriptSig is empty
|
||||
return 0;
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported script type");
|
||||
}
|
||||
|
@ -744,7 +761,7 @@ public class Script {
|
|||
/**
|
||||
* Exposes the script interpreter. Normally you should not use this directly, instead use
|
||||
* {@link TransactionInput#verify(TransactionOutput)} or
|
||||
* {@link Script#correctlySpends(Transaction, long, Script)}. This method
|
||||
* {@link Script#correctlySpends(Transaction, int, TransactionWitness, Coin, Script, Set)}. This method
|
||||
* is useful if you need more precise control or access to the final state of the stack. This interface is very
|
||||
* likely to change in future.
|
||||
*
|
||||
|
@ -764,7 +781,7 @@ public class Script {
|
|||
/**
|
||||
* Exposes the script interpreter. Normally you should not use this directly, instead use
|
||||
* {@link TransactionInput#verify(TransactionOutput)} or
|
||||
* {@link Script#correctlySpends(Transaction, long, Script)}. This method
|
||||
* {@link Script#correctlySpends(Transaction, int, TransactionWitness, Coin, Script, Set)}. This method
|
||||
* is useful if you need more precise control or access to the final state of the stack. This interface is very
|
||||
* likely to change in future.
|
||||
*/
|
||||
|
@ -1526,7 +1543,7 @@ public class Script {
|
|||
* Accessing txContainingThis from another thread while this method runs results in undefined behavior.
|
||||
* @param scriptSigIndex The index in txContainingThis of the scriptSig (note: NOT the index of the scriptPubKey).
|
||||
* @param scriptPubKey The connected scriptPubKey containing the conditions needed to claim the value.
|
||||
* @deprecated Use {@link #correctlySpends(Transaction, long, Script, Set)}
|
||||
* @deprecated Use {@link #correctlySpends(Transaction, int, TransactionWitness, Coin, Script, Set)}
|
||||
* instead so that verification flags do not change as new verification options
|
||||
* are added.
|
||||
*/
|
||||
|
@ -1542,8 +1559,43 @@ public class Script {
|
|||
* Accessing txContainingThis from another thread while this method runs results in undefined behavior.
|
||||
* @param scriptSigIndex The index in txContainingThis of the scriptSig (note: NOT the index of the scriptPubKey).
|
||||
* @param scriptPubKey The connected scriptPubKey containing the conditions needed to claim the value.
|
||||
* @param verifyFlags Each flag enables one validation rule. If in doubt, use {@link #correctlySpends(Transaction, long, Script)}
|
||||
* which sets all flags.
|
||||
* @param witness Transaction witness belonging to the transaction input containing this script. Needed for SegWit.
|
||||
* @param value Value of the output. Needed for SegWit scripts.
|
||||
* @param verifyFlags Each flag enables one validation rule.
|
||||
*/
|
||||
public void correctlySpends(Transaction txContainingThis, int scriptSigIndex, @Nullable TransactionWitness witness, @Nullable Coin value,
|
||||
Script scriptPubKey, Set<VerifyFlag> verifyFlags) throws ScriptException {
|
||||
if (ScriptPattern.isPayToWitnessPubKeyHash(scriptPubKey)) {
|
||||
// For SegWit, full validation isn't implemented. So we simply check the signature. P2SH_P2WPKH is handled
|
||||
// by the P2SH code for now.
|
||||
if (witness.getPushCount() < 2)
|
||||
throw new ScriptException(ScriptError.SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY, witness.toString());
|
||||
TransactionSignature signature;
|
||||
try {
|
||||
signature = TransactionSignature.decodeFromBitcoin(witness.getPush(0), true, true);
|
||||
} catch (SignatureDecodeException x) {
|
||||
throw new ScriptException(ScriptError.SCRIPT_ERR_SIG_DER, "Cannot decode", x);
|
||||
}
|
||||
ECKey pubkey = ECKey.fromPublicOnly(witness.getPush(1));
|
||||
Script scriptCode = new ScriptBuilder().data(ScriptBuilder.createP2PKHOutputScript(pubkey).getProgram())
|
||||
.build();
|
||||
Sha256Hash sigHash = txContainingThis.hashForSignatureWitness(scriptSigIndex, scriptCode, value,
|
||||
signature.sigHashMode(), false);
|
||||
boolean validSig = pubkey.verify(sigHash, signature);
|
||||
if (!validSig)
|
||||
throw new ScriptException(ScriptError.SCRIPT_ERR_CHECKSIGVERIFY, "Invalid signature");
|
||||
} else {
|
||||
correctlySpends(txContainingThis, scriptSigIndex, scriptPubKey, verifyFlags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that this script (interpreted as a scriptSig) correctly spends the given scriptPubKey.
|
||||
* @param txContainingThis The transaction in which this input scriptSig resides.
|
||||
* Accessing txContainingThis from another thread while this method runs results in undefined behavior.
|
||||
* @param scriptSigIndex The index in txContainingThis of the scriptSig (note: NOT the index of the scriptPubKey).
|
||||
* @param scriptPubKey The connected scriptPubKey containing the conditions needed to claim the value.
|
||||
* @param verifyFlags Each flag enables one validation rule.
|
||||
*/
|
||||
public void correctlySpends(Transaction txContainingThis, long scriptSigIndex, Script scriptPubKey,
|
||||
Set<VerifyFlag> verifyFlags) throws ScriptException {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2018 Nicola Atzei
|
||||
* Copyright 2019 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -253,6 +254,11 @@ public class ScriptBuilder {
|
|||
return new Script(chunks);
|
||||
}
|
||||
|
||||
/** Creates an empty script. */
|
||||
public static Script createEmpty() {
|
||||
return new ScriptBuilder().build();
|
||||
}
|
||||
|
||||
/** Creates a scriptPubKey that encodes payment to the given address. */
|
||||
public static Script createOutputScript(Address to) {
|
||||
if (to instanceof LegacyAddress) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2014 Kosta Korenkov
|
||||
* Copyright 2019 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -70,7 +71,8 @@ public abstract class CustomTransactionSigner implements TransactionSigner {
|
|||
// We assume if its already signed, its hopefully got a SIGHASH type that will not invalidate when
|
||||
// we sign missing pieces (to check this would require either assuming any signatures are signing
|
||||
// standard output types or a way to get processed signatures out of script execution)
|
||||
txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey());
|
||||
txIn.getScriptSig().correctlySpends(tx, i, txIn.getWitness(), txOut.getValue(), txOut.getScriptPubKey(),
|
||||
Script.ALL_VERIFY_FLAGS);
|
||||
log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", i);
|
||||
continue;
|
||||
} catch (ScriptException e) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2014 Kosta Korenkov
|
||||
* Copyright 2019 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -17,13 +18,20 @@
|
|||
package org.bitcoinj.signers;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.LegacyAddress;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionInput;
|
||||
import org.bitcoinj.core.TransactionOutput;
|
||||
import org.bitcoinj.core.TransactionWitness;
|
||||
import org.bitcoinj.crypto.DeterministicKey;
|
||||
import org.bitcoinj.crypto.TransactionSignature;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.ScriptBuilder;
|
||||
import org.bitcoinj.script.ScriptException;
|
||||
import org.bitcoinj.script.ScriptPattern;
|
||||
import org.bitcoinj.script.Script.VerifyFlag;
|
||||
import org.bitcoinj.wallet.KeyBag;
|
||||
import org.bitcoinj.wallet.RedeemData;
|
||||
|
@ -63,16 +71,19 @@ public class LocalTransactionSigner implements TransactionSigner {
|
|||
int numInputs = tx.getInputs().size();
|
||||
for (int i = 0; i < numInputs; i++) {
|
||||
TransactionInput txIn = tx.getInput(i);
|
||||
if (txIn.getConnectedOutput() == null) {
|
||||
final TransactionOutput connectedOutput = txIn.getConnectedOutput();
|
||||
if (connectedOutput == null) {
|
||||
log.warn("Missing connected output, assuming input {} is already signed.", i);
|
||||
continue;
|
||||
}
|
||||
Script scriptPubKey = connectedOutput.getScriptPubKey();
|
||||
|
||||
try {
|
||||
// We assume if its already signed, its hopefully got a SIGHASH type that will not invalidate when
|
||||
// we sign missing pieces (to check this would require either assuming any signatures are signing
|
||||
// standard output types or a way to get processed signatures out of script execution)
|
||||
txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey(), MINIMUM_VERIFY_FLAGS);
|
||||
txIn.getScriptSig().correctlySpends(tx, i, txIn.getWitness(), connectedOutput.getValue(),
|
||||
connectedOutput.getScriptPubKey(), MINIMUM_VERIFY_FLAGS);
|
||||
log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", i);
|
||||
continue;
|
||||
} catch (ScriptException e) {
|
||||
|
@ -81,8 +92,6 @@ public class LocalTransactionSigner implements TransactionSigner {
|
|||
|
||||
RedeemData redeemData = txIn.getConnectedRedeemData(keyBag);
|
||||
|
||||
Script scriptPubKey = txIn.getConnectedOutput().getScriptPubKey();
|
||||
|
||||
// For P2SH inputs we need to share derivation path of the signing key with other signers, so that they
|
||||
// use correct key to calculate their signatures.
|
||||
// Married keys all have the same derivation path, so we can safely just take first one here.
|
||||
|
@ -104,18 +113,35 @@ public class LocalTransactionSigner implements TransactionSigner {
|
|||
// a CHECKMULTISIG program for P2SH inputs
|
||||
byte[] script = redeemData.redeemScript.getProgram();
|
||||
try {
|
||||
TransactionSignature signature = tx.calculateSignature(i, key, script, Transaction.SigHash.ALL, false);
|
||||
if (ScriptPattern.isPayToPubKey(scriptPubKey) || ScriptPattern.isPayToPubKeyHash(scriptPubKey)
|
||||
|| ScriptPattern.isPayToScriptHash(scriptPubKey)) {
|
||||
TransactionSignature signature = tx.calculateSignature(i, key, script, Transaction.SigHash.ALL,
|
||||
false);
|
||||
|
||||
// at this point we have incomplete inputScript with OP_0 in place of one or more signatures. We already
|
||||
// have calculated the signature using the local key and now need to insert it in the correct place
|
||||
// within inputScript. For P2PKH and P2PK script there is only one signature and it always
|
||||
// goes first in an inputScript (sigIndex = 0). In P2SH input scripts we need to figure out our relative
|
||||
// position relative to other signers. Since we don't have that information at this point, and since
|
||||
// we always run first, we have to depend on the other signers rearranging the signatures as needed.
|
||||
// Therefore, always place as first signature.
|
||||
int sigIndex = 0;
|
||||
inputScript = scriptPubKey.getScriptSigWithSignature(inputScript, signature.encodeToBitcoin(), sigIndex);
|
||||
txIn.setScriptSig(inputScript);
|
||||
// at this point we have incomplete inputScript with OP_0 in place of one or more signatures. We
|
||||
// already have calculated the signature using the local key and now need to insert it in the
|
||||
// correct place within inputScript. For P2PKH and P2PK script there is only one signature and it
|
||||
// always goes first in an inputScript (sigIndex = 0). In P2SH input scripts we need to figure out
|
||||
// our relative position relative to other signers. Since we don't have that information at this
|
||||
// point, and since we always run first, we have to depend on the other signers rearranging the
|
||||
// signatures as needed. Therefore, always place as first signature.
|
||||
int sigIndex = 0;
|
||||
inputScript = scriptPubKey.getScriptSigWithSignature(inputScript, signature.encodeToBitcoin(),
|
||||
sigIndex);
|
||||
txIn.setScriptSig(inputScript);
|
||||
txIn.setWitness(null);
|
||||
} else if (ScriptPattern.isPayToWitnessPubKeyHash(scriptPubKey)) {
|
||||
Script scriptCode = new ScriptBuilder().data(
|
||||
ScriptBuilder.createOutputScript(LegacyAddress.fromKey(tx.getParams(), key)).getProgram())
|
||||
.build();
|
||||
Coin value = txIn.getValue();
|
||||
TransactionSignature signature = tx.calculateWitnessSignature(i, key, scriptCode, value,
|
||||
Transaction.SigHash.ALL, false);
|
||||
txIn.setScriptSig(ScriptBuilder.createEmpty());
|
||||
txIn.setWitness(TransactionWitness.redeemP2WPKH(signature, key));
|
||||
} else {
|
||||
throw new IllegalStateException(script.toString());
|
||||
}
|
||||
} catch (ECKey.KeyIsEncryptedException e) {
|
||||
throw e;
|
||||
} catch (ECKey.MissingPrivateKeyException e) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2014 Kosta Korenkov
|
||||
* Copyright 2019 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,6 +19,7 @@ package org.bitcoinj.signers;
|
|||
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.TransactionInput;
|
||||
import org.bitcoinj.core.TransactionWitness;
|
||||
import org.bitcoinj.crypto.TransactionSignature;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.ScriptChunk;
|
||||
|
@ -80,7 +82,7 @@ public class MissingSigResolutionSigner implements TransactionSigner {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (ScriptPattern.isPayToPubKey(scriptPubKey) || ScriptPattern.isPayToPubKeyHash(scriptPubKey)) {
|
||||
if (inputScript.getChunks().get(0).equalsOpCode(0)) {
|
||||
if (missingSigsMode == Wallet.MissingSigsMode.THROW) {
|
||||
throw new ECKey.MissingPrivateKeyException();
|
||||
|
@ -88,8 +90,20 @@ public class MissingSigResolutionSigner implements TransactionSigner {
|
|||
txIn.setScriptSig(scriptPubKey.getScriptSigWithSignature(inputScript, dummySig, 0));
|
||||
}
|
||||
}
|
||||
} else if (ScriptPattern.isPayToWitnessPubKeyHash(scriptPubKey)) {
|
||||
if (txIn.getWitness() == null || txIn.getWitness().equals(TransactionWitness.EMPTY)
|
||||
|| txIn.getWitness().getPush(0).length == 0) {
|
||||
if (missingSigsMode == Wallet.MissingSigsMode.THROW) {
|
||||
throw new ECKey.MissingPrivateKeyException();
|
||||
} else if (missingSigsMode == Wallet.MissingSigsMode.USE_DUMMY_SIG) {
|
||||
ECKey key = keyBag.findKeyFromPubKeyHash(
|
||||
ScriptPattern.extractHashFromPayToWitnessHash(scriptPubKey), Script.ScriptType.P2WPKH);
|
||||
txIn.setWitness(TransactionWitness.redeemP2WPKH(TransactionSignature.dummy(), key));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("cannot handle: " + scriptPubKey);
|
||||
}
|
||||
// TODO handle non-P2SH multisig
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2019 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -204,19 +205,19 @@ public class BasicKeyChain implements EncryptableKeyChain {
|
|||
}
|
||||
}
|
||||
|
||||
public ECKey findKeyFromPubHash(byte[] pubkeyHash) {
|
||||
public ECKey findKeyFromPubHash(byte[] pubKeyHash) {
|
||||
lock.lock();
|
||||
try {
|
||||
return hashToKeys.get(ByteString.copyFrom(pubkeyHash));
|
||||
return hashToKeys.get(ByteString.copyFrom(pubKeyHash));
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public ECKey findKeyFromPubKey(byte[] pubkey) {
|
||||
public ECKey findKeyFromPubKey(byte[] pubKey) {
|
||||
lock.lock();
|
||||
try {
|
||||
return pubkeyToKeys.get(ByteString.copyFrom(pubkey));
|
||||
return pubkeyToKeys.get(ByteString.copyFrom(pubKey));
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
@ -625,7 +626,7 @@ public class BasicKeyChain implements EncryptableKeyChain {
|
|||
List<ECKey> keys = getKeys();
|
||||
Collections.sort(keys, ECKey.AGE_COMPARATOR);
|
||||
for (ECKey key : keys)
|
||||
key.formatKeyWithAddress(includePrivateKeys, aesKey, builder, params);
|
||||
key.formatKeyWithAddress(includePrivateKeys, aesKey, builder, params, null);
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.bitcoinj.wallet;
|
||||
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -63,8 +64,8 @@ public class DecryptingKeyBag implements KeyBag {
|
|||
|
||||
@Nullable
|
||||
@Override
|
||||
public ECKey findKeyFromPubKeyHash(byte[] pubKeyHash) {
|
||||
return maybeDecrypt(target.findKeyFromPubKeyHash(pubKeyHash));
|
||||
public ECKey findKeyFromPubKeyHash(byte[] pubKeyHash, @Nullable Script.ScriptType scriptType) {
|
||||
return maybeDecrypt(target.findKeyFromPubKeyHash(pubKeyHash, scriptType));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2014 devrandom
|
||||
* Copyright 2019 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -17,6 +18,7 @@
|
|||
package org.bitcoinj.wallet;
|
||||
|
||||
import org.bitcoinj.crypto.*;
|
||||
import org.bitcoinj.script.Script;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
|
@ -26,36 +28,39 @@ import com.google.common.collect.ImmutableList;
|
|||
public class DefaultKeyChainFactory implements KeyChainFactory {
|
||||
@Override
|
||||
public DeterministicKeyChain makeKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicSeed seed,
|
||||
KeyCrypter crypter, boolean isMarried, ImmutableList<ChildNumber> accountPath) {
|
||||
KeyCrypter crypter, boolean isMarried, Script.ScriptType outputScriptType,
|
||||
ImmutableList<ChildNumber> accountPath) {
|
||||
DeterministicKeyChain chain;
|
||||
if (isMarried)
|
||||
chain = new MarriedKeyChain(seed, crypter, accountPath);
|
||||
chain = new MarriedKeyChain(seed, crypter, outputScriptType, accountPath);
|
||||
else
|
||||
chain = new DeterministicKeyChain(seed, crypter, accountPath);
|
||||
chain = new DeterministicKeyChain(seed, crypter, outputScriptType, accountPath);
|
||||
return chain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeterministicKeyChain makeWatchingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey,
|
||||
boolean isFollowingKey, boolean isMarried) throws UnreadableWalletException {
|
||||
public DeterministicKeyChain makeWatchingKeyChain(Protos.Key key, Protos.Key firstSubKey,
|
||||
DeterministicKey accountKey, boolean isFollowingKey, boolean isMarried, Script.ScriptType outputScriptType)
|
||||
throws UnreadableWalletException {
|
||||
DeterministicKeyChain chain;
|
||||
if (isMarried)
|
||||
chain = new MarriedKeyChain(accountKey);
|
||||
chain = new MarriedKeyChain(accountKey, outputScriptType);
|
||||
else if (isFollowingKey)
|
||||
chain = DeterministicKeyChain.builder().watchAndFollow(accountKey).build();
|
||||
chain = DeterministicKeyChain.builder().watchAndFollow(accountKey).outputScriptType(outputScriptType).build();
|
||||
else
|
||||
chain = DeterministicKeyChain.builder().watch(accountKey).build();
|
||||
chain = DeterministicKeyChain.builder().watch(accountKey).outputScriptType(outputScriptType).build();
|
||||
return chain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeterministicKeyChain makeSpendingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey,
|
||||
boolean isMarried) throws UnreadableWalletException {
|
||||
public DeterministicKeyChain makeSpendingKeyChain(Protos.Key key, Protos.Key firstSubKey,
|
||||
DeterministicKey accountKey, boolean isMarried, Script.ScriptType outputScriptType)
|
||||
throws UnreadableWalletException {
|
||||
DeterministicKeyChain chain;
|
||||
if (isMarried)
|
||||
chain = new MarriedKeyChain(accountKey);
|
||||
chain = new MarriedKeyChain(accountKey, outputScriptType);
|
||||
else
|
||||
chain = DeterministicKeyChain.builder().spend(accountKey).build();
|
||||
chain = DeterministicKeyChain.builder().spend(accountKey).outputScriptType(outputScriptType).build();
|
||||
return chain;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,7 +105,8 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
private DeterministicHierarchy hierarchy;
|
||||
@Nullable private DeterministicKey rootKey;
|
||||
@Nullable private DeterministicSeed seed;
|
||||
@Nullable private final ImmutableList<ChildNumber> accountPath;
|
||||
private final Script.ScriptType outputScriptType;
|
||||
private final ImmutableList<ChildNumber> accountPath;
|
||||
|
||||
// Paths through the key tree. External keys are ones that are communicated to other parties. Internal keys are
|
||||
// keys created for change addresses, coinbases, mixing, etc - anything that isn't communicated. The distinction
|
||||
|
@ -113,7 +114,10 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
// that feature yet. In future we might hand out different accounts for cases where we wish to hand payers
|
||||
// a payment request that can generate lots of addresses independently.
|
||||
// The account path may be overridden by subclasses.
|
||||
// m / 0'
|
||||
public static final ImmutableList<ChildNumber> ACCOUNT_ZERO_PATH = ImmutableList.of(ChildNumber.ZERO_HARDENED);
|
||||
// m / 1'
|
||||
public static final ImmutableList<ChildNumber> ACCOUNT_ONE_PATH = ImmutableList.of(ChildNumber.ONE_HARDENED);
|
||||
// m / 44' / 0' / 0'
|
||||
public static final ImmutableList<ChildNumber> BIP44_ACCOUNT_ZERO_PATH = ImmutableList.of(new ChildNumber(44, true),
|
||||
ChildNumber.ZERO_HARDENED, ChildNumber.ZERO_HARDENED);
|
||||
|
@ -167,6 +171,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
protected long creationTimeSecs = 0;
|
||||
protected byte[] entropy;
|
||||
protected DeterministicSeed seed;
|
||||
protected Script.ScriptType outputScriptType = Script.ScriptType.P2PKH;
|
||||
protected DeterministicKey watchingKey = null;
|
||||
protected boolean isFollowing = false;
|
||||
protected DeterministicKey spendingKey = null;
|
||||
|
@ -255,6 +260,11 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
return self();
|
||||
}
|
||||
|
||||
public T outputScriptType(Script.ScriptType outputScriptType) {
|
||||
this.outputScriptType = outputScriptType;
|
||||
return self();
|
||||
}
|
||||
|
||||
/** The passphrase to use with the generated mnemonic, or null if you would like to use the default empty string. Currently must be the empty string. */
|
||||
public T passphrase(String passphrase) {
|
||||
// FIXME support non-empty passphrase
|
||||
|
@ -267,7 +277,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
*/
|
||||
public T accountPath(ImmutableList<ChildNumber> accountPath) {
|
||||
checkState(watchingKey == null, "either watch or accountPath");
|
||||
this.accountPath = accountPath;
|
||||
this.accountPath = checkNotNull(accountPath);
|
||||
return self();
|
||||
}
|
||||
|
||||
|
@ -280,16 +290,16 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
if (random != null)
|
||||
// Default passphrase to "" if not specified
|
||||
return new DeterministicKeyChain(new DeterministicSeed(random, bits, getPassphrase()), null,
|
||||
accountPath);
|
||||
outputScriptType, accountPath);
|
||||
else if (entropy != null)
|
||||
return new DeterministicKeyChain(new DeterministicSeed(entropy, getPassphrase(), creationTimeSecs),
|
||||
null, accountPath);
|
||||
null, outputScriptType, accountPath);
|
||||
else if (seed != null)
|
||||
return new DeterministicKeyChain(seed, null, accountPath);
|
||||
return new DeterministicKeyChain(seed, null, outputScriptType, accountPath);
|
||||
else if (watchingKey != null)
|
||||
return new DeterministicKeyChain(watchingKey, isFollowing, true);
|
||||
return new DeterministicKeyChain(watchingKey, isFollowing, true, outputScriptType);
|
||||
else if (spendingKey != null)
|
||||
return new DeterministicKeyChain(spendingKey, false, false);
|
||||
return new DeterministicKeyChain(spendingKey, false, false, outputScriptType);
|
||||
else
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
@ -318,7 +328,8 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
* {@link Builder}.
|
||||
* </p>
|
||||
*/
|
||||
public DeterministicKeyChain(DeterministicKey key, boolean isFollowing, boolean isWatching) {
|
||||
public DeterministicKeyChain(DeterministicKey key, boolean isFollowing, boolean isWatching,
|
||||
Script.ScriptType outputScriptType) {
|
||||
if (isWatching)
|
||||
checkArgument(key.isPubKeyOnly(), "Private subtrees not currently supported for watching keys: if you got this key from DKC.getWatchingKey() then use .dropPrivate().dropParent() on it first.");
|
||||
else
|
||||
|
@ -331,6 +342,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
basicKeyChain.importKey(key);
|
||||
hierarchy = new DeterministicHierarchy(key);
|
||||
this.accountPath = key.getPath();
|
||||
this.outputScriptType = outputScriptType;
|
||||
initializeHierarchyUnencrypted(key);
|
||||
this.isFollowing = isFollowing;
|
||||
}
|
||||
|
@ -347,7 +359,10 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
* </p>
|
||||
*/
|
||||
protected DeterministicKeyChain(DeterministicSeed seed, @Nullable KeyCrypter crypter,
|
||||
ImmutableList<ChildNumber> accountPath) {
|
||||
Script.ScriptType outputScriptType, ImmutableList<ChildNumber> accountPath) {
|
||||
checkArgument(outputScriptType == null || outputScriptType == Script.ScriptType.P2PKH
|
||||
|| outputScriptType == Script.ScriptType.P2WPKH, "Only P2PKH or P2WPKH allowed.");
|
||||
this.outputScriptType = outputScriptType != null ? outputScriptType : Script.ScriptType.P2PKH;
|
||||
this.accountPath = accountPath;
|
||||
this.seed = seed;
|
||||
basicKeyChain = new BasicKeyChain(crypter);
|
||||
|
@ -370,7 +385,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
* For use in encryption when {@link #toEncrypted(KeyCrypter, KeyParameter)} is called, so that
|
||||
* subclasses can override that method and create an instance of the right class.
|
||||
*
|
||||
* See also {@link #makeKeyChainFromSeed(DeterministicSeed, ImmutableList)}
|
||||
* See also {@link #makeKeyChainFromSeed(DeterministicSeed, ImmutableList, Script.ScriptType)}
|
||||
*/
|
||||
protected DeterministicKeyChain(KeyCrypter crypter, KeyParameter aesKey, DeterministicKeyChain chain) {
|
||||
// Can't encrypt a watching chain.
|
||||
|
@ -379,6 +394,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
|
||||
checkArgument(!chain.rootKey.isEncrypted(), "Chain already encrypted");
|
||||
this.accountPath = chain.getAccountPath();
|
||||
this.outputScriptType = chain.outputScriptType;
|
||||
|
||||
this.issuedExternalKeys = chain.issuedExternalKeys;
|
||||
this.issuedInternalKeys = chain.issuedInternalKeys;
|
||||
|
@ -413,11 +429,12 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
}
|
||||
}
|
||||
|
||||
/** Override in subclasses to use a different account derivation path */
|
||||
protected ImmutableList<ChildNumber> getAccountPath() {
|
||||
if (accountPath != null)
|
||||
return accountPath;
|
||||
return ACCOUNT_ZERO_PATH;
|
||||
public ImmutableList<ChildNumber> getAccountPath() {
|
||||
return accountPath;
|
||||
}
|
||||
|
||||
public Script.ScriptType getOutputScriptType() {
|
||||
return outputScriptType;
|
||||
}
|
||||
|
||||
private DeterministicKey encryptNonLeaf(KeyParameter aesKey, DeterministicKeyChain chain,
|
||||
|
@ -755,6 +772,8 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
if (key.getParent() != null) {
|
||||
// HD keys inherit the timestamp of their parent if they have one, so no need to serialize it.
|
||||
proto.clearCreationTimestamp();
|
||||
} else {
|
||||
proto.setOutputScriptType(Protos.Key.OutputScriptType.valueOf(outputScriptType.name()));
|
||||
}
|
||||
entries.add(proto.build());
|
||||
}
|
||||
|
@ -778,6 +797,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
int sigsRequiredToSpend = 1;
|
||||
|
||||
List<ChildNumber> accountPath = newArrayList();
|
||||
Script.ScriptType outputScriptType = Script.ScriptType.P2PKH;
|
||||
PeekingIterator<Protos.Key> iter = Iterators.peekingIterator(keys.iterator());
|
||||
while (iter.hasNext()) {
|
||||
Protos.Key key = iter.next();
|
||||
|
@ -835,6 +855,8 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
// Deserialize the public key and path.
|
||||
LazyECPoint pubkey = new LazyECPoint(ECKey.CURVE.getCurve(), key.getPublicKey().toByteArray());
|
||||
final ImmutableList<ChildNumber> immutablePath = ImmutableList.copyOf(path);
|
||||
if (key.hasOutputScriptType())
|
||||
outputScriptType = Script.ScriptType.valueOf(key.getOutputScriptType().name());
|
||||
// Possibly create the chain, if we didn't already do so yet.
|
||||
boolean isWatchingAccountKey = false;
|
||||
boolean isFollowingKey = false;
|
||||
|
@ -860,16 +882,17 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
if (seed == null && key.hasSecretBytes()) {
|
||||
DeterministicKey accountKey = new DeterministicKey(immutablePath, chainCode, pubkey, new BigInteger(1, key.getSecretBytes().toByteArray()), null);
|
||||
accountKey.setCreationTimeSeconds(key.getCreationTimestamp() / 1000);
|
||||
chain = factory.makeSpendingKeyChain(key, iter.peek(), accountKey, isMarried);
|
||||
chain = factory.makeSpendingKeyChain(key, iter.peek(), accountKey, isMarried, outputScriptType);
|
||||
isSpendingKey = true;
|
||||
} else if (seed == null) {
|
||||
DeterministicKey accountKey = new DeterministicKey(immutablePath, chainCode, pubkey, null, null);
|
||||
accountKey.setCreationTimeSeconds(key.getCreationTimestamp() / 1000);
|
||||
chain = factory.makeWatchingKeyChain(key, iter.peek(), accountKey, isFollowingKey, isMarried);
|
||||
chain = factory.makeWatchingKeyChain(key, iter.peek(), accountKey, isFollowingKey, isMarried,
|
||||
outputScriptType);
|
||||
isWatchingAccountKey = true;
|
||||
} else {
|
||||
chain = factory.makeKeyChain(key, iter.peek(), seed, crypter, isMarried,
|
||||
ImmutableList.<ChildNumber> builder().addAll(accountPath).build());
|
||||
outputScriptType, ImmutableList.<ChildNumber> builder().addAll(accountPath).build());
|
||||
chain.lookaheadSize = LAZY_CALCULATE_LOOKAHEAD;
|
||||
// If the seed is encrypted, then the chain is incomplete at this point. However, we will load
|
||||
// it up below as we parse in the keys. We just need to check at the end that we've loaded
|
||||
|
@ -987,7 +1010,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
checkState(seed.isEncrypted());
|
||||
String passphrase = DEFAULT_PASSPHRASE_FOR_MNEMONIC; // FIXME allow non-empty passphrase
|
||||
DeterministicSeed decSeed = seed.decrypt(getKeyCrypter(), passphrase, aesKey);
|
||||
DeterministicKeyChain chain = makeKeyChainFromSeed(decSeed, getAccountPath());
|
||||
DeterministicKeyChain chain = makeKeyChainFromSeed(decSeed, getAccountPath(), outputScriptType);
|
||||
// Now double check that the keys match to catch the case where the key is wrong but padding didn't catch it.
|
||||
if (!chain.getWatchingKey().getPubKeyPoint().equals(getWatchingKey().getPubKeyPoint()))
|
||||
throw new KeyCrypterException("Provided AES key is wrong");
|
||||
|
@ -1014,8 +1037,9 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
* Subclasses should override this to create an instance of the subclass instead of a plain DKC.
|
||||
* This is used in encryption/decryption.
|
||||
*/
|
||||
protected DeterministicKeyChain makeKeyChainFromSeed(DeterministicSeed seed, ImmutableList<ChildNumber> accountPath) {
|
||||
return new DeterministicKeyChain(seed, null, accountPath);
|
||||
protected DeterministicKeyChain makeKeyChainFromSeed(DeterministicSeed seed, ImmutableList<ChildNumber> accountPath,
|
||||
Script.ScriptType outputScriptType) {
|
||||
return new DeterministicKeyChain(seed, null, outputScriptType, accountPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1337,21 +1361,24 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
if (seed != null) {
|
||||
if (includePrivateKeys) {
|
||||
DeterministicSeed decryptedSeed = seed.isEncrypted()
|
||||
? seed.decrypt(getKeyCrypter(), DEFAULT_PASSPHRASE_FOR_MNEMONIC, aesKey) : seed;
|
||||
? seed.decrypt(getKeyCrypter(), DEFAULT_PASSPHRASE_FOR_MNEMONIC, aesKey)
|
||||
: seed;
|
||||
final List<String> words = decryptedSeed.getMnemonicCode();
|
||||
builder.append("Seed as words: ").append(Utils.SPACE_JOINER.join(words)).append('\n');
|
||||
builder.append("Seed as hex: ").append(decryptedSeed.toHexString()).append('\n');
|
||||
builder.append("Seed as words: ").append(Utils.SPACE_JOINER.join(words)).append('\n');
|
||||
builder.append("Seed as hex: ").append(decryptedSeed.toHexString()).append('\n');
|
||||
} else {
|
||||
if (seed.isEncrypted())
|
||||
builder.append("Seed is encrypted\n");
|
||||
}
|
||||
builder.append("Seed birthday: ").append(seed.getCreationTimeSeconds()).append(" [")
|
||||
builder.append("Seed birthday: ").append(seed.getCreationTimeSeconds()).append(" [")
|
||||
.append(Utils.dateTimeFormat(seed.getCreationTimeSeconds() * 1000)).append("]\n");
|
||||
} else {
|
||||
builder.append("Key birthday: ").append(watchingKey.getCreationTimeSeconds()).append(" [")
|
||||
builder.append("Key birthday: ").append(watchingKey.getCreationTimeSeconds()).append(" [")
|
||||
.append(Utils.dateTimeFormat(watchingKey.getCreationTimeSeconds() * 1000)).append("]\n");
|
||||
}
|
||||
builder.append("Key to watch: ").append(watchingKey.serializePubB58(params)).append('\n');
|
||||
builder.append("Ouput script type: ").append(outputScriptType).append('\n');
|
||||
builder.append("Key to watch: ").append(watchingKey.serializePubB58(params, outputScriptType))
|
||||
.append('\n');
|
||||
formatAddresses(includePrivateKeys, aesKey, params, builder);
|
||||
return builder.toString();
|
||||
}
|
||||
|
@ -1359,7 +1386,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
protected void formatAddresses(boolean includePrivateKeys, @Nullable KeyParameter aesKey, NetworkParameters params,
|
||||
StringBuilder builder) {
|
||||
for (ECKey key : getKeys(false, true))
|
||||
key.formatKeyWithAddress(includePrivateKeys, aesKey, builder, params);
|
||||
key.formatKeyWithAddress(includePrivateKeys, aesKey, builder, params, outputScriptType);
|
||||
}
|
||||
|
||||
/** The number of signatures required to spend coins received by this keychain. */
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.bitcoinj.wallet;
|
||||
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.script.Script;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
@ -26,13 +27,18 @@ import javax.annotation.Nullable;
|
|||
*/
|
||||
public interface KeyBag {
|
||||
/**
|
||||
* Locates a keypair from the keychain given the hash of the public key. This is needed when finding out which
|
||||
* key we need to use to redeem a transaction output.
|
||||
* Locates a keypair from the keychain given the hash of the public key, and (optionally) by usage for a specific
|
||||
* script type. This is needed when finding out which key we need to use to redeem a transaction output.
|
||||
*
|
||||
* @return ECKey object or null if no such key was found.
|
||||
* @param pubKeyHash
|
||||
* hash of the keypair to look for
|
||||
* @param scriptType
|
||||
* only look for given usage (currently {@link Script.ScriptType#P2PKH} or
|
||||
* {@link Script.ScriptType#P2WPKH}) or {@code null} if we don't care
|
||||
* @return found key or null if no such key was found.
|
||||
*/
|
||||
@Nullable
|
||||
ECKey findKeyFromPubKeyHash(byte[] pubKeyHash);
|
||||
ECKey findKeyFromPubKeyHash(byte[] pubKeyHash, @Nullable Script.ScriptType scriptType);
|
||||
|
||||
/**
|
||||
* Locates a keypair from the keychain given the raw public key bytes.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2014 devrandom
|
||||
* Copyright 2019 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -20,6 +21,7 @@ import com.google.common.collect.ImmutableList;
|
|||
import org.bitcoinj.crypto.ChildNumber;
|
||||
import org.bitcoinj.crypto.DeterministicKey;
|
||||
import org.bitcoinj.crypto.KeyCrypter;
|
||||
import org.bitcoinj.script.Script;
|
||||
|
||||
/**
|
||||
* Factory interface for creation keychains while de-serializing a wallet.
|
||||
|
@ -33,11 +35,12 @@ public interface KeyChainFactory {
|
|||
* @param seed the seed
|
||||
* @param crypter the encrypted/decrypter
|
||||
* @param isMarried whether the keychain is leading in a marriage
|
||||
* @param accountPath the specified account path
|
||||
* @param outputScriptType type of addresses (aka output scripts) to generate for receiving
|
||||
* @param accountPath account path to generate receiving addresses on
|
||||
*/
|
||||
DeterministicKeyChain makeKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicSeed seed,
|
||||
KeyCrypter crypter, boolean isMarried,
|
||||
ImmutableList<ChildNumber> accountPath);
|
||||
KeyCrypter crypter, boolean isMarried, Script.ScriptType outputScriptType,
|
||||
ImmutableList<ChildNumber> accountPath);
|
||||
|
||||
/**
|
||||
* Make a watching keychain.
|
||||
|
@ -49,9 +52,11 @@ public interface KeyChainFactory {
|
|||
* @param accountKey the account extended public key
|
||||
* @param isFollowingKey whether the keychain is following in a marriage
|
||||
* @param isMarried whether the keychain is leading in a marriage
|
||||
* @param outputScriptType type of addresses (aka output scripts) to generate for watching
|
||||
*/
|
||||
DeterministicKeyChain makeWatchingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey,
|
||||
boolean isFollowingKey, boolean isMarried) throws UnreadableWalletException;
|
||||
boolean isFollowingKey, boolean isMarried, Script.ScriptType outputScriptType)
|
||||
throws UnreadableWalletException;
|
||||
|
||||
/**
|
||||
* Make a spending keychain.
|
||||
|
@ -62,7 +67,8 @@ public interface KeyChainFactory {
|
|||
* @param firstSubKey the protobuf for the first child key (normally the parent of the external subchain)
|
||||
* @param accountKey the account extended public key
|
||||
* @param isMarried whether the keychain is leading in a marriage
|
||||
* @param outputScriptType type of addresses (aka output scripts) to generate for spending
|
||||
*/
|
||||
DeterministicKeyChain makeSpendingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey,
|
||||
boolean isMarried) throws UnreadableWalletException;
|
||||
boolean isMarried, Script.ScriptType outputScriptType) throws UnreadableWalletException;
|
||||
}
|
||||
|
|
|
@ -67,18 +67,22 @@ public class KeyChainGroup implements KeyBag {
|
|||
*/
|
||||
public static class Builder {
|
||||
private final NetworkParameters params;
|
||||
private final KeyChainGroupStructure structure;
|
||||
private final List<DeterministicKeyChain> chains = new LinkedList<DeterministicKeyChain>();
|
||||
|
||||
private Builder(NetworkParameters params) {
|
||||
private Builder(NetworkParameters params, KeyChainGroupStructure structure) {
|
||||
this.params = params;
|
||||
this.structure = structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add chain from a random source.
|
||||
* @param outputScriptType type of addresses (aka output scripts) to generate for receiving
|
||||
*/
|
||||
public Builder fromRandom() {
|
||||
public Builder fromRandom(Script.ScriptType outputScriptType) {
|
||||
this.chains.clear();
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().random(new SecureRandom()).build();
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().random(new SecureRandom())
|
||||
.outputScriptType(outputScriptType).accountPath(structure.accountPathFor(outputScriptType)).build();
|
||||
this.chains.add(chain);
|
||||
return this;
|
||||
}
|
||||
|
@ -86,10 +90,12 @@ public class KeyChainGroup implements KeyBag {
|
|||
/**
|
||||
* Add chain from a given seed.
|
||||
* @param seed deterministic seed to derive all keys from
|
||||
* @param outputScriptType type of addresses (aka output scripts) to generate for receiving
|
||||
*/
|
||||
public Builder fromSeed(DeterministicSeed seed) {
|
||||
public Builder fromSeed(DeterministicSeed seed, Script.ScriptType outputScriptType) {
|
||||
this.chains.clear();
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).build();
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).outputScriptType(outputScriptType)
|
||||
.accountPath(structure.accountPathFor(outputScriptType)).build();
|
||||
this.chains.add(chain);
|
||||
return this;
|
||||
}
|
||||
|
@ -144,7 +150,11 @@ public class KeyChainGroup implements KeyBag {
|
|||
}
|
||||
|
||||
public static KeyChainGroup.Builder builder(NetworkParameters params) {
|
||||
return new Builder(params);
|
||||
return new Builder(params, KeyChainGroupStructure.DEFAULT);
|
||||
}
|
||||
|
||||
public static KeyChainGroup.Builder builder(NetworkParameters params, KeyChainGroupStructure structure) {
|
||||
return new Builder(params, structure);
|
||||
}
|
||||
|
||||
private KeyChainGroup(NetworkParameters params, @Nullable BasicKeyChain basicKeyChain, @Nullable List<DeterministicKeyChain> chains,
|
||||
|
@ -235,6 +245,7 @@ public class KeyChainGroup implements KeyBag {
|
|||
*/
|
||||
public Address currentAddress(KeyChain.KeyPurpose purpose) {
|
||||
DeterministicKeyChain chain = getActiveKeyChain();
|
||||
Script.ScriptType outputScriptType = chain.getOutputScriptType();
|
||||
if (chain.isMarried()) {
|
||||
Address current = currentAddresses.get(purpose);
|
||||
if (current == null) {
|
||||
|
@ -242,8 +253,10 @@ public class KeyChainGroup implements KeyBag {
|
|||
currentAddresses.put(purpose, current);
|
||||
}
|
||||
return current;
|
||||
} else if (outputScriptType == Script.ScriptType.P2PKH || outputScriptType == Script.ScriptType.P2WPKH) {
|
||||
return Address.fromKey(params, currentKey(purpose), outputScriptType);
|
||||
} else {
|
||||
return LegacyAddress.fromKey(params, currentKey(purpose));
|
||||
throw new IllegalStateException(chain.getOutputScriptType().toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,6 +302,7 @@ public class KeyChainGroup implements KeyBag {
|
|||
*/
|
||||
public Address freshAddress(KeyChain.KeyPurpose purpose) {
|
||||
DeterministicKeyChain chain = getActiveKeyChain();
|
||||
Script.ScriptType outputScriptType = chain.getOutputScriptType();
|
||||
if (chain.isMarried()) {
|
||||
Script outputScript = chain.freshOutputScript(purpose);
|
||||
checkState(ScriptPattern.isPayToScriptHash(outputScript)); // Only handle P2SH for now
|
||||
|
@ -297,8 +311,10 @@ public class KeyChainGroup implements KeyBag {
|
|||
maybeLookaheadScripts();
|
||||
currentAddresses.put(purpose, freshAddress);
|
||||
return freshAddress;
|
||||
} else if (outputScriptType == Script.ScriptType.P2PKH || outputScriptType == Script.ScriptType.P2WPKH) {
|
||||
return Address.fromKey(params, freshKey(purpose), outputScriptType);
|
||||
} else {
|
||||
return LegacyAddress.fromKey(params, freshKey(purpose));
|
||||
throw new IllegalStateException(chain.getOutputScriptType().toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -432,14 +448,20 @@ public class KeyChainGroup implements KeyBag {
|
|||
|
||||
@Nullable
|
||||
@Override
|
||||
public ECKey findKeyFromPubKeyHash(byte[] pubKeyHash) {
|
||||
public ECKey findKeyFromPubKeyHash(byte[] pubKeyHash, @Nullable Script.ScriptType scriptType) {
|
||||
ECKey result;
|
||||
// BasicKeyChain can mix output script types.
|
||||
if ((result = basic.findKeyFromPubHash(pubKeyHash)) != null)
|
||||
return result;
|
||||
if (chains != null)
|
||||
for (DeterministicKeyChain chain : chains)
|
||||
if (chains != null) {
|
||||
for (DeterministicKeyChain chain : chains) {
|
||||
// This check limits DeterministicKeyChain to specific output script usage.
|
||||
if (scriptType != null && scriptType != chain.getOutputScriptType())
|
||||
continue;
|
||||
if ((result = chain.findKeyFromPubHash(pubKeyHash)) != null)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright by the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.bitcoinj.wallet;
|
||||
|
||||
import org.bitcoinj.crypto.ChildNumber;
|
||||
import org.bitcoinj.script.Script;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/** Defines a structure for hierarchical deterministic wallets. */
|
||||
public interface KeyChainGroupStructure {
|
||||
/** Map desired output script type to an account path */
|
||||
ImmutableList<ChildNumber> accountPathFor(Script.ScriptType outputScriptType);
|
||||
|
||||
/** Default {@link KeyChainGroupStructure} implementation. Based on BIP32 "Wallet structure". */
|
||||
public static final KeyChainGroupStructure DEFAULT = new KeyChainGroupStructure() {
|
||||
@Override
|
||||
public ImmutableList<ChildNumber> accountPathFor(Script.ScriptType outputScriptType) {
|
||||
if (outputScriptType == null || outputScriptType == Script.ScriptType.P2PKH)
|
||||
return DeterministicKeyChain.ACCOUNT_ZERO_PATH;
|
||||
else if (outputScriptType == Script.ScriptType.P2WPKH)
|
||||
return DeterministicKeyChain.ACCOUNT_ONE_PATH;
|
||||
else
|
||||
throw new IllegalArgumentException(outputScriptType.toString());
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2019 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -65,7 +66,9 @@ public class KeyTimeCoinSelector implements CoinSelector {
|
|||
if (ScriptPattern.isPayToPubKey(scriptPubKey)) {
|
||||
controllingKey = wallet.findKeyFromPubKey(ScriptPattern.extractKeyFromPayToPubKey(scriptPubKey));
|
||||
} else if (ScriptPattern.isPayToPubKeyHash(scriptPubKey)) {
|
||||
controllingKey = wallet.findKeyFromPubKeyHash(ScriptPattern.extractHashFromPayToPubKeyHash(scriptPubKey));
|
||||
controllingKey = wallet.findKeyFromPubKeyHash(ScriptPattern.extractHashFromPayToPubKeyHash(scriptPubKey), Script.ScriptType.P2PKH);
|
||||
} else if (ScriptPattern.isPayToWitnessPubKeyHash(scriptPubKey)) {
|
||||
controllingKey = wallet.findKeyFromPubKeyHash(ScriptPattern.extractHashFromPayToWitnessHash(scriptPubKey), Script.ScriptType.P2WPKH);
|
||||
} else {
|
||||
log.info("Skipping tx output {} because it's not of simple form.", output);
|
||||
continue;
|
||||
|
|
|
@ -102,14 +102,14 @@ public class MarriedKeyChain extends DeterministicKeyChain {
|
|||
|
||||
MarriedKeyChain chain;
|
||||
if (random != null)
|
||||
chain = new MarriedKeyChain(new DeterministicSeed(random, bits, getPassphrase()), null, accountPath);
|
||||
chain = new MarriedKeyChain(new DeterministicSeed(random, bits, getPassphrase()), null, outputScriptType, accountPath);
|
||||
else if (entropy != null)
|
||||
chain = new MarriedKeyChain(new DeterministicSeed(entropy, getPassphrase(), creationTimeSecs), null,
|
||||
accountPath);
|
||||
outputScriptType, accountPath);
|
||||
else if (seed != null)
|
||||
chain = new MarriedKeyChain(seed, null, accountPath);
|
||||
chain = new MarriedKeyChain(seed, null, outputScriptType, accountPath);
|
||||
else if (watchingKey != null)
|
||||
chain = new MarriedKeyChain(watchingKey);
|
||||
chain = new MarriedKeyChain(watchingKey, outputScriptType);
|
||||
else
|
||||
throw new IllegalStateException();
|
||||
chain.addFollowingAccountKeys(followingKeys, threshold);
|
||||
|
@ -125,16 +125,16 @@ public class MarriedKeyChain extends DeterministicKeyChain {
|
|||
* This constructor is not stable across releases! If you need a stable API, use {@link #builder()} to use a
|
||||
* {@link Builder}.
|
||||
*/
|
||||
protected MarriedKeyChain(DeterministicKey accountKey) {
|
||||
super(accountKey, false, true);
|
||||
protected MarriedKeyChain(DeterministicKey accountKey, Script.ScriptType outputScriptType) {
|
||||
super(accountKey, false, true, outputScriptType);
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is not stable across releases! If you need a stable API, use {@link #builder()} to use a
|
||||
* {@link Builder}.
|
||||
*/
|
||||
protected MarriedKeyChain(DeterministicSeed seed, KeyCrypter crypter, ImmutableList<ChildNumber> accountPath) {
|
||||
super(seed, crypter, accountPath);
|
||||
protected MarriedKeyChain(DeterministicSeed seed, KeyCrypter crypter, Script.ScriptType outputScriptType, ImmutableList<ChildNumber> accountPath) {
|
||||
super(seed, crypter, outputScriptType, accountPath);
|
||||
}
|
||||
|
||||
void setFollowingKeyChains(List<DeterministicKeyChain> followingKeyChains) {
|
||||
|
@ -189,7 +189,8 @@ public class MarriedKeyChain extends DeterministicKeyChain {
|
|||
|
||||
for (DeterministicKey key : followingAccountKeys) {
|
||||
checkArgument(key.getPath().size() == getAccountPath().size(), "Following keys have to be account keys");
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().watchAndFollow(key).build();
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().watchAndFollow(key)
|
||||
.outputScriptType(getOutputScriptType()).build();
|
||||
if (lookaheadSize >= 0)
|
||||
chain.setLookaheadSize(lookaheadSize);
|
||||
if (lookaheadThreshold >= 0)
|
||||
|
|
|
@ -2779,6 +2779,23 @@ public final class Protos {
|
|||
* <code>repeated uint32 account_path = 10 [packed = true];</code>
|
||||
*/
|
||||
int getAccountPath(int index);
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Type of addresses (aka output scripts) to generate for receiving.
|
||||
* </pre>
|
||||
*
|
||||
* <code>optional .wallet.Key.OutputScriptType output_script_type = 11;</code>
|
||||
*/
|
||||
boolean hasOutputScriptType();
|
||||
/**
|
||||
* <pre>
|
||||
* Type of addresses (aka output scripts) to generate for receiving.
|
||||
* </pre>
|
||||
*
|
||||
* <code>optional .wallet.Key.OutputScriptType output_script_type = 11;</code>
|
||||
*/
|
||||
org.bitcoinj.wallet.Protos.Key.OutputScriptType getOutputScriptType();
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
|
@ -2808,6 +2825,7 @@ public final class Protos {
|
|||
creationTimestamp_ = 0L;
|
||||
deterministicSeed_ = com.google.protobuf.ByteString.EMPTY;
|
||||
accountPath_ = java.util.Collections.emptyList();
|
||||
outputScriptType_ = 1;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
|
@ -2935,6 +2953,17 @@ public final class Protos {
|
|||
input.popLimit(limit);
|
||||
break;
|
||||
}
|
||||
case 88: {
|
||||
int rawValue = input.readEnum();
|
||||
org.bitcoinj.wallet.Protos.Key.OutputScriptType value = org.bitcoinj.wallet.Protos.Key.OutputScriptType.valueOf(rawValue);
|
||||
if (value == null) {
|
||||
unknownFields.mergeVarintField(11, rawValue);
|
||||
} else {
|
||||
bitField0_ |= 0x00000200;
|
||||
outputScriptType_ = rawValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
|
@ -3116,6 +3145,96 @@ public final class Protos {
|
|||
// @@protoc_insertion_point(enum_scope:wallet.Key.Type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Protobuf enum {@code wallet.Key.OutputScriptType}
|
||||
*/
|
||||
public enum OutputScriptType
|
||||
implements com.google.protobuf.ProtocolMessageEnum {
|
||||
/**
|
||||
* <code>P2PKH = 1;</code>
|
||||
*/
|
||||
P2PKH(1),
|
||||
/**
|
||||
* <code>P2WPKH = 2;</code>
|
||||
*/
|
||||
P2WPKH(2),
|
||||
;
|
||||
|
||||
/**
|
||||
* <code>P2PKH = 1;</code>
|
||||
*/
|
||||
public static final int P2PKH_VALUE = 1;
|
||||
/**
|
||||
* <code>P2WPKH = 2;</code>
|
||||
*/
|
||||
public static final int P2WPKH_VALUE = 2;
|
||||
|
||||
|
||||
public final int getNumber() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #forNumber(int)} instead.
|
||||
*/
|
||||
@java.lang.Deprecated
|
||||
public static OutputScriptType valueOf(int value) {
|
||||
return forNumber(value);
|
||||
}
|
||||
|
||||
public static OutputScriptType forNumber(int value) {
|
||||
switch (value) {
|
||||
case 1: return P2PKH;
|
||||
case 2: return P2WPKH;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static com.google.protobuf.Internal.EnumLiteMap<OutputScriptType>
|
||||
internalGetValueMap() {
|
||||
return internalValueMap;
|
||||
}
|
||||
private static final com.google.protobuf.Internal.EnumLiteMap<
|
||||
OutputScriptType> internalValueMap =
|
||||
new com.google.protobuf.Internal.EnumLiteMap<OutputScriptType>() {
|
||||
public OutputScriptType findValueByNumber(int number) {
|
||||
return OutputScriptType.forNumber(number);
|
||||
}
|
||||
};
|
||||
|
||||
public final com.google.protobuf.Descriptors.EnumValueDescriptor
|
||||
getValueDescriptor() {
|
||||
return getDescriptor().getValues().get(ordinal());
|
||||
}
|
||||
public final com.google.protobuf.Descriptors.EnumDescriptor
|
||||
getDescriptorForType() {
|
||||
return getDescriptor();
|
||||
}
|
||||
public static final com.google.protobuf.Descriptors.EnumDescriptor
|
||||
getDescriptor() {
|
||||
return org.bitcoinj.wallet.Protos.Key.getDescriptor().getEnumTypes().get(1);
|
||||
}
|
||||
|
||||
private static final OutputScriptType[] VALUES = values();
|
||||
|
||||
public static OutputScriptType valueOf(
|
||||
com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
|
||||
if (desc.getType() != getDescriptor()) {
|
||||
throw new java.lang.IllegalArgumentException(
|
||||
"EnumValueDescriptor is not for this type.");
|
||||
}
|
||||
return VALUES[desc.getIndex()];
|
||||
}
|
||||
|
||||
private final int value;
|
||||
|
||||
private OutputScriptType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(enum_scope:wallet.Key.OutputScriptType)
|
||||
}
|
||||
|
||||
private int bitField0_;
|
||||
public static final int TYPE_FIELD_NUMBER = 1;
|
||||
private int type_;
|
||||
|
@ -3409,6 +3528,30 @@ public final class Protos {
|
|||
}
|
||||
private int accountPathMemoizedSerializedSize = -1;
|
||||
|
||||
public static final int OUTPUT_SCRIPT_TYPE_FIELD_NUMBER = 11;
|
||||
private int outputScriptType_;
|
||||
/**
|
||||
* <pre>
|
||||
* Type of addresses (aka output scripts) to generate for receiving.
|
||||
* </pre>
|
||||
*
|
||||
* <code>optional .wallet.Key.OutputScriptType output_script_type = 11;</code>
|
||||
*/
|
||||
public boolean hasOutputScriptType() {
|
||||
return ((bitField0_ & 0x00000200) == 0x00000200);
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Type of addresses (aka output scripts) to generate for receiving.
|
||||
* </pre>
|
||||
*
|
||||
* <code>optional .wallet.Key.OutputScriptType output_script_type = 11;</code>
|
||||
*/
|
||||
public org.bitcoinj.wallet.Protos.Key.OutputScriptType getOutputScriptType() {
|
||||
org.bitcoinj.wallet.Protos.Key.OutputScriptType result = org.bitcoinj.wallet.Protos.Key.OutputScriptType.valueOf(outputScriptType_);
|
||||
return result == null ? org.bitcoinj.wallet.Protos.Key.OutputScriptType.P2PKH : result;
|
||||
}
|
||||
|
||||
private byte memoizedIsInitialized = -1;
|
||||
public final boolean isInitialized() {
|
||||
byte isInitialized = memoizedIsInitialized;
|
||||
|
@ -3478,6 +3621,9 @@ public final class Protos {
|
|||
for (int i = 0; i < accountPath_.size(); i++) {
|
||||
output.writeUInt32NoTag(accountPath_.get(i));
|
||||
}
|
||||
if (((bitField0_ & 0x00000200) == 0x00000200)) {
|
||||
output.writeEnum(11, outputScriptType_);
|
||||
}
|
||||
unknownFields.writeTo(output);
|
||||
}
|
||||
|
||||
|
@ -3535,6 +3681,10 @@ public final class Protos {
|
|||
}
|
||||
accountPathMemoizedSerializedSize = dataSize;
|
||||
}
|
||||
if (((bitField0_ & 0x00000200) == 0x00000200)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeEnumSize(11, outputScriptType_);
|
||||
}
|
||||
size += unknownFields.getSerializedSize();
|
||||
memoizedSize = size;
|
||||
return size;
|
||||
|
@ -3598,6 +3748,10 @@ public final class Protos {
|
|||
}
|
||||
result = result && getAccountPathList()
|
||||
.equals(other.getAccountPathList());
|
||||
result = result && (hasOutputScriptType() == other.hasOutputScriptType());
|
||||
if (hasOutputScriptType()) {
|
||||
result = result && outputScriptType_ == other.outputScriptType_;
|
||||
}
|
||||
result = result && unknownFields.equals(other.unknownFields);
|
||||
return result;
|
||||
}
|
||||
|
@ -3650,6 +3804,10 @@ public final class Protos {
|
|||
hash = (37 * hash) + ACCOUNT_PATH_FIELD_NUMBER;
|
||||
hash = (53 * hash) + getAccountPathList().hashCode();
|
||||
}
|
||||
if (hasOutputScriptType()) {
|
||||
hash = (37 * hash) + OUTPUT_SCRIPT_TYPE_FIELD_NUMBER;
|
||||
hash = (53 * hash) + outputScriptType_;
|
||||
}
|
||||
hash = (29 * hash) + unknownFields.hashCode();
|
||||
memoizedHashCode = hash;
|
||||
return hash;
|
||||
|
@ -3812,6 +3970,8 @@ public final class Protos {
|
|||
bitField0_ = (bitField0_ & ~0x00000100);
|
||||
accountPath_ = java.util.Collections.emptyList();
|
||||
bitField0_ = (bitField0_ & ~0x00000200);
|
||||
outputScriptType_ = 1;
|
||||
bitField0_ = (bitField0_ & ~0x00000400);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -3889,6 +4049,10 @@ public final class Protos {
|
|||
bitField0_ = (bitField0_ & ~0x00000200);
|
||||
}
|
||||
result.accountPath_ = accountPath_;
|
||||
if (((from_bitField0_ & 0x00000400) == 0x00000400)) {
|
||||
to_bitField0_ |= 0x00000200;
|
||||
}
|
||||
result.outputScriptType_ = outputScriptType_;
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
return result;
|
||||
|
@ -3970,6 +4134,9 @@ public final class Protos {
|
|||
}
|
||||
onChanged();
|
||||
}
|
||||
if (other.hasOutputScriptType()) {
|
||||
setOutputScriptType(other.getOutputScriptType());
|
||||
}
|
||||
this.mergeUnknownFields(other.unknownFields);
|
||||
onChanged();
|
||||
return this;
|
||||
|
@ -4888,6 +5055,58 @@ public final class Protos {
|
|||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
private int outputScriptType_ = 1;
|
||||
/**
|
||||
* <pre>
|
||||
* Type of addresses (aka output scripts) to generate for receiving.
|
||||
* </pre>
|
||||
*
|
||||
* <code>optional .wallet.Key.OutputScriptType output_script_type = 11;</code>
|
||||
*/
|
||||
public boolean hasOutputScriptType() {
|
||||
return ((bitField0_ & 0x00000400) == 0x00000400);
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Type of addresses (aka output scripts) to generate for receiving.
|
||||
* </pre>
|
||||
*
|
||||
* <code>optional .wallet.Key.OutputScriptType output_script_type = 11;</code>
|
||||
*/
|
||||
public org.bitcoinj.wallet.Protos.Key.OutputScriptType getOutputScriptType() {
|
||||
org.bitcoinj.wallet.Protos.Key.OutputScriptType result = org.bitcoinj.wallet.Protos.Key.OutputScriptType.valueOf(outputScriptType_);
|
||||
return result == null ? org.bitcoinj.wallet.Protos.Key.OutputScriptType.P2PKH : result;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Type of addresses (aka output scripts) to generate for receiving.
|
||||
* </pre>
|
||||
*
|
||||
* <code>optional .wallet.Key.OutputScriptType output_script_type = 11;</code>
|
||||
*/
|
||||
public Builder setOutputScriptType(org.bitcoinj.wallet.Protos.Key.OutputScriptType value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
bitField0_ |= 0x00000400;
|
||||
outputScriptType_ = value.getNumber();
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <pre>
|
||||
* Type of addresses (aka output scripts) to generate for receiving.
|
||||
* </pre>
|
||||
*
|
||||
* <code>optional .wallet.Key.OutputScriptType output_script_type = 11;</code>
|
||||
*/
|
||||
public Builder clearOutputScriptType() {
|
||||
bitField0_ = (bitField0_ & ~0x00000400);
|
||||
outputScriptType_ = 1;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
public final Builder setUnknownFields(
|
||||
final com.google.protobuf.UnknownFieldSet unknownFields) {
|
||||
return super.setUnknownFields(unknownFields);
|
||||
|
@ -20418,7 +20637,7 @@ public final class Protos {
|
|||
"ode\030\001 \002(\014\022\014\n\004path\030\002 \003(\r\022\026\n\016issued_subkey" +
|
||||
"s\030\003 \001(\r\022\026\n\016lookahead_size\030\004 \001(\r\022\023\n\013isFol" +
|
||||
"lowing\030\005 \001(\010\022\036\n\023sigsRequiredToSpend\030\006 \001(" +
|
||||
"\r:\0011\"\264\003\n\003Key\022\036\n\004type\030\001 \002(\0162\020.wallet.Key." +
|
||||
"\r:\0011\"\231\004\n\003Key\022\036\n\004type\030\001 \002(\0162\020.wallet.Key." +
|
||||
"Type\022\024\n\014secret_bytes\030\002 \001(\014\022-\n\016encrypted_",
|
||||
"data\030\006 \001(\0132\025.wallet.EncryptedData\022\022\n\npub" +
|
||||
"lic_key\030\003 \001(\014\022\r\n\005label\030\004 \001(\t\022\032\n\022creation" +
|
||||
|
@ -20426,70 +20645,72 @@ public final class Protos {
|
|||
"\001(\0132\030.wallet.DeterministicKey\022\032\n\022determi" +
|
||||
"nistic_seed\030\010 \001(\014\022;\n\034encrypted_determini" +
|
||||
"stic_seed\030\t \001(\0132\025.wallet.EncryptedData\022\030" +
|
||||
"\n\014account_path\030\n \003(\rB\002\020\001\"a\n\004Type\022\014\n\010ORIG" +
|
||||
"INAL\020\001\022\030\n\024ENCRYPTED_SCRYPT_AES\020\002\022\032\n\026DETE" +
|
||||
"RMINISTIC_MNEMONIC\020\003\022\025\n\021DETERMINISTIC_KE" +
|
||||
"Y\020\004\"5\n\006Script\022\017\n\007program\030\001 \002(\014\022\032\n\022creati",
|
||||
"on_timestamp\030\002 \002(\003\"\035\n\rScriptWitness\022\014\n\004d" +
|
||||
"ata\030\001 \003(\014\"\272\001\n\020TransactionInput\022\"\n\032transa" +
|
||||
"ction_out_point_hash\030\001 \002(\014\022#\n\033transactio" +
|
||||
"n_out_point_index\030\002 \002(\r\022\024\n\014script_bytes\030" +
|
||||
"\003 \002(\014\022\020\n\010sequence\030\004 \001(\r\022\r\n\005value\030\005 \001(\003\022&" +
|
||||
"\n\007witness\030\006 \001(\0132\025.wallet.ScriptWitness\"\177" +
|
||||
"\n\021TransactionOutput\022\r\n\005value\030\001 \002(\003\022\024\n\014sc" +
|
||||
"ript_bytes\030\002 \002(\014\022!\n\031spent_by_transaction" +
|
||||
"_hash\030\003 \001(\014\022\"\n\032spent_by_transaction_inde" +
|
||||
"x\030\004 \001(\005\"\267\003\n\025TransactionConfidence\0220\n\004typ",
|
||||
"e\030\001 \001(\0162\".wallet.TransactionConfidence.T" +
|
||||
"ype\022\032\n\022appeared_at_height\030\002 \001(\005\022\036\n\026overr" +
|
||||
"iding_transaction\030\003 \001(\014\022\r\n\005depth\030\004 \001(\005\022)" +
|
||||
"\n\014broadcast_by\030\006 \003(\0132\023.wallet.PeerAddres" +
|
||||
"s\022\033\n\023last_broadcasted_at\030\010 \001(\003\0224\n\006source" +
|
||||
"\030\007 \001(\0162$.wallet.TransactionConfidence.So" +
|
||||
"urce\"`\n\004Type\022\013\n\007UNKNOWN\020\000\022\014\n\010BUILDING\020\001\022" +
|
||||
"\013\n\007PENDING\020\002\022\025\n\021NOT_IN_BEST_CHAIN\020\003\022\010\n\004D" +
|
||||
"EAD\020\004\022\017\n\013IN_CONFLICT\020\005\"A\n\006Source\022\022\n\016SOUR" +
|
||||
"CE_UNKNOWN\020\000\022\022\n\016SOURCE_NETWORK\020\001\022\017\n\013SOUR",
|
||||
"CE_SELF\020\002\"\303\005\n\013Transaction\022\017\n\007version\030\001 \002" +
|
||||
"(\005\022\014\n\004hash\030\002 \002(\014\022&\n\004pool\030\003 \001(\0162\030.wallet." +
|
||||
"Transaction.Pool\022\021\n\tlock_time\030\004 \001(\r\022\022\n\nu" +
|
||||
"pdated_at\030\005 \001(\003\0223\n\021transaction_input\030\006 \003" +
|
||||
"(\0132\030.wallet.TransactionInput\0225\n\022transact" +
|
||||
"ion_output\030\007 \003(\0132\031.wallet.TransactionOut" +
|
||||
"put\022\022\n\nblock_hash\030\010 \003(\014\022 \n\030block_relativ" +
|
||||
"ity_offsets\030\013 \003(\005\0221\n\nconfidence\030\t \001(\0132\035." +
|
||||
"wallet.TransactionConfidence\0225\n\007purpose\030" +
|
||||
"\n \001(\0162\033.wallet.Transaction.Purpose:\007UNKN",
|
||||
"OWN\022+\n\rexchange_rate\030\014 \001(\0132\024.wallet.Exch" +
|
||||
"angeRate\022\014\n\004memo\030\r \001(\t\"Y\n\004Pool\022\013\n\007UNSPEN" +
|
||||
"T\020\004\022\t\n\005SPENT\020\005\022\014\n\010INACTIVE\020\002\022\010\n\004DEAD\020\n\022\013" +
|
||||
"\n\007PENDING\020\020\022\024\n\020PENDING_INACTIVE\020\022\"\243\001\n\007Pu" +
|
||||
"rpose\022\013\n\007UNKNOWN\020\000\022\020\n\014USER_PAYMENT\020\001\022\020\n\014" +
|
||||
"KEY_ROTATION\020\002\022\034\n\030ASSURANCE_CONTRACT_CLA" +
|
||||
"IM\020\003\022\035\n\031ASSURANCE_CONTRACT_PLEDGE\020\004\022\033\n\027A" +
|
||||
"SSURANCE_CONTRACT_STUB\020\005\022\r\n\tRAISE_FEE\020\006\"" +
|
||||
"N\n\020ScryptParameters\022\014\n\004salt\030\001 \002(\014\022\020\n\001n\030\002" +
|
||||
" \001(\003:\00516384\022\014\n\001r\030\003 \001(\005:\0018\022\014\n\001p\030\004 \001(\005:\0011\"",
|
||||
"8\n\tExtension\022\n\n\002id\030\001 \002(\t\022\014\n\004data\030\002 \002(\014\022\021" +
|
||||
"\n\tmandatory\030\003 \002(\010\" \n\003Tag\022\013\n\003tag\030\001 \002(\t\022\014\n" +
|
||||
"\004data\030\002 \002(\014\"\261\004\n\006Wallet\022\032\n\022network_identi" +
|
||||
"fier\030\001 \002(\t\022\034\n\024last_seen_block_hash\030\002 \001(\014" +
|
||||
"\022\036\n\026last_seen_block_height\030\014 \001(\r\022!\n\031last" +
|
||||
"_seen_block_time_secs\030\016 \001(\003\022\030\n\003key\030\003 \003(\013" +
|
||||
"2\013.wallet.Key\022(\n\013transaction\030\004 \003(\0132\023.wal" +
|
||||
"let.Transaction\022&\n\016watched_script\030\017 \003(\0132" +
|
||||
"\016.wallet.Script\022C\n\017encryption_type\030\005 \001(\016" +
|
||||
"2\035.wallet.Wallet.EncryptionType:\013UNENCRY",
|
||||
"PTED\0227\n\025encryption_parameters\030\006 \001(\0132\030.wa" +
|
||||
"llet.ScryptParameters\022\022\n\007version\030\007 \001(\005:\001" +
|
||||
"1\022$\n\textension\030\n \003(\0132\021.wallet.Extension\022" +
|
||||
"\023\n\013description\030\013 \001(\t\022\031\n\021key_rotation_tim" +
|
||||
"e\030\r \001(\004\022\031\n\004tags\030\020 \003(\0132\013.wallet.Tag\";\n\016En" +
|
||||
"cryptionType\022\017\n\013UNENCRYPTED\020\001\022\030\n\024ENCRYPT" +
|
||||
"ED_SCRYPT_AES\020\002\"R\n\014ExchangeRate\022\022\n\ncoin_" +
|
||||
"value\030\001 \002(\003\022\022\n\nfiat_value\030\002 \002(\003\022\032\n\022fiat_" +
|
||||
"currency_code\030\003 \002(\tB\035\n\023org.bitcoinj.wall" +
|
||||
"etB\006Protos"
|
||||
"\n\014account_path\030\n \003(\rB\002\020\001\0228\n\022output_scrip" +
|
||||
"t_type\030\013 \001(\0162\034.wallet.Key.OutputScriptTy" +
|
||||
"pe\"a\n\004Type\022\014\n\010ORIGINAL\020\001\022\030\n\024ENCRYPTED_SC" +
|
||||
"RYPT_AES\020\002\022\032\n\026DETERMINISTIC_MNEMONIC\020\003\022\025",
|
||||
"\n\021DETERMINISTIC_KEY\020\004\")\n\020OutputScriptTyp" +
|
||||
"e\022\t\n\005P2PKH\020\001\022\n\n\006P2WPKH\020\002\"5\n\006Script\022\017\n\007pr" +
|
||||
"ogram\030\001 \002(\014\022\032\n\022creation_timestamp\030\002 \002(\003\"" +
|
||||
"\035\n\rScriptWitness\022\014\n\004data\030\001 \003(\014\"\272\001\n\020Trans" +
|
||||
"actionInput\022\"\n\032transaction_out_point_has" +
|
||||
"h\030\001 \002(\014\022#\n\033transaction_out_point_index\030\002" +
|
||||
" \002(\r\022\024\n\014script_bytes\030\003 \002(\014\022\020\n\010sequence\030\004" +
|
||||
" \001(\r\022\r\n\005value\030\005 \001(\003\022&\n\007witness\030\006 \001(\0132\025.w" +
|
||||
"allet.ScriptWitness\"\177\n\021TransactionOutput" +
|
||||
"\022\r\n\005value\030\001 \002(\003\022\024\n\014script_bytes\030\002 \002(\014\022!\n",
|
||||
"\031spent_by_transaction_hash\030\003 \001(\014\022\"\n\032spen" +
|
||||
"t_by_transaction_index\030\004 \001(\005\"\267\003\n\025Transac" +
|
||||
"tionConfidence\0220\n\004type\030\001 \001(\0162\".wallet.Tr" +
|
||||
"ansactionConfidence.Type\022\032\n\022appeared_at_" +
|
||||
"height\030\002 \001(\005\022\036\n\026overriding_transaction\030\003" +
|
||||
" \001(\014\022\r\n\005depth\030\004 \001(\005\022)\n\014broadcast_by\030\006 \003(" +
|
||||
"\0132\023.wallet.PeerAddress\022\033\n\023last_broadcast" +
|
||||
"ed_at\030\010 \001(\003\0224\n\006source\030\007 \001(\0162$.wallet.Tra" +
|
||||
"nsactionConfidence.Source\"`\n\004Type\022\013\n\007UNK" +
|
||||
"NOWN\020\000\022\014\n\010BUILDING\020\001\022\013\n\007PENDING\020\002\022\025\n\021NOT",
|
||||
"_IN_BEST_CHAIN\020\003\022\010\n\004DEAD\020\004\022\017\n\013IN_CONFLIC" +
|
||||
"T\020\005\"A\n\006Source\022\022\n\016SOURCE_UNKNOWN\020\000\022\022\n\016SOU" +
|
||||
"RCE_NETWORK\020\001\022\017\n\013SOURCE_SELF\020\002\"\303\005\n\013Trans" +
|
||||
"action\022\017\n\007version\030\001 \002(\005\022\014\n\004hash\030\002 \002(\014\022&\n" +
|
||||
"\004pool\030\003 \001(\0162\030.wallet.Transaction.Pool\022\021\n" +
|
||||
"\tlock_time\030\004 \001(\r\022\022\n\nupdated_at\030\005 \001(\003\0223\n\021" +
|
||||
"transaction_input\030\006 \003(\0132\030.wallet.Transac" +
|
||||
"tionInput\0225\n\022transaction_output\030\007 \003(\0132\031." +
|
||||
"wallet.TransactionOutput\022\022\n\nblock_hash\030\010" +
|
||||
" \003(\014\022 \n\030block_relativity_offsets\030\013 \003(\005\0221",
|
||||
"\n\nconfidence\030\t \001(\0132\035.wallet.TransactionC" +
|
||||
"onfidence\0225\n\007purpose\030\n \001(\0162\033.wallet.Tran" +
|
||||
"saction.Purpose:\007UNKNOWN\022+\n\rexchange_rat" +
|
||||
"e\030\014 \001(\0132\024.wallet.ExchangeRate\022\014\n\004memo\030\r " +
|
||||
"\001(\t\"Y\n\004Pool\022\013\n\007UNSPENT\020\004\022\t\n\005SPENT\020\005\022\014\n\010I" +
|
||||
"NACTIVE\020\002\022\010\n\004DEAD\020\n\022\013\n\007PENDING\020\020\022\024\n\020PEND" +
|
||||
"ING_INACTIVE\020\022\"\243\001\n\007Purpose\022\013\n\007UNKNOWN\020\000\022" +
|
||||
"\020\n\014USER_PAYMENT\020\001\022\020\n\014KEY_ROTATION\020\002\022\034\n\030A" +
|
||||
"SSURANCE_CONTRACT_CLAIM\020\003\022\035\n\031ASSURANCE_C" +
|
||||
"ONTRACT_PLEDGE\020\004\022\033\n\027ASSURANCE_CONTRACT_S",
|
||||
"TUB\020\005\022\r\n\tRAISE_FEE\020\006\"N\n\020ScryptParameters" +
|
||||
"\022\014\n\004salt\030\001 \002(\014\022\020\n\001n\030\002 \001(\003:\00516384\022\014\n\001r\030\003 " +
|
||||
"\001(\005:\0018\022\014\n\001p\030\004 \001(\005:\0011\"8\n\tExtension\022\n\n\002id\030" +
|
||||
"\001 \002(\t\022\014\n\004data\030\002 \002(\014\022\021\n\tmandatory\030\003 \002(\010\" " +
|
||||
"\n\003Tag\022\013\n\003tag\030\001 \002(\t\022\014\n\004data\030\002 \002(\014\"\261\004\n\006Wal" +
|
||||
"let\022\032\n\022network_identifier\030\001 \002(\t\022\034\n\024last_" +
|
||||
"seen_block_hash\030\002 \001(\014\022\036\n\026last_seen_block" +
|
||||
"_height\030\014 \001(\r\022!\n\031last_seen_block_time_se" +
|
||||
"cs\030\016 \001(\003\022\030\n\003key\030\003 \003(\0132\013.wallet.Key\022(\n\013tr" +
|
||||
"ansaction\030\004 \003(\0132\023.wallet.Transaction\022&\n\016",
|
||||
"watched_script\030\017 \003(\0132\016.wallet.Script\022C\n\017" +
|
||||
"encryption_type\030\005 \001(\0162\035.wallet.Wallet.En" +
|
||||
"cryptionType:\013UNENCRYPTED\0227\n\025encryption_" +
|
||||
"parameters\030\006 \001(\0132\030.wallet.ScryptParamete" +
|
||||
"rs\022\022\n\007version\030\007 \001(\005:\0011\022$\n\textension\030\n \003(" +
|
||||
"\0132\021.wallet.Extension\022\023\n\013description\030\013 \001(" +
|
||||
"\t\022\031\n\021key_rotation_time\030\r \001(\004\022\031\n\004tags\030\020 \003" +
|
||||
"(\0132\013.wallet.Tag\";\n\016EncryptionType\022\017\n\013UNE" +
|
||||
"NCRYPTED\020\001\022\030\n\024ENCRYPTED_SCRYPT_AES\020\002\"R\n\014" +
|
||||
"ExchangeRate\022\022\n\ncoin_value\030\001 \002(\003\022\022\n\nfiat",
|
||||
"_value\030\002 \002(\003\022\032\n\022fiat_currency_code\030\003 \002(\t" +
|
||||
"B\035\n\023org.bitcoinj.walletB\006Protos"
|
||||
};
|
||||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
||||
new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
|
||||
|
@ -20526,7 +20747,7 @@ public final class Protos {
|
|||
internal_static_wallet_Key_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
|
||||
internal_static_wallet_Key_descriptor,
|
||||
new java.lang.String[] { "Type", "SecretBytes", "EncryptedData", "PublicKey", "Label", "CreationTimestamp", "DeterministicKey", "DeterministicSeed", "EncryptedDeterministicSeed", "AccountPath", });
|
||||
new java.lang.String[] { "Type", "SecretBytes", "EncryptedData", "PublicKey", "Label", "CreationTimestamp", "DeterministicKey", "DeterministicSeed", "EncryptedDeterministicSeed", "AccountPath", "OutputScriptType", });
|
||||
internal_static_wallet_Script_descriptor =
|
||||
getDescriptor().getMessageTypes().get(4);
|
||||
internal_static_wallet_Script_fieldAccessorTable = new
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2014 Kosta Korenkov
|
||||
* Copyright 2019 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -52,12 +53,13 @@ public class RedeemData {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates RedeemData for P2PKH or P2PK input. Provided key is a single private key needed
|
||||
* to spend such inputs and provided program should be a proper CHECKSIG program.
|
||||
* Creates RedeemData for P2PKH, P2WPKH or P2PK input. Provided key is a single private key needed
|
||||
* to spend such inputs.
|
||||
*/
|
||||
public static RedeemData of(ECKey key, Script program) {
|
||||
checkArgument(ScriptPattern.isPayToPubKeyHash(program) || ScriptPattern.isPayToPubKey(program));
|
||||
return key != null ? new RedeemData(Collections.singletonList(key), program) : null;
|
||||
public static RedeemData of(ECKey key, Script redeemScript) {
|
||||
checkArgument(ScriptPattern.isPayToPubKeyHash(redeemScript)
|
||||
|| ScriptPattern.isPayToWitnessPubKeyHash(redeemScript) || ScriptPattern.isPayToPubKey(redeemScript));
|
||||
return key != null ? new RedeemData(Collections.singletonList(key), redeemScript) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,7 @@ import com.google.protobuf.*;
|
|||
import net.jcip.annotations.*;
|
||||
import org.bitcoinj.core.listeners.*;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Base58;
|
||||
import org.bitcoinj.core.AbstractBlockChain;
|
||||
import org.bitcoinj.core.BlockChain;
|
||||
import org.bitcoinj.core.BloomFilter;
|
||||
|
@ -75,6 +76,7 @@ import org.bouncycastle.crypto.params.*;
|
|||
import javax.annotation.*;
|
||||
import java.io.*;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
|
@ -251,18 +253,44 @@ public class Wallet extends BaseTaggableObject
|
|||
* Creates a new, empty wallet with a randomly chosen seed and no transactions. Make sure to provide for sufficient
|
||||
* backup! Any keys will be derived from the seed. If you want to restore a wallet from disk instead, see
|
||||
* {@link #loadFromFile}.
|
||||
* @param params network parameters
|
||||
* @param outputScriptType type of addresses (aka output scripts) to generate for receiving
|
||||
*/
|
||||
public Wallet(NetworkParameters params) {
|
||||
this(Context.getOrCreate(params));
|
||||
public static Wallet createDeterministic(NetworkParameters params, Script.ScriptType outputScriptType) {
|
||||
return createDeterministic(Context.getOrCreate(params), outputScriptType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new, empty wallet with a randomly chosen seed and no transactions. Make sure to provide for sufficient
|
||||
* backup! Any keys will be derived from the seed. If you want to restore a wallet from disk instead, see
|
||||
* {@link #loadFromFile}.
|
||||
* @param params network parameters
|
||||
* @deprecated Use {@link #createDeterministic(NetworkParameters, ScriptType)}
|
||||
*/
|
||||
@Deprecated
|
||||
public Wallet(NetworkParameters params) {
|
||||
this(params, KeyChainGroup.builder(params).fromRandom(Script.ScriptType.P2PKH).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new, empty wallet with a randomly chosen seed and no transactions. Make sure to provide for sufficient
|
||||
* backup! Any keys will be derived from the seed. If you want to restore a wallet from disk instead, see
|
||||
* {@link #loadFromFile}.
|
||||
* @param outputScriptType type of addresses (aka output scripts) to generate for receiving
|
||||
*/
|
||||
public static Wallet createDeterministic(Context context, Script.ScriptType outputScriptType) {
|
||||
return new Wallet(context, KeyChainGroup.builder(context.getParams()).fromRandom(outputScriptType).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new, empty wallet with a randomly chosen seed and no transactions. Make sure to provide for sufficient
|
||||
* backup! Any keys will be derived from the seed. If you want to restore a wallet from disk instead, see
|
||||
* {@link #loadFromFile}.
|
||||
* @deprecated Use {@link #createDeterministic(Context, ScriptType)}
|
||||
*/
|
||||
@Deprecated
|
||||
public Wallet(Context context) {
|
||||
this(context, KeyChainGroup.builder(context.getParams()).fromRandom().build());
|
||||
this(context, KeyChainGroup.builder(context.getParams()).fromRandom(Script.ScriptType.P2PKH).build());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -274,14 +302,52 @@ public class Wallet extends BaseTaggableObject
|
|||
return new Wallet(params, KeyChainGroup.createBasic(params));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param params network parameters
|
||||
* @param seed deterministic seed
|
||||
* @param outputScriptType type of addresses (aka output scripts) to generate for receiving
|
||||
* @return a wallet from a deterministic seed with a default account path
|
||||
*/
|
||||
public static Wallet fromSeed(NetworkParameters params, DeterministicSeed seed,
|
||||
Script.ScriptType outputScriptType) {
|
||||
return fromSeed(params, seed, outputScriptType, KeyChainGroupStructure.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param params network parameters
|
||||
* @param seed deterministic seed
|
||||
* @param outputScriptType type of addresses (aka output scripts) to generate for receiving
|
||||
* @param structure structure for your wallet
|
||||
* @return a wallet from a deterministic seed with a default account path
|
||||
*/
|
||||
public static Wallet fromSeed(NetworkParameters params, DeterministicSeed seed, Script.ScriptType outputScriptType,
|
||||
KeyChainGroupStructure structure) {
|
||||
return new Wallet(params, KeyChainGroup.builder(params, structure).fromSeed(seed, outputScriptType).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param params network parameters
|
||||
* @param seed deterministic seed
|
||||
* @return a wallet from a deterministic seed with a
|
||||
* {@link DeterministicKeyChain#ACCOUNT_ZERO_PATH 0 hardened path}
|
||||
* @deprecated Use {@link #fromSeed(NetworkParameters, DeterministicSeed, ScriptType, KeyChainGroupStructure)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static Wallet fromSeed(NetworkParameters params, DeterministicSeed seed) {
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).build();
|
||||
return fromSeed(params, seed, Script.ScriptType.P2PKH);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param params network parameters
|
||||
* @param seed deterministic seed
|
||||
* @param outputScriptType type of addresses (aka output scripts) to generate for receiving
|
||||
* @param accountPath account path to generate receiving addresses on
|
||||
* @return an instance of a wallet from a deterministic seed.
|
||||
*/
|
||||
public static Wallet fromSeed(NetworkParameters params, DeterministicSeed seed, Script.ScriptType outputScriptType,
|
||||
ImmutableList<ChildNumber> accountPath) {
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).outputScriptType(outputScriptType)
|
||||
.accountPath(accountPath).build();
|
||||
return new Wallet(params, KeyChainGroup.builder(params).addChain(chain).build());
|
||||
}
|
||||
|
||||
|
@ -290,21 +356,34 @@ public class Wallet extends BaseTaggableObject
|
|||
* @param seed deterministic seed
|
||||
* @param accountPath account path
|
||||
* @return an instance of a wallet from a deterministic seed.
|
||||
* @deprecated Use {@link #fromSeed(NetworkParameters, DeterministicSeed, ScriptType, ImmutableList)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static Wallet fromSeed(NetworkParameters params, DeterministicSeed seed, ImmutableList<ChildNumber> accountPath) {
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).accountPath(accountPath).build();
|
||||
return new Wallet(params, KeyChainGroup.builder(params).addChain(chain).build());
|
||||
return fromSeed(params, seed, Script.ScriptType.P2PKH, accountPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given watching key. This HAS
|
||||
* to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}.
|
||||
*/
|
||||
public static Wallet fromWatchingKey(NetworkParameters params, DeterministicKey watchKey) {
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().watch(watchKey).build();
|
||||
public static Wallet fromWatchingKey(NetworkParameters params, DeterministicKey watchKey,
|
||||
Script.ScriptType outputScriptType) {
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().watch(watchKey).outputScriptType(outputScriptType)
|
||||
.build();
|
||||
return new Wallet(params, KeyChainGroup.builder(params).addChain(chain).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given watching key. This HAS
|
||||
* to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}.
|
||||
* @deprecated Use {@link #fromWatchingKey(NetworkParameters, DeterministicKey, ScriptType)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static Wallet fromWatchingKey(NetworkParameters params, DeterministicKey watchKey) {
|
||||
return fromWatchingKey(params, watchKey, Script.ScriptType.P2PKH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given watching key. The
|
||||
* account path is specified. The key is specified in base58 notation and the creation time of the key. If you don't
|
||||
|
@ -313,18 +392,30 @@ public class Wallet extends BaseTaggableObject
|
|||
public static Wallet fromWatchingKeyB58(NetworkParameters params, String watchKeyB58, long creationTimeSeconds) {
|
||||
final DeterministicKey watchKey = DeterministicKey.deserializeB58(null, watchKeyB58, params);
|
||||
watchKey.setCreationTimeSeconds(creationTimeSeconds);
|
||||
return fromWatchingKey(params, watchKey);
|
||||
return fromWatchingKey(params, watchKey, outputScriptTypeFromB58(params, watchKeyB58));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given spending key. This HAS
|
||||
* to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}. This wallet can also spend.
|
||||
*/
|
||||
public static Wallet fromSpendingKey(NetworkParameters params, DeterministicKey spendKey) {
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().spend(spendKey).build();
|
||||
public static Wallet fromSpendingKey(NetworkParameters params, DeterministicKey spendKey,
|
||||
Script.ScriptType outputScriptType) {
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().spend(spendKey).outputScriptType(outputScriptType)
|
||||
.build();
|
||||
return new Wallet(params, KeyChainGroup.builder(params).addChain(chain).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given spending key. This HAS
|
||||
* to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}. This wallet can also spend.
|
||||
* @deprecated Use {@link #fromSpendingKey(NetworkParameters, DeterministicKey, ScriptType)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static Wallet fromSpendingKey(NetworkParameters params, DeterministicKey spendKey) {
|
||||
return fromSpendingKey(params, spendKey, Script.ScriptType.P2PKH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wallet that tracks payments to and from the HD key hierarchy rooted by the given spending key.
|
||||
* The key is specified in base58 notation and the creation time of the key. If you don't know the creation time,
|
||||
|
@ -333,7 +424,7 @@ public class Wallet extends BaseTaggableObject
|
|||
public static Wallet fromSpendingKeyB58(NetworkParameters params, String spendingKeyB58, long creationTimeSeconds) {
|
||||
final DeterministicKey spendKey = DeterministicKey.deserializeB58(null, spendingKeyB58, params);
|
||||
spendKey.setCreationTimeSeconds(creationTimeSeconds);
|
||||
return fromSpendingKey(params, spendKey);
|
||||
return fromSpendingKey(params, spendKey, outputScriptTypeFromB58(params, spendingKeyB58));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -341,11 +432,12 @@ public class Wallet extends BaseTaggableObject
|
|||
* to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}.
|
||||
*/
|
||||
public static Wallet fromMasterKey(NetworkParameters params, DeterministicKey masterKey,
|
||||
ChildNumber accountNumber) {
|
||||
Script.ScriptType outputScriptType, ChildNumber accountNumber) {
|
||||
DeterministicKey accountKey = HDKeyDerivation.deriveChildKey(masterKey, accountNumber);
|
||||
accountKey = accountKey.dropParent();
|
||||
accountKey.setCreationTimeSeconds(masterKey.getCreationTimeSeconds());
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().spend(accountKey).build();
|
||||
DeterministicKeyChain chain = DeterministicKeyChain.builder().spend(accountKey)
|
||||
.outputScriptType(outputScriptType).build();
|
||||
return new Wallet(params, KeyChainGroup.builder(params).addChain(chain).build());
|
||||
}
|
||||
|
||||
|
@ -361,6 +453,16 @@ public class Wallet extends BaseTaggableObject
|
|||
return new Wallet(params, group);
|
||||
}
|
||||
|
||||
private static Script.ScriptType outputScriptTypeFromB58(NetworkParameters params, String base58) {
|
||||
int header = ByteBuffer.wrap(Base58.decodeChecked(base58)).getInt();
|
||||
if (header == params.getBip32HeaderP2PKHpub() || header == params.getBip32HeaderP2PKHpriv())
|
||||
return Script.ScriptType.P2PKH;
|
||||
else if (header == params.getBip32HeaderP2WPKHpub() || header == params.getBip32HeaderP2WPKHpriv())
|
||||
return Script.ScriptType.P2WPKH;
|
||||
else
|
||||
throw new IllegalArgumentException(base58.substring(0, 4));
|
||||
}
|
||||
|
||||
public Wallet(NetworkParameters params, KeyChainGroup keyChainGroup) {
|
||||
this(Context.getOrCreate(params), keyChainGroup);
|
||||
}
|
||||
|
@ -590,11 +692,18 @@ public class Wallet extends BaseTaggableObject
|
|||
* {@link #currentReceiveKey()} or {@link #currentReceiveAddress()}.
|
||||
*/
|
||||
public List<Address> getIssuedReceiveAddresses() {
|
||||
final List<ECKey> keys = getIssuedReceiveKeys();
|
||||
List<Address> addresses = new ArrayList<>(keys.size());
|
||||
for (ECKey key : keys)
|
||||
addresses.add(LegacyAddress.fromKey(getParams(), key));
|
||||
return addresses;
|
||||
keyChainGroupLock.lock();
|
||||
try {
|
||||
final DeterministicKeyChain activeKeyChain = keyChainGroup.getActiveKeyChain();
|
||||
final List<ECKey> keys = activeKeyChain.getIssuedReceiveKeys();
|
||||
final Script.ScriptType outputScriptType = activeKeyChain.getOutputScriptType();
|
||||
List<Address> addresses = new ArrayList<>(keys.size());
|
||||
for (ECKey key : keys)
|
||||
addresses.add(Address.fromKey(getParams(), key, outputScriptType));
|
||||
return addresses;
|
||||
} finally {
|
||||
keyChainGroupLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1015,10 +1124,10 @@ public class Wallet extends BaseTaggableObject
|
|||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public ECKey findKeyFromPubKeyHash(byte[] pubKeyHash) {
|
||||
public ECKey findKeyFromPubKeyHash(byte[] pubKeyHash, @Nullable Script.ScriptType scriptType) {
|
||||
keyChainGroupLock.lock();
|
||||
try {
|
||||
return keyChainGroup.findKeyFromPubKeyHash(pubKeyHash);
|
||||
return keyChainGroup.findKeyFromPubKeyHash(pubKeyHash, scriptType);
|
||||
} finally {
|
||||
keyChainGroupLock.unlock();
|
||||
}
|
||||
|
@ -1040,14 +1149,14 @@ public class Wallet extends BaseTaggableObject
|
|||
public boolean isAddressMine(Address address) {
|
||||
final ScriptType scriptType = address.getOutputScriptType();
|
||||
if (scriptType == ScriptType.P2PKH || scriptType == ScriptType.P2WPKH)
|
||||
return isPubKeyHashMine(address.getHash());
|
||||
return isPubKeyHashMine(address.getHash(), scriptType);
|
||||
else
|
||||
throw new IllegalArgumentException(address.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPubKeyHashMine(byte[] pubKeyHash) {
|
||||
return findKeyFromPubKeyHash(pubKeyHash) != null;
|
||||
public boolean isPubKeyHashMine(byte[] pubKeyHash, @Nullable Script.ScriptType scriptType) {
|
||||
return findKeyFromPubKeyHash(pubKeyHash, scriptType) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1067,7 +1176,7 @@ public class Wallet extends BaseTaggableObject
|
|||
public ECKey findKeyFromAddress(Address address) {
|
||||
final ScriptType scriptType = address.getOutputScriptType();
|
||||
if (scriptType == ScriptType.P2PKH || scriptType == ScriptType.P2WPKH)
|
||||
return findKeyFromPubKeyHash(address.getHash());
|
||||
return findKeyFromPubKeyHash(address.getHash(), scriptType);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
@ -1132,6 +1241,9 @@ public class Wallet extends BaseTaggableObject
|
|||
LegacyAddress a = LegacyAddress.fromScriptHash(tx.getParams(),
|
||||
ScriptPattern.extractHashFromPayToScriptHash(script));
|
||||
keyChainGroup.markP2SHAddressAsUsed(a);
|
||||
} else if (ScriptPattern.isPayToWitnessHash(script)) {
|
||||
byte[] pubkeyHash = ScriptPattern.extractHashFromPayToWitnessHash(script);
|
||||
keyChainGroup.markPubKeyHashAsUsed(pubkeyHash);
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
// Just means we didn't understand the output of this transaction: ignore it.
|
||||
|
@ -4082,16 +4194,19 @@ public class Wallet extends BaseTaggableObject
|
|||
int numInputs = tx.getInputs().size();
|
||||
for (int i = 0; i < numInputs; i++) {
|
||||
TransactionInput txIn = tx.getInput(i);
|
||||
if (txIn.getConnectedOutput() == null) {
|
||||
TransactionOutput connectedOutput = txIn.getConnectedOutput();
|
||||
if (connectedOutput == null) {
|
||||
// Missing connected output, assuming already signed.
|
||||
continue;
|
||||
}
|
||||
Script scriptPubKey = connectedOutput.getScriptPubKey();
|
||||
|
||||
try {
|
||||
// We assume if its already signed, its hopefully got a SIGHASH type that will not invalidate when
|
||||
// we sign missing pieces (to check this would require either assuming any signatures are signing
|
||||
// standard output types or a way to get processed signatures out of script execution)
|
||||
txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey());
|
||||
txIn.getScriptSig().correctlySpends(tx, i, txIn.getWitness(), connectedOutput.getValue(),
|
||||
connectedOutput.getScriptPubKey(), Script.ALL_VERIFY_FLAGS);
|
||||
log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", i);
|
||||
continue;
|
||||
} catch (ScriptException e) {
|
||||
|
@ -4099,10 +4214,10 @@ public class Wallet extends BaseTaggableObject
|
|||
// Expected.
|
||||
}
|
||||
|
||||
Script scriptPubKey = txIn.getConnectedOutput().getScriptPubKey();
|
||||
RedeemData redeemData = txIn.getConnectedRedeemData(maybeDecryptingKeyBag);
|
||||
checkNotNull(redeemData, "Transaction exists in wallet that we cannot redeem: %s", txIn.getOutpoint().getHash());
|
||||
txIn.setScriptSig(scriptPubKey.createEmptyInputScript(redeemData.keys.get(0), redeemData.redeemScript));
|
||||
txIn.setWitness(scriptPubKey.createEmptyWitness(redeemData.keys.get(0)));
|
||||
}
|
||||
|
||||
TransactionSigner.ProposedTransaction proposal = new TransactionSigner.ProposedTransaction(tx);
|
||||
|
@ -4181,8 +4296,13 @@ public class Wallet extends BaseTaggableObject
|
|||
RedeemData data = findRedeemDataFromScriptHash(ScriptPattern.extractHashFromPayToScriptHash(script));
|
||||
return data != null && canSignFor(data.redeemScript);
|
||||
} else if (ScriptPattern.isPayToPubKeyHash(script)) {
|
||||
ECKey key = findKeyFromPubKeyHash(ScriptPattern.extractHashFromPayToPubKeyHash(script));
|
||||
ECKey key = findKeyFromPubKeyHash(ScriptPattern.extractHashFromPayToPubKeyHash(script),
|
||||
Script.ScriptType.P2PKH);
|
||||
return key != null && (key.isEncrypted() || key.hasPrivKey());
|
||||
} else if (ScriptPattern.isPayToWitnessPubKeyHash(script)) {
|
||||
ECKey key = findKeyFromPubKeyHash(ScriptPattern.extractHashFromPayToWitnessHash(script),
|
||||
Script.ScriptType.P2WPKH);
|
||||
return key != null && (key.isEncrypted() || key.hasPrivKey()) && key.isCompressed();
|
||||
} else if (ScriptPattern.isSentToMultisig(script)) {
|
||||
for (ECKey pubkey : script.getPubKeys()) {
|
||||
ECKey key = findKeyFromPubKey(pubkey.getPubKey());
|
||||
|
@ -4645,7 +4765,12 @@ public class Wallet extends BaseTaggableObject
|
|||
// before calling, but because this is public API we must still lock again regardless.
|
||||
keyChainGroupLock.lock();
|
||||
try {
|
||||
return !watchedScripts.isEmpty();
|
||||
if (!watchedScripts.isEmpty())
|
||||
return true;
|
||||
for (DeterministicKeyChain chain : keyChainGroup.chains)
|
||||
if (chain.getOutputScriptType() == Script.ScriptType.P2WPKH)
|
||||
return true;
|
||||
return false;
|
||||
} finally {
|
||||
keyChainGroupLock.unlock();
|
||||
}
|
||||
|
@ -4701,7 +4826,8 @@ public class Wallet extends BaseTaggableObject
|
|||
// Returns true if the output is one that won't be selected by a data element matching in the scriptSig.
|
||||
private boolean isTxOutputBloomFilterable(TransactionOutput out) {
|
||||
Script script = out.getScriptPubKey();
|
||||
boolean isScriptTypeSupported = ScriptPattern.isPayToPubKey(script) || ScriptPattern.isPayToScriptHash(script);
|
||||
boolean isScriptTypeSupported = ScriptPattern.isPayToPubKey(script) || ScriptPattern.isPayToScriptHash(script)
|
||||
|| ScriptPattern.isPayToWitnessPubKeyHash(script) || ScriptPattern.isPayToWitnessScriptHash(script);
|
||||
return (isScriptTypeSupported && myUnspents.contains(out)) || watchedScripts.contains(script);
|
||||
}
|
||||
|
||||
|
@ -4959,7 +5085,12 @@ public class Wallet extends BaseTaggableObject
|
|||
ECKey key = null;
|
||||
Script redeemScript = null;
|
||||
if (ScriptPattern.isPayToPubKeyHash(script)) {
|
||||
key = findKeyFromPubKeyHash(ScriptPattern.extractHashFromPayToPubKeyHash(script));
|
||||
key = findKeyFromPubKeyHash(ScriptPattern.extractHashFromPayToPubKeyHash(script),
|
||||
Script.ScriptType.P2PKH);
|
||||
checkNotNull(key, "Coin selection includes unspendable outputs");
|
||||
} else if (ScriptPattern.isPayToWitnessPubKeyHash(script)) {
|
||||
key = findKeyFromPubKeyHash(ScriptPattern.extractHashFromPayToWitnessHash(script),
|
||||
Script.ScriptType.P2WPKH);
|
||||
checkNotNull(key, "Coin selection includes unspendable outputs");
|
||||
} else if (ScriptPattern.isPayToScriptHash(script)) {
|
||||
redeemScript = findRedeemDataFromScriptHash(ScriptPattern.extractHashFromPayToScriptHash(script)).redeemScript;
|
||||
|
|
|
@ -137,6 +137,13 @@ message Key {
|
|||
|
||||
// The path to the root. Only applicable to a DETERMINISTIC_MNEMONIC key entry.
|
||||
repeated uint32 account_path = 10 [packed = true];
|
||||
|
||||
enum OutputScriptType {
|
||||
P2PKH = 1;
|
||||
P2WPKH = 2;
|
||||
}
|
||||
// Type of addresses (aka output scripts) to generate for receiving.
|
||||
optional OutputScriptType output_script_type = 11;
|
||||
}
|
||||
|
||||
message Script {
|
||||
|
|
|
@ -22,8 +22,10 @@ import com.google.common.collect.*;
|
|||
import com.google.common.util.concurrent.*;
|
||||
import org.bitcoinj.core.listeners.*;
|
||||
import org.bitcoinj.net.discovery.*;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.testing.*;
|
||||
import org.bitcoinj.utils.*;
|
||||
import org.bitcoinj.wallet.KeyChainGroupStructure;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
@ -781,7 +783,7 @@ public class PeerGroupTest extends TestWithPeerGroup {
|
|||
final int NUM_KEYS = 9;
|
||||
|
||||
// First, grab a load of keys from the wallet, and then recreate it so it forgets that those keys were issued.
|
||||
Wallet shadow = Wallet.fromSeed(wallet.getParams(), wallet.getKeyChainSeed());
|
||||
Wallet shadow = Wallet.fromSeed(wallet.getParams(), wallet.getKeyChainSeed(), Script.ScriptType.P2PKH);
|
||||
List<ECKey> keys = new ArrayList<>(NUM_KEYS);
|
||||
for (int i = 0; i < NUM_KEYS; i++) {
|
||||
keys.add(shadow.freshReceiveKey());
|
||||
|
|
|
@ -324,18 +324,21 @@ public class TransactionTest {
|
|||
assertEquals(txHex, HEX.encode(tx.bitcoinSerialize()));
|
||||
assertEquals(2, tx.getInputs().size());
|
||||
assertEquals(2, tx.getOutputs().size());
|
||||
TransactionInput txIn0 = tx.getInput(0);
|
||||
TransactionInput txIn1 = tx.getInput(1);
|
||||
|
||||
ECKey key0 = ECKey.fromPrivate(
|
||||
HEX.decode("bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"));
|
||||
ECKey key0 = ECKey.fromPrivate(HEX.decode("bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"));
|
||||
Script scriptPubKey0 = ScriptBuilder.createP2PKOutputScript(key0);
|
||||
assertEquals("2103c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432ac",
|
||||
HEX.encode(ScriptBuilder.createP2PKOutputScript(key0).getProgram()));
|
||||
ECKey key1 = ECKey.fromPrivate(
|
||||
HEX.decode("619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"));
|
||||
assertEquals("025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357",
|
||||
key1.getPublicKeyAsHex());
|
||||
HEX.encode(scriptPubKey0.getProgram()));
|
||||
ECKey key1 = ECKey.fromPrivate(HEX.decode("619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"));
|
||||
assertEquals("025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357", key1.getPublicKeyAsHex());
|
||||
Script scriptPubKey1 = ScriptBuilder.createP2WPKHOutputScript(key1);
|
||||
assertEquals("00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1", HEX.encode(scriptPubKey1.getProgram()));
|
||||
txIn1.connect(new TransactionOutput(TESTNET, null, Coin.COIN.multiply(6), scriptPubKey1.getProgram()));
|
||||
|
||||
TransactionSignature txSig0 = tx.calculateSignature(0, key0,
|
||||
ScriptBuilder.createP2PKOutputScript(key0).getProgram(),
|
||||
scriptPubKey0,
|
||||
Transaction.SigHash.ALL, false);
|
||||
assertEquals("30450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01",
|
||||
HEX.encode(txSig0.encodeToBitcoin()));
|
||||
|
@ -345,24 +348,20 @@ public class TransactionTest {
|
|||
HEX.encode(scriptCode.getProgram()));
|
||||
|
||||
TransactionSignature txSig1 = tx.calculateWitnessSignature(1, key1,
|
||||
scriptCode, Coin.COIN.multiply(6),
|
||||
scriptCode, txIn1.getValue(),
|
||||
Transaction.SigHash.ALL, false);
|
||||
assertEquals("304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee"
|
||||
+ "01",
|
||||
HEX.encode(txSig1.encodeToBitcoin()));
|
||||
|
||||
TransactionInput txIn0 = tx.getInput(0);
|
||||
txIn0.setScriptSig(new ScriptBuilder()
|
||||
.data(txSig0.encodeToBitcoin())
|
||||
.build());
|
||||
assertFalse(correctlySpends(txIn0, scriptPubKey0, 0));
|
||||
txIn0.setScriptSig(new ScriptBuilder().data(txSig0.encodeToBitcoin()).build());
|
||||
assertTrue(correctlySpends(txIn0, scriptPubKey0, 0));
|
||||
|
||||
TransactionWitness witness = new TransactionWitness(2);
|
||||
witness.setPush(0, txSig1.encodeToBitcoin());
|
||||
witness.setPush(1, key1.getPubKey());
|
||||
|
||||
TransactionInput txIn1 = tx.getInput(1);
|
||||
txIn1.setWitness(witness);
|
||||
assertFalse(correctlySpends(txIn1, scriptPubKey1, 1));
|
||||
txIn1.setWitness(TransactionWitness.redeemP2WPKH(txSig1, key1));
|
||||
// no redeem script for p2wpkh
|
||||
assertTrue(correctlySpends(txIn1, scriptPubKey1, 1));
|
||||
|
||||
String signedTxHex = "01000000" // version
|
||||
+ "00" // marker
|
||||
|
@ -400,6 +399,7 @@ public class TransactionTest {
|
|||
assertEquals(txHex, HEX.encode(tx.bitcoinSerialize()));
|
||||
assertEquals(1, tx.getInputs().size());
|
||||
assertEquals(2, tx.getOutputs().size());
|
||||
TransactionInput txIn = tx.getInput(0);
|
||||
|
||||
ECKey key = ECKey.fromPrivate(
|
||||
HEX.decode("eb696a065ef48a2192da5b28b694f87544b30fae8327c4510137a922f32c6dcf"));
|
||||
|
@ -415,10 +415,7 @@ public class TransactionTest {
|
|||
assertEquals("a9144733f37cf4db86fbc2efed2500b4f4e49f31202387",
|
||||
HEX.encode(scriptPubKey.getProgram()));
|
||||
|
||||
Script scriptCode = new ScriptBuilder()
|
||||
.data(ScriptBuilder.createOutputScript(LegacyAddress.fromKey(TESTNET, key))
|
||||
.getProgram())
|
||||
.build();
|
||||
Script scriptCode = new ScriptBuilder().data(ScriptBuilder.createP2PKHOutputScript(key).getProgram()).build();
|
||||
assertEquals("1976a91479091972186c449eb1ded22b78e40d009bdf008988ac",
|
||||
HEX.encode(scriptCode.getProgram()));
|
||||
|
||||
|
@ -429,13 +426,10 @@ public class TransactionTest {
|
|||
+ "01",
|
||||
HEX.encode(txSig.encodeToBitcoin()));
|
||||
|
||||
TransactionWitness witness = new TransactionWitness(2);
|
||||
witness.setPush(0, txSig.encodeToBitcoin());
|
||||
witness.setPush(1, key.getPubKey());
|
||||
|
||||
TransactionInput txIn = tx.getInput(0);
|
||||
txIn.setWitness(witness);
|
||||
assertFalse(correctlySpends(txIn, scriptPubKey, 0));
|
||||
txIn.setWitness(TransactionWitness.redeemP2WPKH(txSig, key));
|
||||
txIn.setScriptSig(new ScriptBuilder().data(redeemScript.getProgram()).build());
|
||||
assertTrue(correctlySpends(txIn, scriptPubKey, 0));
|
||||
|
||||
String signedTxHex = "01000000" // version
|
||||
+ "00" // marker
|
||||
|
@ -455,6 +449,16 @@ public class TransactionTest {
|
|||
assertEquals(signedTxHex, HEX.encode(tx.bitcoinSerialize()));
|
||||
}
|
||||
|
||||
private boolean correctlySpends(TransactionInput txIn, Script scriptPubKey, int inputIndex) {
|
||||
try {
|
||||
txIn.getScriptSig().correctlySpends(txIn.getParentTransaction(), inputIndex, txIn.getWitness(),
|
||||
txIn.getValue(), scriptPubKey, Script.ALL_VERIFY_FLAGS);
|
||||
return true;
|
||||
} catch (ScriptException x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToStringWhenLockTimeIsSpecifiedInBlockHeight() {
|
||||
Transaction tx = FakeTxBuilder.createFakeTx(UNITTEST);
|
||||
|
|
|
@ -109,12 +109,10 @@ public class WalletProtobufSerializerTest {
|
|||
Wallet wallet1 = roundTrip(myWallet);
|
||||
assertEquals(0, wallet1.getTransactions(true).size());
|
||||
assertEquals(Coin.ZERO, wallet1.getBalance());
|
||||
assertArrayEquals(myKey.getPubKey(),
|
||||
wallet1.findKeyFromPubKeyHash(myKey.getPubKeyHash()).getPubKey());
|
||||
assertArrayEquals(myKey.getPrivKeyBytes(),
|
||||
wallet1.findKeyFromPubKeyHash(myKey.getPubKeyHash()).getPrivKeyBytes());
|
||||
assertEquals(myKey.getCreationTimeSeconds(),
|
||||
wallet1.findKeyFromPubKeyHash(myKey.getPubKeyHash()).getCreationTimeSeconds());
|
||||
ECKey foundKey = wallet1.findKeyFromPubKeyHash(myKey.getPubKeyHash(), null);
|
||||
assertArrayEquals(myKey.getPubKey(), foundKey.getPubKey());
|
||||
assertArrayEquals(myKey.getPrivKeyBytes(), foundKey.getPrivKeyBytes());
|
||||
assertEquals(myKey.getCreationTimeSeconds(), foundKey.getCreationTimeSeconds());
|
||||
assertEquals(mScriptCreationTime,
|
||||
wallet1.getWatchedScripts().get(0).getCreationTimeSeconds());
|
||||
assertEquals(1, wallet1.getWatchedScripts().size());
|
||||
|
@ -197,8 +195,9 @@ public class WalletProtobufSerializerTest {
|
|||
myWallet = new Wallet(UNITTEST);
|
||||
myWallet.importKey(myKey);
|
||||
Wallet wallet1 = roundTrip(myWallet);
|
||||
assertArrayEquals(myKey.getPubKey(), wallet1.findKeyFromPubKeyHash(myKey.getPubKeyHash()).getPubKey());
|
||||
assertArrayEquals(myKey.getPrivKeyBytes(), wallet1.findKeyFromPubKeyHash(myKey.getPubKeyHash()).getPrivKeyBytes());
|
||||
ECKey foundKey = wallet1.findKeyFromPubKeyHash(myKey.getPubKeyHash(), null);
|
||||
assertArrayEquals(myKey.getPubKey(), foundKey.getPubKey());
|
||||
assertArrayEquals(myKey.getPrivKeyBytes(), foundKey.getPrivKeyBytes());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,12 +332,10 @@ public class WalletProtobufSerializerTest {
|
|||
Wallet wallet1 = roundTrip(myWallet);
|
||||
assertEquals(0, wallet1.getTransactions(true).size());
|
||||
assertEquals(Coin.ZERO, wallet1.getBalance());
|
||||
assertArrayEquals(myKey.getPubKey(),
|
||||
wallet1.findKeyFromPubKeyHash(myKey.getPubKeyHash()).getPubKey());
|
||||
assertArrayEquals(myKey.getPrivKeyBytes(),
|
||||
wallet1.findKeyFromPubKeyHash(myKey.getPubKeyHash()).getPrivKeyBytes());
|
||||
assertEquals(myKey.getCreationTimeSeconds(),
|
||||
wallet1.findKeyFromPubKeyHash(myKey.getPubKeyHash()).getCreationTimeSeconds());
|
||||
ECKey foundKey = wallet1.findKeyFromPubKeyHash(myKey.getPubKeyHash(), null);
|
||||
assertArrayEquals(myKey.getPubKey(), foundKey.getPubKey());
|
||||
assertArrayEquals(myKey.getPrivKeyBytes(), foundKey.getPrivKeyBytes());
|
||||
assertEquals(myKey.getCreationTimeSeconds(), foundKey.getCreationTimeSeconds());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.bitcoinj.core.Utils;
|
|||
import org.bitcoinj.crypto.*;
|
||||
import org.bitcoinj.params.MainNetParams;
|
||||
import org.bitcoinj.params.UnitTestParams;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.utils.BriefLogFormatter;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.bitcoinj.wallet.listeners.AbstractKeyChainEventListener;
|
||||
|
@ -50,6 +51,7 @@ import static org.junit.Assert.*;
|
|||
|
||||
public class DeterministicKeyChainTest {
|
||||
private DeterministicKeyChain chain;
|
||||
private DeterministicKeyChain segwitChain;
|
||||
private DeterministicKeyChain bip44chain;
|
||||
private final byte[] ENTROPY = Sha256Hash.hash("don't use a string seed like this in real life".getBytes());
|
||||
private static final NetworkParameters UNITTEST = UnitTestParams.get();
|
||||
|
@ -63,13 +65,17 @@ public class DeterministicKeyChainTest {
|
|||
// You should use a random seed instead. The secs constant comes from the unit test file, so we can compare
|
||||
// serialized data properly.
|
||||
long secs = 1389353062L;
|
||||
chain = DeterministicKeyChain.builder().entropy(ENTROPY, secs).build();
|
||||
chain = DeterministicKeyChain.builder().entropy(ENTROPY, secs)
|
||||
.accountPath(DeterministicKeyChain.ACCOUNT_ZERO_PATH).outputScriptType(Script.ScriptType.P2PKH).build();
|
||||
chain.setLookaheadSize(10);
|
||||
assertEquals(secs, checkNotNull(chain.getSeed()).getCreationTimeSeconds());
|
||||
|
||||
bip44chain = DeterministicKeyChain.builder().entropy(ENTROPY, secs).accountPath(BIP44_ACCOUNT_ONE_PATH).build();
|
||||
segwitChain = DeterministicKeyChain.builder().entropy(ENTROPY, secs)
|
||||
.accountPath(DeterministicKeyChain.ACCOUNT_ONE_PATH).outputScriptType(Script.ScriptType.P2WPKH).build();
|
||||
segwitChain.setLookaheadSize(10);
|
||||
|
||||
bip44chain = DeterministicKeyChain.builder().entropy(ENTROPY, secs).accountPath(BIP44_ACCOUNT_ONE_PATH)
|
||||
.outputScriptType(Script.ScriptType.P2PKH).build();
|
||||
bip44chain.setLookaheadSize(10);
|
||||
assertEquals(secs, checkNotNull(bip44chain.getSeed()).getCreationTimeSeconds());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -167,7 +173,8 @@ public class DeterministicKeyChainTest {
|
|||
// Check that we get the right events at the right time.
|
||||
final List<List<ECKey>> listenerKeys = Lists.newArrayList();
|
||||
long secs = 1389353062L;
|
||||
chain = DeterministicKeyChain.builder().entropy(ENTROPY, secs).build();
|
||||
chain = DeterministicKeyChain.builder().entropy(ENTROPY, secs).outputScriptType(Script.ScriptType.P2PKH)
|
||||
.build();
|
||||
chain.addEventListener(new AbstractKeyChainEventListener() {
|
||||
@Override
|
||||
public void onKeysAdded(List<ECKey> keys) {
|
||||
|
@ -247,6 +254,43 @@ public class DeterministicKeyChainTest {
|
|||
assertEquals(oldLookaheadSize, chain.getLookaheadSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeSegwitUnencrypted() throws UnreadableWalletException {
|
||||
segwitChain.maybeLookAhead();
|
||||
DeterministicKey key1 = segwitChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||
DeterministicKey key2 = segwitChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||
DeterministicKey key3 = segwitChain.getKey(KeyChain.KeyPurpose.CHANGE);
|
||||
List<Protos.Key> keys = segwitChain.serializeToProtobuf();
|
||||
// 1 mnemonic/seed, 1 master key, 1 account key, 2 internal keys, 3 derived, 20 lookahead and 5 lookahead threshold.
|
||||
int numItems =
|
||||
1 // mnemonic/seed
|
||||
+ 1 // master key
|
||||
+ 1 // account key
|
||||
+ 2 // ext/int parent keys
|
||||
+ (segwitChain.getLookaheadSize() + segwitChain.getLookaheadThreshold()) * 2 // lookahead zone on each chain
|
||||
;
|
||||
assertEquals(numItems, keys.size());
|
||||
|
||||
// Get another key that will be lost during round-tripping, to ensure we can derive it again.
|
||||
DeterministicKey key4 = segwitChain.getKey(KeyChain.KeyPurpose.CHANGE);
|
||||
|
||||
final String EXPECTED_SERIALIZATION = checkSerialization(keys, "deterministic-wallet-segwit-serialization.txt");
|
||||
|
||||
// Round trip the data back and forth to check it is preserved.
|
||||
int oldLookaheadSize = segwitChain.getLookaheadSize();
|
||||
segwitChain = DeterministicKeyChain.fromProtobuf(keys, null).get(0);
|
||||
assertEquals(EXPECTED_SERIALIZATION, protoToString(segwitChain.serializeToProtobuf()));
|
||||
assertEquals(key1, segwitChain.findKeyFromPubHash(key1.getPubKeyHash()));
|
||||
assertEquals(key2, segwitChain.findKeyFromPubHash(key2.getPubKeyHash()));
|
||||
assertEquals(key3, segwitChain.findKeyFromPubHash(key3.getPubKeyHash()));
|
||||
assertEquals(key4, segwitChain.getKey(KeyChain.KeyPurpose.CHANGE));
|
||||
key1.sign(Sha256Hash.ZERO_HASH);
|
||||
key2.sign(Sha256Hash.ZERO_HASH);
|
||||
key3.sign(Sha256Hash.ZERO_HASH);
|
||||
key4.sign(Sha256Hash.ZERO_HASH);
|
||||
assertEquals(oldLookaheadSize, segwitChain.getLookaheadSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeUnencryptedBIP44() throws UnreadableWalletException {
|
||||
bip44chain.maybeLookAhead();
|
||||
|
@ -358,7 +402,8 @@ public class DeterministicKeyChainTest {
|
|||
assertEquals("xpub69KR9epSNBM59KLuasxMU5CyKytMJjBP5HEZ5p8YoGUCpM6cM9hqxB9DDPCpUUtqmw5duTckvPfwpoWGQUFPmRLpxs5jYiTf2u6xRMcdhDf", pub58);
|
||||
watchingKey = DeterministicKey.deserializeB58(null, pub58, MAINNET);
|
||||
watchingKey.setCreationTimeSeconds(100000);
|
||||
chain = DeterministicKeyChain.builder().watch(watchingKey).build();
|
||||
chain = DeterministicKeyChain.builder().watch(watchingKey).outputScriptType(chain.getOutputScriptType())
|
||||
.build();
|
||||
assertEquals(100000, chain.getEarliestKeyCreationTime());
|
||||
chain.setLookaheadSize(10);
|
||||
chain.maybeLookAhead();
|
||||
|
@ -394,7 +439,8 @@ public class DeterministicKeyChainTest {
|
|||
DeterministicKey watchingKey = bip44chain.getWatchingKey();
|
||||
watchingKey = watchingKey.dropPrivateBytes().dropParent();
|
||||
watchingKey.setCreationTimeSeconds(100000);
|
||||
chain = DeterministicKeyChain.builder().watch(watchingKey).build();
|
||||
chain = DeterministicKeyChain.builder().watch(watchingKey).outputScriptType(bip44chain.getOutputScriptType())
|
||||
.build();
|
||||
assertEquals(100000, chain.getEarliestKeyCreationTime());
|
||||
chain.setLookaheadSize(10);
|
||||
chain.maybeLookAhead();
|
||||
|
@ -435,7 +481,8 @@ public class DeterministicKeyChainTest {
|
|||
assertEquals("xpub69KR9epJ2Wp6ywiv4Xu5WfBUpX4GLu6D5NUMd4oUkCFoZoRNyk3ZCxfKPDkkGvCPa16dPgEdY63qoyLqEa5TQQy1nmfSmgWcagRzimyV7uA", pub58);
|
||||
watchingKey = DeterministicKey.deserializeB58(null, pub58, MAINNET);
|
||||
watchingKey.setCreationTimeSeconds(100000);
|
||||
chain = DeterministicKeyChain.builder().watch(watchingKey).build();
|
||||
chain = DeterministicKeyChain.builder().watch(watchingKey).outputScriptType(chain1.getOutputScriptType())
|
||||
.build();
|
||||
assertEquals(accountOne, chain.getAccountPath());
|
||||
assertEquals(100000, chain.getEarliestKeyCreationTime());
|
||||
chain.setLookaheadSize(10);
|
||||
|
@ -461,6 +508,46 @@ public class DeterministicKeyChainTest {
|
|||
assertEquals(key4.getPubKeyPoint(), rekey4.getPubKeyPoint());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void watchingSegwitChain() throws UnreadableWalletException {
|
||||
Utils.setMockClock();
|
||||
DeterministicKey key1 = segwitChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||
DeterministicKey key2 = segwitChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||
DeterministicKey key3 = segwitChain.getKey(KeyChain.KeyPurpose.CHANGE);
|
||||
DeterministicKey key4 = segwitChain.getKey(KeyChain.KeyPurpose.CHANGE);
|
||||
|
||||
DeterministicKey watchingKey = segwitChain.getWatchingKey();
|
||||
final String pub58 = watchingKey.serializePubB58(MAINNET, segwitChain.getOutputScriptType());
|
||||
assertEquals("zpub6nywkzAGfYS2siEfJtm9mo3hwDk8eUtL8EJ31XeWSd7C7x7esnfMMWmWiSs8od5jRt11arTjKLLbxCXuWNSXcxpi9PMSAphMt2ZE2gLnXGE", pub58);
|
||||
watchingKey = DeterministicKey.deserializeB58(null, pub58, MAINNET);
|
||||
watchingKey.setCreationTimeSeconds(100000);
|
||||
segwitChain = DeterministicKeyChain.builder().watch(watchingKey)
|
||||
.outputScriptType(segwitChain.getOutputScriptType()).build();
|
||||
assertEquals(100000, segwitChain.getEarliestKeyCreationTime());
|
||||
segwitChain.setLookaheadSize(10);
|
||||
segwitChain.maybeLookAhead();
|
||||
|
||||
assertEquals(key1.getPubKeyPoint(), segwitChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).getPubKeyPoint());
|
||||
assertEquals(key2.getPubKeyPoint(), segwitChain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).getPubKeyPoint());
|
||||
final DeterministicKey key = segwitChain.getKey(KeyChain.KeyPurpose.CHANGE);
|
||||
assertEquals(key3.getPubKeyPoint(), key.getPubKeyPoint());
|
||||
try {
|
||||
// Can't sign with a key from a watching chain.
|
||||
key.sign(Sha256Hash.ZERO_HASH);
|
||||
fail();
|
||||
} catch (ECKey.MissingPrivateKeyException e) {
|
||||
// Ignored.
|
||||
}
|
||||
// Test we can serialize and deserialize a watching chain OK.
|
||||
List<Protos.Key> serialization = segwitChain.serializeToProtobuf();
|
||||
checkSerialization(serialization, "watching-wallet-p2wpkh-serialization.txt");
|
||||
final DeterministicKeyChain chain = DeterministicKeyChain.fromProtobuf(serialization, null).get(0);
|
||||
assertEquals(DeterministicKeyChain.ACCOUNT_ONE_PATH, chain.getAccountPath());
|
||||
assertEquals(Script.ScriptType.P2WPKH, chain.getOutputScriptType());
|
||||
final DeterministicKey rekey4 = segwitChain.getKey(KeyChain.KeyPurpose.CHANGE);
|
||||
assertEquals(key4.getPubKeyPoint(), rekey4.getPubKeyPoint());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spendingChain() throws UnreadableWalletException {
|
||||
Utils.setMockClock();
|
||||
|
@ -475,7 +562,8 @@ public class DeterministicKeyChainTest {
|
|||
assertEquals("xprv9vL4k9HYXonmvqGSUrRM6wGEmx3ruGTXi4JxHRiwEvwDwYmTocPbQNpjN89gpqPrFofmfvALwgnNFBCH2grse1YDf8ERAwgdvbjRtoMfsbV", prv58);
|
||||
watchingKey = DeterministicKey.deserializeB58(null, prv58, params);
|
||||
watchingKey.setCreationTimeSeconds(100000);
|
||||
chain = DeterministicKeyChain.builder().spend(watchingKey).build();
|
||||
chain = DeterministicKeyChain.builder().spend(watchingKey).outputScriptType(chain.getOutputScriptType())
|
||||
.build();
|
||||
assertEquals(100000, chain.getEarliestKeyCreationTime());
|
||||
chain.setLookaheadSize(10);
|
||||
chain.maybeLookAhead();
|
||||
|
@ -517,7 +605,8 @@ public class DeterministicKeyChainTest {
|
|||
assertEquals("xprv9vL4k9HYXonmzR7UC1ngJ3hTjxkmjLLUo3RexSfUGSWcACHzghWBLJAwW6xzs59XeFizQxFQWtscoTfrF9PSXrUgAtBgr13Nuojax8xTBRz", prv58);
|
||||
watchingKey = DeterministicKey.deserializeB58(null, prv58, params);
|
||||
watchingKey.setCreationTimeSeconds(secs);
|
||||
chain = DeterministicKeyChain.builder().spend(watchingKey).build();
|
||||
chain = DeterministicKeyChain.builder().spend(watchingKey).outputScriptType(chain.getOutputScriptType())
|
||||
.build();
|
||||
assertEquals(accountTwo, chain.getAccountPath());
|
||||
assertEquals(secs, chain.getEarliestKeyCreationTime());
|
||||
chain.setLookaheadSize(10);
|
||||
|
@ -544,7 +633,8 @@ public class DeterministicKeyChainTest {
|
|||
assertEquals("xprv9yYQhynAmWWuz62PScx5Q2frBET2F1raaXna5A2E9Lj8XWgmKBL7S98Yand8F736j9UCTNWQeiB4yL5pLZP7JDY2tY8eszGQkiKDwBkezeS", prv58);
|
||||
watchingKey = DeterministicKey.deserializeB58(null, prv58, params);
|
||||
watchingKey.setCreationTimeSeconds(secs);
|
||||
DeterministicKeyChain fromPrivBase58Chain = DeterministicKeyChain.builder().spend(watchingKey).build();
|
||||
DeterministicKeyChain fromPrivBase58Chain = DeterministicKeyChain.builder().spend(watchingKey)
|
||||
.outputScriptType(bip44chain.getOutputScriptType()).build();
|
||||
assertEquals(secs, fromPrivBase58Chain.getEarliestKeyCreationTime());
|
||||
fromPrivBase58Chain.setLookaheadSize(10);
|
||||
fromPrivBase58Chain.maybeLookAhead();
|
||||
|
@ -555,8 +645,8 @@ public class DeterministicKeyChainTest {
|
|||
DeterministicKey accountKey = HDKeyDerivation.deriveChildKey(coinLevelKey, new ChildNumber(0, true));
|
||||
accountKey = accountKey.dropParent();
|
||||
accountKey.setCreationTimeSeconds(watchingKey.getCreationTimeSeconds());
|
||||
KeyChainGroup group = KeyChainGroup.builder(params)
|
||||
.addChain(DeterministicKeyChain.builder().spend(accountKey).build()).build();
|
||||
KeyChainGroup group = KeyChainGroup.builder(params).addChain(DeterministicKeyChain.builder().spend(accountKey)
|
||||
.outputScriptType(bip44chain.getOutputScriptType()).build()).build();
|
||||
DeterministicKeyChain fromMasterKeyChain = group.getActiveKeyChain();
|
||||
assertEquals(BIP44_ACCOUNT_ONE_PATH, fromMasterKeyChain.getAccountPath());
|
||||
assertEquals(secs, fromMasterKeyChain.getEarliestKeyCreationTime());
|
||||
|
@ -611,7 +701,8 @@ public class DeterministicKeyChainTest {
|
|||
@Test(expected = IllegalStateException.class)
|
||||
public void watchingCannotEncrypt() throws Exception {
|
||||
final DeterministicKey accountKey = chain.getKeyByPath(DeterministicKeyChain.ACCOUNT_ZERO_PATH);
|
||||
chain = DeterministicKeyChain.builder().watch(accountKey.dropPrivateBytes().dropParent()).build();
|
||||
chain = DeterministicKeyChain.builder().watch(accountKey.dropPrivateBytes().dropParent())
|
||||
.outputScriptType(chain.getOutputScriptType()).build();
|
||||
assertEquals(DeterministicKeyChain.ACCOUNT_ZERO_PATH, chain.getAccountPath());
|
||||
chain = chain.toEncrypted("this doesn't make any sense");
|
||||
}
|
||||
|
@ -642,7 +733,8 @@ public class DeterministicKeyChainTest {
|
|||
DeterministicKey[] keys = new DeterministicKey[100];
|
||||
for (int i = 0; i < keys.length; i++)
|
||||
keys[i] = chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||
chain = DeterministicKeyChain.builder().watch(chain.getWatchingKey().dropPrivateBytes().dropParent()).build();
|
||||
chain = DeterministicKeyChain.builder().watch(chain.getWatchingKey().dropPrivateBytes().dropParent())
|
||||
.outputScriptType(chain.getOutputScriptType()).build();
|
||||
int e = chain.numBloomFilterEntries();
|
||||
BloomFilter filter = chain.getFilter(e, 0.001, 1);
|
||||
for (DeterministicKey key : keys)
|
||||
|
|
|
@ -25,9 +25,11 @@ import org.bitcoinj.core.Sha256Hash;
|
|||
import org.bitcoinj.core.Utils;
|
||||
import org.bitcoinj.crypto.*;
|
||||
import org.bitcoinj.params.MainNetParams;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.Script.ScriptType;
|
||||
import org.bitcoinj.utils.BriefLogFormatter;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.bitcoinj.wallet.KeyChain.KeyPurpose;
|
||||
import org.bitcoinj.wallet.listeners.KeyChainEventListener;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
@ -49,6 +51,7 @@ public class KeyChainGroupTest {
|
|||
private static final int LOOKAHEAD_SIZE = 5;
|
||||
private static final NetworkParameters MAINNET = MainNetParams.get();
|
||||
private static final String XPUB = "xpub68KFnj3bqUx1s7mHejLDBPywCAKdJEu1b49uniEEn2WSbHmZ7xbLqFTjJbtx1LUcAt1DwhoqWHmo2s5WMJp6wi38CiF2hYD49qVViKVvAoi";
|
||||
private static final byte[] ENTROPY = Sha256Hash.hash("don't use a string seed like this in real life".getBytes());
|
||||
private KeyChainGroup group;
|
||||
private DeterministicKey watchingAccountKey;
|
||||
|
||||
|
@ -56,7 +59,7 @@ public class KeyChainGroupTest {
|
|||
public void setup() {
|
||||
BriefLogFormatter.init();
|
||||
Utils.setMockClock();
|
||||
group = KeyChainGroup.builder(MAINNET).fromRandom().build();
|
||||
group = KeyChainGroup.builder(MAINNET).fromRandom(Script.ScriptType.P2PKH).build();
|
||||
group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests.
|
||||
group.getActiveKeyChain(); // Force create a chain.
|
||||
|
||||
|
@ -161,16 +164,16 @@ public class KeyChainGroupTest {
|
|||
assertEquals(a, result);
|
||||
result = group.findKeyFromPubKey(b.getPubKey());
|
||||
assertEquals(b, result);
|
||||
result = group.findKeyFromPubKeyHash(a.getPubKeyHash());
|
||||
result = group.findKeyFromPubKeyHash(a.getPubKeyHash(), null);
|
||||
assertEquals(a, result);
|
||||
result = group.findKeyFromPubKeyHash(b.getPubKeyHash());
|
||||
result = group.findKeyFromPubKeyHash(b.getPubKeyHash(), null);
|
||||
assertEquals(b, result);
|
||||
result = group.findKeyFromPubKey(c.getPubKey());
|
||||
assertEquals(c, result);
|
||||
result = group.findKeyFromPubKeyHash(c.getPubKeyHash());
|
||||
result = group.findKeyFromPubKeyHash(c.getPubKeyHash(), null);
|
||||
assertEquals(c, result);
|
||||
assertNull(group.findKeyFromPubKey(d.getPubKey()));
|
||||
assertNull(group.findKeyFromPubKeyHash(d.getPubKeyHash()));
|
||||
assertNull(group.findKeyFromPubKeyHash(d.getPubKeyHash(), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -302,7 +305,7 @@ public class KeyChainGroupTest {
|
|||
|
||||
@Test
|
||||
public void encryptionWhilstEmpty() throws Exception {
|
||||
group = KeyChainGroup.builder(MAINNET).fromRandom().build();
|
||||
group = KeyChainGroup.builder(MAINNET).fromRandom(Script.ScriptType.P2PKH).build();
|
||||
group.setLookaheadSize(5);
|
||||
KeyCrypterScrypt scrypt = new KeyCrypterScrypt(2);
|
||||
final KeyParameter aesKey = scrypt.deriveKey("password");
|
||||
|
@ -455,8 +458,8 @@ public class KeyChainGroupTest {
|
|||
|
||||
@Test
|
||||
public void serializeWatching() throws Exception {
|
||||
group = KeyChainGroup.builder(MAINNET)
|
||||
.addChain(DeterministicKeyChain.builder().watch(watchingAccountKey).build()).build();
|
||||
group = KeyChainGroup.builder(MAINNET).addChain(DeterministicKeyChain.builder().watch(watchingAccountKey)
|
||||
.outputScriptType(Script.ScriptType.P2PKH).build()).build();
|
||||
group.setLookaheadSize(LOOKAHEAD_SIZE);
|
||||
group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||
group.freshKey(KeyChain.KeyPurpose.CHANGE);
|
||||
|
@ -494,7 +497,8 @@ public class KeyChainGroupTest {
|
|||
ECKey key1 = group.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||
final DeterministicSeed seed = checkNotNull(group.getActiveKeyChain().getSeed());
|
||||
KeyChainGroup group2 = KeyChainGroup.builder(MAINNET)
|
||||
.addChain(DeterministicKeyChain.builder().seed(seed).build()).build();
|
||||
.addChain(DeterministicKeyChain.builder().seed(seed).outputScriptType(Script.ScriptType.P2PKH).build())
|
||||
.build();
|
||||
group2.setLookaheadSize(5);
|
||||
ECKey key2 = group2.freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||
assertEquals(key1, key2);
|
||||
|
@ -597,7 +601,7 @@ public class KeyChainGroupTest {
|
|||
|
||||
@Test
|
||||
public void isNotWatching() {
|
||||
group = KeyChainGroup.builder(MAINNET).fromRandom().build();
|
||||
group = KeyChainGroup.builder(MAINNET).fromRandom(Script.ScriptType.P2PKH).build();
|
||||
final ECKey key = ECKey.fromPrivate(BigInteger.TEN);
|
||||
group.importKeys(key);
|
||||
assertFalse(group.isWatching());
|
||||
|
@ -608,7 +612,7 @@ public class KeyChainGroupTest {
|
|||
group = KeyChainGroup.builder(MAINNET)
|
||||
.addChain(DeterministicKeyChain.builder().watch(DeterministicKey.deserializeB58(
|
||||
"xpub69bjfJ91ikC5ghsqsVDHNq2dRGaV2HHVx7Y9LXi27LN9BWWAXPTQr4u8U3wAtap8bLdHdkqPpAcZmhMS5SnrMQC4ccaoBccFhh315P4UYzo",
|
||||
MAINNET)).build())
|
||||
MAINNET)).outputScriptType(Script.ScriptType.P2PKH).build())
|
||||
.build();
|
||||
final ECKey watchingKey = ECKey.fromPublicOnly(new ECKey().getPubKeyPoint());
|
||||
group.importKeys(watchingKey);
|
||||
|
@ -626,10 +630,46 @@ public class KeyChainGroupTest {
|
|||
group = KeyChainGroup.builder(MAINNET)
|
||||
.addChain(DeterministicKeyChain.builder().watch(DeterministicKey.deserializeB58(
|
||||
"xpub69bjfJ91ikC5ghsqsVDHNq2dRGaV2HHVx7Y9LXi27LN9BWWAXPTQr4u8U3wAtap8bLdHdkqPpAcZmhMS5SnrMQC4ccaoBccFhh315P4UYzo",
|
||||
MAINNET)).build())
|
||||
MAINNET)).outputScriptType(Script.ScriptType.P2PKH).build())
|
||||
.build();
|
||||
final ECKey key = ECKey.fromPrivate(BigInteger.TEN);
|
||||
group.importKeys(key);
|
||||
group.isWatching();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void segwitKeyChainGroup() throws Exception {
|
||||
group = KeyChainGroup.builder(MAINNET).addChain(DeterministicKeyChain.builder().entropy(ENTROPY, 0)
|
||||
.outputScriptType(Script.ScriptType.P2WPKH).accountPath(DeterministicKeyChain.ACCOUNT_ONE_PATH).build())
|
||||
.build();
|
||||
group.setLookaheadSize(LOOKAHEAD_SIZE); // Don't want slow tests.
|
||||
assertEquals(Script.ScriptType.P2WPKH, group.getActiveKeyChain().getOutputScriptType());
|
||||
assertEquals("bc1qhcurdec849thpjjp3e27atvya43gy2snrechd9",
|
||||
group.currentAddress(KeyPurpose.RECEIVE_FUNDS).toString());
|
||||
assertEquals("bc1qw8sf3mwuwn74qnhj83gjg0cwkk78fun2pxl9t2", group.currentAddress(KeyPurpose.CHANGE).toString());
|
||||
|
||||
// round-trip through protobuf
|
||||
group = KeyChainGroup.fromProtobufUnencrypted(MAINNET, group.serializeToProtobuf());
|
||||
assertEquals(Script.ScriptType.P2WPKH, group.getActiveKeyChain().getOutputScriptType());
|
||||
assertEquals("bc1qhcurdec849thpjjp3e27atvya43gy2snrechd9",
|
||||
group.currentAddress(KeyPurpose.RECEIVE_FUNDS).toString());
|
||||
assertEquals("bc1qw8sf3mwuwn74qnhj83gjg0cwkk78fun2pxl9t2", group.currentAddress(KeyPurpose.CHANGE).toString());
|
||||
|
||||
// encryption
|
||||
KeyCrypterScrypt scrypt = new KeyCrypterScrypt(2);
|
||||
KeyParameter aesKey = scrypt.deriveKey("password");
|
||||
group.encrypt(scrypt, aesKey);
|
||||
assertEquals(Script.ScriptType.P2WPKH, group.getActiveKeyChain().getOutputScriptType());
|
||||
assertEquals("bc1qhcurdec849thpjjp3e27atvya43gy2snrechd9",
|
||||
group.currentAddress(KeyPurpose.RECEIVE_FUNDS).toString());
|
||||
assertEquals("bc1qw8sf3mwuwn74qnhj83gjg0cwkk78fun2pxl9t2", group.currentAddress(KeyPurpose.CHANGE).toString());
|
||||
|
||||
// round-trip encrypted again, then dectypt
|
||||
group = KeyChainGroup.fromProtobufEncrypted(MAINNET, group.serializeToProtobuf(), scrypt);
|
||||
group.decrypt(aesKey);
|
||||
assertEquals(Script.ScriptType.P2WPKH, group.getActiveKeyChain().getOutputScriptType());
|
||||
assertEquals("bc1qhcurdec849thpjjp3e27atvya43gy2snrechd9",
|
||||
group.currentAddress(KeyPurpose.RECEIVE_FUNDS).toString());
|
||||
assertEquals("bc1qw8sf3mwuwn74qnhj83gjg0cwkk78fun2pxl9t2", group.currentAddress(KeyPurpose.CHANGE).toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.bitcoinj.core.ECKey;
|
|||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
import org.bitcoinj.core.LegacyAddress;
|
||||
import org.bitcoinj.core.PeerAddress;
|
||||
import org.bitcoinj.core.SegwitAddress;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.StoredBlock;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
@ -62,6 +63,7 @@ import com.google.common.collect.Lists;
|
|||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.bitcoinj.wallet.KeyChain.KeyPurpose;
|
||||
import org.bitcoinj.wallet.Protos.Wallet.EncryptionType;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
@ -178,10 +180,11 @@ public class WalletTest extends TestWithWallet {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void encryptDecryptWalletWithArbitraryPath() throws Exception {
|
||||
public void encryptDecryptWalletWithArbitraryPathAndScriptType() throws Exception {
|
||||
final byte[] ENTROPY = Sha256Hash.hash("don't use a string seed like this in real life".getBytes());
|
||||
KeyChainGroup keyChainGroup = KeyChainGroup.builder(UNITTEST)
|
||||
.addChain(DeterministicKeyChain.builder().seed(new DeterministicSeed(ENTROPY, "", 1389353062L))
|
||||
.outputScriptType(Script.ScriptType.P2WPKH)
|
||||
.accountPath(DeterministicKeyChain.BIP44_ACCOUNT_ZERO_PATH).build())
|
||||
.build();
|
||||
Wallet encryptedWallet = new Wallet(UNITTEST, keyChainGroup);
|
||||
|
@ -1618,7 +1621,8 @@ public class WalletTest extends TestWithWallet {
|
|||
@Test
|
||||
public void isWatching() {
|
||||
assertFalse(wallet.isWatching());
|
||||
Wallet watchingWallet = Wallet.fromWatchingKey(UNITTEST, wallet.getWatchingKey().dropPrivateBytes().dropParent());
|
||||
Wallet watchingWallet = Wallet.fromWatchingKey(UNITTEST,
|
||||
wallet.getWatchingKey().dropPrivateBytes().dropParent(), Script.ScriptType.P2PKH);
|
||||
assertTrue(watchingWallet.isWatching());
|
||||
wallet.encrypt(PASSWORD1);
|
||||
assertFalse(wallet.isWatching());
|
||||
|
@ -1630,7 +1634,8 @@ public class WalletTest extends TestWithWallet {
|
|||
String serialized = watchKey.serializePubB58(UNITTEST);
|
||||
|
||||
// Construct watching wallet.
|
||||
Wallet watchingWallet = Wallet.fromWatchingKey(UNITTEST, DeterministicKey.deserializeB58(null, serialized, UNITTEST));
|
||||
Wallet watchingWallet = Wallet.fromWatchingKey(UNITTEST,
|
||||
DeterministicKey.deserializeB58(null, serialized, UNITTEST), Script.ScriptType.P2PKH);
|
||||
DeterministicKey key2 = watchingWallet.freshReceiveKey();
|
||||
assertEquals(myKey, key2);
|
||||
|
||||
|
@ -2954,7 +2959,7 @@ public class WalletTest extends TestWithWallet {
|
|||
assertEquals(THREE_CENTS.subtract(tx.getFee()), tx.getValueSentToMe(wallet));
|
||||
// TX sends to one of our addresses (for now we ignore married wallets).
|
||||
final Address toAddress = tx.getOutput(0).getScriptPubKey().getToAddress(UNITTEST);
|
||||
final ECKey rotatingToKey = wallet.findKeyFromPubKeyHash(toAddress.getHash());
|
||||
final ECKey rotatingToKey = wallet.findKeyFromPubKeyHash(toAddress.getHash(), toAddress.getOutputScriptType());
|
||||
assertNotNull(rotatingToKey);
|
||||
assertFalse(wallet.isKeyRotating(rotatingToKey));
|
||||
assertEquals(3, tx.getInputs().size());
|
||||
|
@ -2970,7 +2975,8 @@ public class WalletTest extends TestWithWallet {
|
|||
sendMoneyToWallet(wallet, AbstractBlockChain.NewBlockType.BEST_CHAIN, CENT, LegacyAddress.fromKey(UNITTEST, key1));
|
||||
wallet.doMaintenance(null, true);
|
||||
tx = broadcaster.waitForTransactionAndSucceed();
|
||||
assertNotNull(wallet.findKeyFromPubKeyHash(tx.getOutput(0).getScriptPubKey().getPubKeyHash()));
|
||||
assertNotNull(wallet.findKeyFromPubKeyHash(tx.getOutput(0).getScriptPubKey().getPubKeyHash(),
|
||||
toAddress.getOutputScriptType()));
|
||||
log.info("Unexpected thing: {}", tx);
|
||||
assertEquals(Coin.valueOf(19300), tx.getFee());
|
||||
assertEquals(1, tx.getInputs().size());
|
||||
|
@ -3055,7 +3061,7 @@ public class WalletTest extends TestWithWallet {
|
|||
List<Transaction> txns = wallet.doMaintenance(null, false).get();
|
||||
assertEquals(1, txns.size());
|
||||
Address output = txns.get(0).getOutput(0).getScriptPubKey().getToAddress(UNITTEST);
|
||||
ECKey usedKey = wallet.findKeyFromPubKeyHash(output.getHash());
|
||||
ECKey usedKey = wallet.findKeyFromPubKeyHash(output.getHash(), output.getOutputScriptType());
|
||||
assertEquals(goodKey.getCreationTimeSeconds(), usedKey.getCreationTimeSeconds());
|
||||
assertEquals(goodKey.getCreationTimeSeconds(), wallet.freshReceiveKey().getCreationTimeSeconds());
|
||||
assertEquals("mrM3TpCnav5YQuVA1xLercCGJH4DXujMtv", LegacyAddress.fromKey(UNITTEST, usedKey).toString());
|
||||
|
@ -3129,7 +3135,8 @@ public class WalletTest extends TestWithWallet {
|
|||
// Delete the sigs
|
||||
for (TransactionInput input : req.tx.getInputs())
|
||||
input.clearScriptBytes();
|
||||
Wallet watching = Wallet.fromWatchingKey(UNITTEST, wallet.getWatchingKey().dropParent().dropPrivateBytes());
|
||||
Wallet watching = Wallet.fromWatchingKey(UNITTEST, wallet.getWatchingKey().dropParent().dropPrivateBytes(),
|
||||
Script.ScriptType.P2PKH);
|
||||
watching.completeTx(SendRequest.forTx(req.tx));
|
||||
}
|
||||
|
||||
|
@ -3531,4 +3538,39 @@ public class WalletTest extends TestWithWallet {
|
|||
wallet = roundTrip(wallet);
|
||||
assertTrue(wallet.isConsistent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scriptTypeKeyChainRestrictions() {
|
||||
// Set up chains: basic chain, P2PKH deterministric chain, P2WPKH deterministic chain.
|
||||
DeterministicKeyChain p2pkhChain = DeterministicKeyChain.builder().random(new SecureRandom())
|
||||
.outputScriptType(Script.ScriptType.P2PKH).build();
|
||||
DeterministicKeyChain p2wpkhChain = DeterministicKeyChain.builder().random(new SecureRandom())
|
||||
.outputScriptType(Script.ScriptType.P2WPKH).build();
|
||||
KeyChainGroup kcg = KeyChainGroup.builder(UNITTEST).addChain(p2pkhChain).addChain(p2wpkhChain).build();
|
||||
Wallet wallet = new Wallet(UNITTEST, kcg);
|
||||
|
||||
// Set up one key from each chain.
|
||||
ECKey importedKey = new ECKey();
|
||||
wallet.importKey(importedKey);
|
||||
ECKey p2pkhKey = p2pkhChain.getKey(KeyPurpose.RECEIVE_FUNDS);
|
||||
ECKey p2wpkhKey = p2wpkhChain.getKey(KeyPurpose.RECEIVE_FUNDS);
|
||||
|
||||
// Test imported key: it's not limited to script type.
|
||||
assertTrue(wallet.isAddressMine(LegacyAddress.fromKey(UNITTEST, importedKey)));
|
||||
assertTrue(wallet.isAddressMine(SegwitAddress.fromKey(UNITTEST, importedKey)));
|
||||
assertEquals(importedKey, wallet.findKeyFromAddress(LegacyAddress.fromKey(UNITTEST, importedKey)));
|
||||
assertEquals(importedKey, wallet.findKeyFromAddress(SegwitAddress.fromKey(UNITTEST, importedKey)));
|
||||
|
||||
// Test key from P2PKH chain: it's limited to P2PKH addresses
|
||||
assertTrue(wallet.isAddressMine(LegacyAddress.fromKey(UNITTEST, p2pkhKey)));
|
||||
assertFalse(wallet.isAddressMine(SegwitAddress.fromKey(UNITTEST, p2pkhKey)));
|
||||
assertEquals(p2pkhKey, wallet.findKeyFromAddress(LegacyAddress.fromKey(UNITTEST, p2pkhKey)));
|
||||
assertNull(wallet.findKeyFromAddress(SegwitAddress.fromKey(UNITTEST, p2pkhKey)));
|
||||
|
||||
// Test key from P2WPKH chain: it's limited to P2WPKH addresses
|
||||
assertFalse(wallet.isAddressMine(LegacyAddress.fromKey(UNITTEST, p2wpkhKey)));
|
||||
assertTrue(wallet.isAddressMine(SegwitAddress.fromKey(UNITTEST, p2wpkhKey)));
|
||||
assertNull(wallet.findKeyFromAddress(LegacyAddress.fromKey(UNITTEST, p2wpkhKey)));
|
||||
assertEquals(p2wpkhKey, wallet.findKeyFromAddress(SegwitAddress.fromKey(UNITTEST, p2wpkhKey)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ creation_timestamp: 1389353062000
|
|||
deterministic_key {
|
||||
chain_code: "XL\240FW\203\316\230\334\374J\003\357=\215\001\206\365\207Z\006m\334X`\236,;_\304\000^"
|
||||
}
|
||||
output_script_type: P2PKH
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
secret_bytes: "\370\\\267\361L\021K\214\215\272yRP\234(\304\365\303h\251\250\0236\270\344\210\300\330\363(=\332"
|
||||
|
|
|
@ -0,0 +1,280 @@
|
|||
type: DETERMINISTIC_MNEMONIC
|
||||
secret_bytes: "aerobic toe save section draw warm cute upon raccoon mother priority pilot taste sweet next traffic fatal sword dentist original crisp team caution rebel"
|
||||
creation_timestamp: 1389353062000
|
||||
deterministic_seed: "E\032\356\206\230,\275\263\364=\334^f\307\037\350\321X7R\262z\205\3564\371tp\2639R\342\027 J\266\253\250\320\022\031\233\271~O$\330\260\214\fz\231tI\353\215*\037\355\205\213.\224?"
|
||||
account_path: 2147483649
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
secret_bytes: "\270E0\202(\362b\023\276\264\347\226E2\360\221\347\325\233L\203\3276\272\213\2436&\304\373\221\025"
|
||||
public_key: "\002\342$\253\332\031\352\324q\316M\251}\274\267\370X$\366>Q\316\005\330\376\353f!WHLL\a"
|
||||
creation_timestamp: 1389353062000
|
||||
deterministic_key {
|
||||
chain_code: "XL\240FW\203\316\230\334\374J\003\357=\215\001\206\365\207Z\006m\334X`\236,;_\304\000^"
|
||||
}
|
||||
output_script_type: P2WPKH
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
secret_bytes: "\267y\204N)\270Ysr\315?t\213.g\224\332\354#\262\216\341%\217B\322%v\365\357\301\202"
|
||||
public_key: "\002\270\032\242\r\210\310\316\361\360s3\233A\374\002\r\035m;w~\032\201\237\367/O5\231\270\216k"
|
||||
deterministic_key {
|
||||
chain_code: "\255\v_(\027?\036\353\373\212\346\360\276\364q/\246\351z\331\332a\207\177&\335\206\220\276f+\211"
|
||||
path: 2147483649
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
secret_bytes: "\322j5\033\375\257\371-\211~d)\"{\30629\3446\025=H\370Q\227\325=\002\240|r\343"
|
||||
public_key: "\002\237\202\357O\355r\020(]\235\b\3162\263N\331\320\216\246\262\376\260\352\224\313Bw\347~\310\336X"
|
||||
deterministic_key {
|
||||
chain_code: "*\210c\235x\377\001\277\b\"\321\363\r*\'ci\310\317\344\220\025\341\v\r\337\034\200H\366\312P"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
issued_subkeys: 2
|
||||
lookahead_size: 10
|
||||
sigsRequiredToSpend: 1
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
secret_bytes: "(USw\200Yh\313F|o\213\240u\265@n\000r\033\305\376\211\a\304D\034\342\316\021\374\207"
|
||||
public_key: "\002\031\335\304L?`\236\224c\252\305\375\2678\275\342\236\242\230\266C\363\327U\341\340\337GeW\2544"
|
||||
deterministic_key {
|
||||
chain_code: "\336m6\270\332/\363\016\340=;\003\225\341S\024\262\332vM\201L\207\342\306=!\352A\f\272{"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
issued_subkeys: 1
|
||||
lookahead_size: 10
|
||||
sigsRequiredToSpend: 1
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002P\022\037\025\003\264\302/\321\314\243\246(\371\006\245\203\255\363o\237\250\b\233O?\225\316T)n\357"
|
||||
deterministic_key {
|
||||
chain_code: "9\032\260\204\177\362\362\310`\243)\2067\351\324O\355\337V\245\375\t\375\214mw\353\355\316\246bA"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 0
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\246\306%*\023\234\0316\273;9\341\033\370 \341\220\312\216\003\300\316`\201\352\357\327\216\234\0254\031"
|
||||
deterministic_key {
|
||||
chain_code: "\335\267\305\177 \255ew\bH\210@\033\267\224f\375\325\205\362`\022.3\224\f\206\260\361z\260\371"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 1
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\am\036\377\ff8\003\222\351#?\356e\000\367\254i0\324H~\251E/\352B\2616\0030+"
|
||||
deterministic_key {
|
||||
chain_code: "{#\231\t\034\023T@\317\322\344^w\254)\350!\327\221\033\263C\347\277zb\301^`\004\277\350"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 2
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\307\214\031\332\032\205oH\346\272\v\267\n#9ivZ\247\327L)3\254\"\201\331K\340\211\026`"
|
||||
deterministic_key {
|
||||
chain_code: "@}\204\033\347\346\270\345\376M\027\263\262h\242\322[l\254\263\351O\334^\273k\003\225\2065\017\031"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 3
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\025N\\\177\273\325\224\353{5G\273\304J\234t\354\260f\036!L\202\246\361\223\356T.\200\223\206"
|
||||
deterministic_key {
|
||||
chain_code: "\230\244\354A\316\272\223j\226\341\023-\327\302>\317\275\022\252\366R\230\312a\310\347u,\004\233\b9"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 4
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\b\035p\217z\250\".;C\0313>\366\251\"\246Y\307\034\265\273V\202P\207\024\322\316\340$7"
|
||||
deterministic_key {
|
||||
chain_code: "W&\026N5\205:1\216d\256m\357\2248\341\227\204\352\177P\265\"\036\v\204p1\177BC\236"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 5
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\220L\270j\r\270\277\336\016\370\332\203\260\356\322\263<\357$\003\017k\nU<6\235\n\253\230;\205"
|
||||
deterministic_key {
|
||||
chain_code: "\025\304d\363\376\aA\275p&_\336\355\374\224\315\264\222m\363m\222S\200\017-\274\330\322\244A\302"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 6
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\232\370\302\261\006\200\347-\257[\205\v\273\352\v8_\270\304\331Z.Ud\351\264s\251*8_\375"
|
||||
deterministic_key {
|
||||
chain_code: "0\345\362I\317=\033I\270\b\322J\316\275D\220\335\334\320\367\275\334\336\367\237l\377\376\3638\203\233"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 7
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002E\317\324\302\345\b\021\275qQ\303\030\257-\352\2744>H\026\340\371\022\"X\025\255\357\367\271\316D"
|
||||
deterministic_key {
|
||||
chain_code: "\313\"m\227\025\343o\2252\377\320\200FF~T\322\363\2330\021\251 \276\317\274\0227\322\a96"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 8
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\376k!\002\320\037\352\030\211?\031/H*\242\326\365\022\374\273\254\323)3\370\1770\340nv\333\244"
|
||||
deterministic_key {
|
||||
chain_code: "\315u1\361c\030\266@+\344\225\022\337\034\325\272\223r\251\262\357\211\255.\356\005\031Ds\217,\367"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 9
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\003O\356\2103\362\253-\016\211\276\023i\233\256\3044\236RP<\253\367\346.R/\330\006\331\017\n"
|
||||
deterministic_key {
|
||||
chain_code: "\231\026I\a1\'\350^]THS\323\355\v\347\237\356\302\226\343\216K,omM\237\272\373\234\207"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 10
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\0035\304\365\255c\376\260\366\213l\246o\v\342\300\232f\301\306\233\245\373Z\300\230\231\206\201L\231\235\306"
|
||||
deterministic_key {
|
||||
chain_code: "\256\216Pe\026\317\313\a\376\031\021\302\207&\363N\271\'\357\275|\334\315\215\272\237\202\231\277\370\264\356"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 11
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002g\340hs\337]\302$\275\225\277\326\233\3016`\027\267Y+\323\376\360\353\266\350X\247\320\273\247\026"
|
||||
deterministic_key {
|
||||
chain_code: " \217\242P&\336p\226W\200I+\346\t\213\347d\260-\243\241+\213+\304\365^\227\203\206\231\031"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 12
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\036\220\032\376\211\327G\236\371\032\276\004l\341\206\235\001\336\2248\314\027\226u*j\251\262c\252\346\265"
|
||||
deterministic_key {
|
||||
chain_code: "\363\345\031D\by@\353zGS\267o{ c`c\300NH\006T%miB\210\325x$\306"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 0
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\00269\201\317\302t\016Q\247\205n\204\263MI\345X\330da\006>\251\204h\372+u.>\321v"
|
||||
deterministic_key {
|
||||
chain_code: "\317j\363\36773\254u\037\3663\243\n-Z\237T\210\263\202u\315\337/\245.H\364\004\253+o"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 1
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\025#\214(\322\374C\000\234KO\1776\276T\354\217\372g:\340\035c\271\275\375RF`\357\260u"
|
||||
deterministic_key {
|
||||
chain_code: "\351\345 \224\354I\201\254\275\207\300\357\240\216\326P\031f\232Ll@\363\207\236\203\027t\350\370Kn"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 2
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\250+_^\241\353Z%ie\237\233\241.#\322\"\373W\276eJ\255\327\250\024v\016\0340\343\t"
|
||||
deterministic_key {
|
||||
chain_code: "\211\220?,\212\350\001\233[><h\"\246{\311\372ET\353n\022\332\224\264\373\b\023\272\277$\325"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 3
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\323\260\005\254\242\350\307\3556\302\373\003\246\330\000g`+0\253\365Q\323Zz\"?\376G.\356f"
|
||||
deterministic_key {
|
||||
chain_code: "\273\016\342\\V\200w\252\203\350B\314\277\245;u\327\372wL\311b_S+\376\277\306\375k\" "
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 4
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\0032\331b?\"\026\337\235\026u^\331\327-j4p\357\177\247c\355\310(9\330\231\262\023& \227"
|
||||
deterministic_key {
|
||||
chain_code: "\272\a\311\264\237\246\312G\v~\005\300\236\214ZHk\t\301\203@%iP\005\204\367\344\334\302`\r"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 5
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\377\317\372\345\267\264\024\343\302>\f\202~&\034\376\206\360\006\314\035\243\261#*r@\"\037\275\004H"
|
||||
deterministic_key {
|
||||
chain_code: "\335\030j\262\306\024\fg\346n\025\254\322U\331\323\270\004\017\023\240>\260\307_\343\235\304\222L\212\372"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 6
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003!)\246\275\r\201\246\\\233\260\345\222\215v\371g\314\211\n\0375\203m\305\001`\177 zu4O"
|
||||
deterministic_key {
|
||||
chain_code: "\333h\304\004\366\346\351*\366\205\236\377L_\200a\354N\241\235B\272\\\004|\245=\266\324W\352\'"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 7
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\026o\366\273n\366\371Krn\335\216\246\022T\005\f\265t\377\036\217.\\\bV!\fs\251\365\243"
|
||||
deterministic_key {
|
||||
chain_code: "\305\257\344_\027\025\033=>\000 \tm\273\":\265\214\023\267\203me\264*P\222\2247+Ow"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 8
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\345M0`s\370\3617\n\254\'H<\301}n\326\374\253\240\273wT\t\201r|*\331\361\315\277"
|
||||
deterministic_key {
|
||||
chain_code: "V\310\t\226\301\254\f\230\024\332\341a\271hG\331\232\024\266\235\377\004\021\212\016\257\227V\342c\212\203"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 9
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002|h\274DS-\330\035\366o\203x\3343\020q\225\024\005c\326B\200q2\213f\346N?V\330"
|
||||
deterministic_key {
|
||||
chain_code: "\233\211\254E\347\036\352\262\027\363WF\324\345\232\247j\277\f\344\031\\\177\263\223j\333?\003\025W\020"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 10
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\256[\025\037L\371\365\300\357\315\224v\006\r\236\335BIO\210vvT\n\266\243{G\321`\366\377"
|
||||
deterministic_key {
|
||||
chain_code: ":\240\372\237\034\033DR1\220\t\022\200\033\307/\200wBy\365\223\244\0170\327\277>Z9\365\277"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 11
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\375\023ByLoCf\260\230\001\334\373\031\327\325\347\353Ad\253\244\246A=\217vf\034D5\250"
|
||||
deterministic_key {
|
||||
chain_code: "\277\265\332jOb\260G6,u\342\263A]\214KF\360\337\217\231:\342x\225\330\250Fa*n"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 12
|
||||
}
|
|
@ -11,6 +11,7 @@ creation_timestamp: 1389353062000
|
|||
deterministic_key {
|
||||
chain_code: "XL\240FW\203\316\230\334\374J\003\357=\215\001\206\365\207Z\006m\334X`\236,;_\304\000^"
|
||||
}
|
||||
output_script_type: P2PKH
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
secret_bytes: "\354B\331\275;\000\254?\3428\006\220G\365\243\333s\260s\213R\313\307\377f\331B\351\327=\001\333"
|
||||
|
|
|
@ -6,6 +6,7 @@ deterministic_key {
|
|||
chain_code: "_\377Y\344\324\234\263\356_\002\325\222\241B\307C\302\021L\345f\2731u\364~\325\306\333\367\233\257"
|
||||
path: 2147483650
|
||||
}
|
||||
output_script_type: P2PKH
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
secret_bytes: "\214E-\031jfj\351\324\206;\304\034v93\035[\251\374\t_@\262\273%q.r\276\016\241"
|
||||
|
|
|
@ -8,6 +8,7 @@ deterministic_key {
|
|||
path: 2147483649
|
||||
path: 2147483648
|
||||
}
|
||||
output_script_type: P2PKH
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
secret_bytes: "\362\305\242\3637\2748Z?]\035s\272\253J\300\033\250\022r\350\020\277U\036K<\335\237\333/\303"
|
||||
|
|
|
@ -6,6 +6,7 @@ deterministic_key {
|
|||
chain_code: "F\336\2067\377M\026)%\357ZL#\203\320\324\217-\3305\310\244\n\205\277E\323L\250ww\314"
|
||||
path: 2147483648
|
||||
}
|
||||
output_script_type: P2PKH
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
secret_bytes: "\362\305\242\3637\2748Z?]\035s\272\253J\300\033\250\022r\350\020\277U\036K<\335\237\333/\303"
|
||||
|
|
|
@ -6,6 +6,7 @@ deterministic_key {
|
|||
chain_code: "\370\017\223\021O?.@gZ|\233j\3437\317q-\241!\177J \323\'\264s\203\314\321\v\346"
|
||||
path: 2147483648
|
||||
}
|
||||
output_script_type: P2PKH
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
secret_bytes: "<M\020I\364\276\336Z\255\341\330\257\337 \366E_\027\2433w\325\263\"$\350\f\244\006\251u\021"
|
||||
|
|
|
@ -7,6 +7,7 @@ deterministic_key {
|
|||
path: 2147483649
|
||||
path: 2147483648
|
||||
}
|
||||
output_script_type: P2PKH
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\2471\326i\331A\337|\373\276\3214\257\363\266Q\315x\341\317\200\243\234\336<s}\261\240,\233\371"
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\270\032\242\r\210\310\316\361\360s3\233A\374\002\r\035m;w~\032\201\237\367/O5\231\270\216k"
|
||||
creation_timestamp: 100000000
|
||||
deterministic_key {
|
||||
chain_code: "\255\v_(\027?\036\353\373\212\346\360\276\364q/\246\351z\331\332a\207\177&\335\206\220\276f+\211"
|
||||
path: 2147483649
|
||||
}
|
||||
output_script_type: P2WPKH
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\237\202\357O\355r\020(]\235\b\3162\263N\331\320\216\246\262\376\260\352\224\313Bw\347~\310\336X"
|
||||
deterministic_key {
|
||||
chain_code: "*\210c\235x\377\001\277\b\"\321\363\r*\'ci\310\317\344\220\025\341\v\r\337\034\200H\366\312P"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
issued_subkeys: 2
|
||||
lookahead_size: 10
|
||||
sigsRequiredToSpend: 1
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\031\335\304L?`\236\224c\252\305\375\2678\275\342\236\242\230\266C\363\327U\341\340\337GeW\2544"
|
||||
deterministic_key {
|
||||
chain_code: "\336m6\270\332/\363\016\340=;\003\225\341S\024\262\332vM\201L\207\342\306=!\352A\f\272{"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
issued_subkeys: 1
|
||||
lookahead_size: 10
|
||||
sigsRequiredToSpend: 1
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002P\022\037\025\003\264\302/\321\314\243\246(\371\006\245\203\255\363o\237\250\b\233O?\225\316T)n\357"
|
||||
deterministic_key {
|
||||
chain_code: "9\032\260\204\177\362\362\310`\243)\2067\351\324O\355\337V\245\375\t\375\214mw\353\355\316\246bA"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 0
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\246\306%*\023\234\0316\273;9\341\033\370 \341\220\312\216\003\300\316`\201\352\357\327\216\234\0254\031"
|
||||
deterministic_key {
|
||||
chain_code: "\335\267\305\177 \255ew\bH\210@\033\267\224f\375\325\205\362`\022.3\224\f\206\260\361z\260\371"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 1
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\am\036\377\ff8\003\222\351#?\356e\000\367\254i0\324H~\251E/\352B\2616\0030+"
|
||||
deterministic_key {
|
||||
chain_code: "{#\231\t\034\023T@\317\322\344^w\254)\350!\327\221\033\263C\347\277zb\301^`\004\277\350"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 2
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\307\214\031\332\032\205oH\346\272\v\267\n#9ivZ\247\327L)3\254\"\201\331K\340\211\026`"
|
||||
deterministic_key {
|
||||
chain_code: "@}\204\033\347\346\270\345\376M\027\263\262h\242\322[l\254\263\351O\334^\273k\003\225\2065\017\031"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 3
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\025N\\\177\273\325\224\353{5G\273\304J\234t\354\260f\036!L\202\246\361\223\356T.\200\223\206"
|
||||
deterministic_key {
|
||||
chain_code: "\230\244\354A\316\272\223j\226\341\023-\327\302>\317\275\022\252\366R\230\312a\310\347u,\004\233\b9"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 4
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\b\035p\217z\250\".;C\0313>\366\251\"\246Y\307\034\265\273V\202P\207\024\322\316\340$7"
|
||||
deterministic_key {
|
||||
chain_code: "W&\026N5\205:1\216d\256m\357\2248\341\227\204\352\177P\265\"\036\v\204p1\177BC\236"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 5
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\220L\270j\r\270\277\336\016\370\332\203\260\356\322\263<\357$\003\017k\nU<6\235\n\253\230;\205"
|
||||
deterministic_key {
|
||||
chain_code: "\025\304d\363\376\aA\275p&_\336\355\374\224\315\264\222m\363m\222S\200\017-\274\330\322\244A\302"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 6
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\232\370\302\261\006\200\347-\257[\205\v\273\352\v8_\270\304\331Z.Ud\351\264s\251*8_\375"
|
||||
deterministic_key {
|
||||
chain_code: "0\345\362I\317=\033I\270\b\322J\316\275D\220\335\334\320\367\275\334\336\367\237l\377\376\3638\203\233"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 7
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002E\317\324\302\345\b\021\275qQ\303\030\257-\352\2744>H\026\340\371\022\"X\025\255\357\367\271\316D"
|
||||
deterministic_key {
|
||||
chain_code: "\313\"m\227\025\343o\2252\377\320\200FF~T\322\363\2330\021\251 \276\317\274\0227\322\a96"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 8
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\376k!\002\320\037\352\030\211?\031/H*\242\326\365\022\374\273\254\323)3\370\1770\340nv\333\244"
|
||||
deterministic_key {
|
||||
chain_code: "\315u1\361c\030\266@+\344\225\022\337\034\325\272\223r\251\262\357\211\255.\356\005\031Ds\217,\367"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 9
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\003O\356\2103\362\253-\016\211\276\023i\233\256\3044\236RP<\253\367\346.R/\330\006\331\017\n"
|
||||
deterministic_key {
|
||||
chain_code: "\231\026I\a1\'\350^]THS\323\355\v\347\237\356\302\226\343\216K,omM\237\272\373\234\207"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 10
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\0035\304\365\255c\376\260\366\213l\246o\v\342\300\232f\301\306\233\245\373Z\300\230\231\206\201L\231\235\306"
|
||||
deterministic_key {
|
||||
chain_code: "\256\216Pe\026\317\313\a\376\031\021\302\207&\363N\271\'\357\275|\334\315\215\272\237\202\231\277\370\264\356"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 11
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002g\340hs\337]\302$\275\225\277\326\233\3016`\027\267Y+\323\376\360\353\266\350X\247\320\273\247\026"
|
||||
deterministic_key {
|
||||
chain_code: " \217\242P&\336p\226W\200I+\346\t\213\347d\260-\243\241+\213+\304\365^\227\203\206\231\031"
|
||||
path: 2147483649
|
||||
path: 0
|
||||
path: 12
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\036\220\032\376\211\327G\236\371\032\276\004l\341\206\235\001\336\2248\314\027\226u*j\251\262c\252\346\265"
|
||||
deterministic_key {
|
||||
chain_code: "\363\345\031D\by@\353zGS\267o{ c`c\300NH\006T%miB\210\325x$\306"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 0
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\00269\201\317\302t\016Q\247\205n\204\263MI\345X\330da\006>\251\204h\372+u.>\321v"
|
||||
deterministic_key {
|
||||
chain_code: "\317j\363\36773\254u\037\3663\243\n-Z\237T\210\263\202u\315\337/\245.H\364\004\253+o"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 1
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\025#\214(\322\374C\000\234KO\1776\276T\354\217\372g:\340\035c\271\275\375RF`\357\260u"
|
||||
deterministic_key {
|
||||
chain_code: "\351\345 \224\354I\201\254\275\207\300\357\240\216\326P\031f\232Ll@\363\207\236\203\027t\350\370Kn"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 2
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\250+_^\241\353Z%ie\237\233\241.#\322\"\373W\276eJ\255\327\250\024v\016\0340\343\t"
|
||||
deterministic_key {
|
||||
chain_code: "\211\220?,\212\350\001\233[><h\"\246{\311\372ET\353n\022\332\224\264\373\b\023\272\277$\325"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 3
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\323\260\005\254\242\350\307\3556\302\373\003\246\330\000g`+0\253\365Q\323Zz\"?\376G.\356f"
|
||||
deterministic_key {
|
||||
chain_code: "\273\016\342\\V\200w\252\203\350B\314\277\245;u\327\372wL\311b_S+\376\277\306\375k\" "
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 4
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\0032\331b?\"\026\337\235\026u^\331\327-j4p\357\177\247c\355\310(9\330\231\262\023& \227"
|
||||
deterministic_key {
|
||||
chain_code: "\272\a\311\264\237\246\312G\v~\005\300\236\214ZHk\t\301\203@%iP\005\204\367\344\334\302`\r"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 5
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\377\317\372\345\267\264\024\343\302>\f\202~&\034\376\206\360\006\314\035\243\261#*r@\"\037\275\004H"
|
||||
deterministic_key {
|
||||
chain_code: "\335\030j\262\306\024\fg\346n\025\254\322U\331\323\270\004\017\023\240>\260\307_\343\235\304\222L\212\372"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 6
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003!)\246\275\r\201\246\\\233\260\345\222\215v\371g\314\211\n\0375\203m\305\001`\177 zu4O"
|
||||
deterministic_key {
|
||||
chain_code: "\333h\304\004\366\346\351*\366\205\236\377L_\200a\354N\241\235B\272\\\004|\245=\266\324W\352\'"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 7
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\026o\366\273n\366\371Krn\335\216\246\022T\005\f\265t\377\036\217.\\\bV!\fs\251\365\243"
|
||||
deterministic_key {
|
||||
chain_code: "\305\257\344_\027\025\033=>\000 \tm\273\":\265\214\023\267\203me\264*P\222\2247+Ow"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 8
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003\345M0`s\370\3617\n\254\'H<\301}n\326\374\253\240\273wT\t\201r|*\331\361\315\277"
|
||||
deterministic_key {
|
||||
chain_code: "V\310\t\226\301\254\f\230\024\332\341a\271hG\331\232\024\266\235\377\004\021\212\016\257\227V\342c\212\203"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 9
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002|h\274DS-\330\035\366o\203x\3343\020q\225\024\005c\326B\200q2\213f\346N?V\330"
|
||||
deterministic_key {
|
||||
chain_code: "\233\211\254E\347\036\352\262\027\363WF\324\345\232\247j\277\f\344\031\\\177\263\223j\333?\003\025W\020"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 10
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\256[\025\037L\371\365\300\357\315\224v\006\r\236\335BIO\210vvT\n\266\243{G\321`\366\377"
|
||||
deterministic_key {
|
||||
chain_code: ":\240\372\237\034\033DR1\220\t\022\200\033\307/\200wBy\365\223\244\0170\327\277>Z9\365\277"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 11
|
||||
}
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\375\023ByLoCf\260\230\001\334\373\031\327\325\347\353Ad\253\244\246A=\217vf\034D5\250"
|
||||
deterministic_key {
|
||||
chain_code: "\277\265\332jOb\260G6,u\342\263A]\214KF\360\337\217\231:\342x\225\330\250Fa*n"
|
||||
path: 2147483649
|
||||
path: 1
|
||||
path: 12
|
||||
}
|
|
@ -5,6 +5,7 @@ deterministic_key {
|
|||
chain_code: "SE\031\000\277\023 \256W\237\036\034c\340\aB-I\246\304\025\325\262\314\235\240]\020\374T\315\027"
|
||||
path: 1
|
||||
}
|
||||
output_script_type: P2PKH
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\003QO{n\351\326\357\343T\203u\345\025{]\231\234\273A\376U`\307\212\336h\255\215\335(\364\001"
|
||||
|
|
|
@ -5,6 +5,7 @@ deterministic_key {
|
|||
chain_code: "\370\017\223\021O?.@gZ|\233j\3437\317q-\241!\177J \323\'\264s\203\314\321\v\346"
|
||||
path: 2147483648
|
||||
}
|
||||
output_script_type: P2PKH
|
||||
|
||||
type: DETERMINISTIC_KEY
|
||||
public_key: "\002\361V\216\001\371p\270\212\272\236%\216\356o\025g\r\035>a\305j\001P\217Q\242\261.\353\367\315"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2015 Ross Nicoll.
|
||||
* Copyright 2019 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -64,7 +65,7 @@ public class GenerateLowSTests {
|
|||
final ECKey key = new ECKey(secureRandom);
|
||||
final KeyBag bag = new KeyBag() {
|
||||
@Override
|
||||
public ECKey findKeyFromPubKeyHash(byte[] pubKeyHash) {
|
||||
public ECKey findKeyFromPubKeyHash(byte[] pubkeyHash, Script.ScriptType scriptType) {
|
||||
return key;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,10 @@ import org.bitcoinj.core.listeners.DownloadProgressTracker;
|
|||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.net.discovery.DnsDiscovery;
|
||||
import org.bitcoinj.params.TestNet3Params;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.store.SPVBlockStore;
|
||||
import org.bitcoinj.wallet.DeterministicSeed;
|
||||
import org.bitcoinj.wallet.KeyChainGroupStructure;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -46,7 +48,7 @@ public class RestoreFromSeed {
|
|||
DeterministicSeed seed = new DeterministicSeed(seedCode, null, passphrase, creationtime);
|
||||
|
||||
// The wallet class provides a easy fromSeed() function that loads a new wallet from a given seed.
|
||||
Wallet wallet = Wallet.fromSeed(params, seed);
|
||||
Wallet wallet = Wallet.fromSeed(params, seed, Script.ScriptType.P2PKH);
|
||||
|
||||
// Because we are importing an existing wallet which might already have transactions we must re-download the blockchain to make the wallet picks up these transactions
|
||||
// You can find some information about this in the guides: https://bitcoinj.github.io/working-with-the-wallet#setup
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.bitcoinj.core.NetworkParameters;
|
|||
import org.bitcoinj.core.Utils;
|
||||
import org.bitcoinj.kits.WalletAppKit;
|
||||
import org.bitcoinj.params.*;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.utils.BriefLogFormatter;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.bitcoinj.wallet.DeterministicSeed;
|
||||
|
@ -46,6 +47,7 @@ import static wallettemplate.utils.GuiUtils.*;
|
|||
|
||||
public class Main extends Application {
|
||||
public static NetworkParameters params = MainNetParams.get();
|
||||
public static final Script.ScriptType PREFERRED_OUTPUT_SCRIPT_TYPE = Script.ScriptType.P2WPKH;
|
||||
public static final String APP_NAME = "WalletTemplate";
|
||||
private static final String WALLET_FILE_NAME = APP_NAME.replaceAll("[^a-zA-Z0-9.-]", "_") + "-"
|
||||
+ params.getPaymentProtocolId();
|
||||
|
@ -132,7 +134,7 @@ public class Main extends Application {
|
|||
|
||||
public void setupWalletKit(@Nullable DeterministicSeed seed) {
|
||||
// If seed is non-null it means we are restoring from backup.
|
||||
bitcoin = new WalletAppKit(params, new File("."), WALLET_FILE_NAME) {
|
||||
bitcoin = new WalletAppKit(params, PREFERRED_OUTPUT_SCRIPT_TYPE, null, new File("."), WALLET_FILE_NAME) {
|
||||
@Override
|
||||
protected void onSetupCompleted() {
|
||||
// Don't make the user wait for confirmations for now, as the intention is they're sending it
|
||||
|
|
|
@ -58,6 +58,7 @@ public class SendMoneyController {
|
|||
new TextFieldValidator(amountEdit, text ->
|
||||
!WTUtils.didThrow(() -> checkState(Coin.parseCoin(text).compareTo(balance) <= 0)));
|
||||
amountEdit.setText(balance.toPlainString());
|
||||
address.setPromptText(Address.fromKey(Main.params, new ECKey(), Main.PREFERRED_OUTPUT_SCRIPT_TYPE).toString());
|
||||
}
|
||||
|
||||
public void cancel(ActionEvent event) {
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<Font size="24.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<TextField fx:id="address" maxWidth="1.7976931348623157E308" promptText="1EZEqFBd8yuc9ir2761987q7k3VcALC8YQ" HBox.hgrow="ALWAYS">
|
||||
<TextField fx:id="address" maxWidth="1.7976931348623157E308" HBox.hgrow="ALWAYS">
|
||||
<VBox.margin>
|
||||
<Insets />
|
||||
</VBox.margin>
|
||||
|
|
Loading…
Add table
Reference in a new issue