mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-14 19:25:51 +00:00
HD Wallets: support watching wallets in Wallet and wallet-tool.
Also, respect includePrivateKeys flag for the seed in wallet.toString again.
This commit is contained in:
parent
c7f7fd29e0
commit
51b71a4363
@ -18,6 +18,7 @@
|
|||||||
package com.google.bitcoin.core;
|
package com.google.bitcoin.core;
|
||||||
|
|
||||||
import com.google.bitcoin.core.TransactionConfidence.ConfidenceType;
|
import com.google.bitcoin.core.TransactionConfidence.ConfidenceType;
|
||||||
|
import com.google.bitcoin.crypto.DeterministicKey;
|
||||||
import com.google.bitcoin.crypto.KeyCrypter;
|
import com.google.bitcoin.crypto.KeyCrypter;
|
||||||
import com.google.bitcoin.crypto.KeyCrypterException;
|
import com.google.bitcoin.crypto.KeyCrypterException;
|
||||||
import com.google.bitcoin.crypto.KeyCrypterScrypt;
|
import com.google.bitcoin.crypto.KeyCrypterScrypt;
|
||||||
@ -212,6 +213,10 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
return new Wallet(params, new KeyChainGroup(seed));
|
return new Wallet(params, new KeyChainGroup(seed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Wallet fromWatchingKey(NetworkParameters params, DeterministicKey watchKey) {
|
||||||
|
return new Wallet(params, new KeyChainGroup(watchKey));
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: When this class moves to the Wallet package, along with the protobuf serializer, then hide this.
|
// TODO: When this class moves to the Wallet package, along with the protobuf serializer, then hide this.
|
||||||
/** For internal use only. */
|
/** For internal use only. */
|
||||||
public Wallet(NetworkParameters params, KeyChainGroup keyChainGroup) {
|
public Wallet(NetworkParameters params, KeyChainGroup keyChainGroup) {
|
||||||
@ -274,7 +279,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
* it's actually seen in a pending or confirmed transaction, at which point this method will start returning
|
* it's actually seen in a pending or confirmed transaction, at which point this method will start returning
|
||||||
* a different key (for each purpose independently).
|
* a different key (for each purpose independently).
|
||||||
*/
|
*/
|
||||||
public ECKey currentKey(KeyChain.KeyPurpose purpose) {
|
public DeterministicKey currentKey(KeyChain.KeyPurpose purpose) {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
return keychain.currentKey(purpose);
|
return keychain.currentKey(purpose);
|
||||||
@ -287,7 +292,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
* An alias for calling {@link #currentKey(com.google.bitcoin.wallet.KeyChain.KeyPurpose)} with
|
* An alias for calling {@link #currentKey(com.google.bitcoin.wallet.KeyChain.KeyPurpose)} with
|
||||||
* {@link com.google.bitcoin.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS} as the parameter.
|
* {@link com.google.bitcoin.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS} as the parameter.
|
||||||
*/
|
*/
|
||||||
public ECKey currentReceiveKey() {
|
public DeterministicKey currentReceiveKey() {
|
||||||
return currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
return currentKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,10 +304,10 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
* into a receive coins wizard type UI. You should use this when the user is definitely going to hand this key out
|
* into a receive coins wizard type UI. You should use this when the user is definitely going to hand this key out
|
||||||
* to someone who wishes to send money.
|
* to someone who wishes to send money.
|
||||||
*/
|
*/
|
||||||
public ECKey freshKey(KeyChain.KeyPurpose purpose) {
|
public DeterministicKey freshKey(KeyChain.KeyPurpose purpose) {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
ECKey key = keychain.freshKey(purpose);
|
DeterministicKey key = keychain.freshKey(purpose);
|
||||||
// Do we really need an immediate hard save? Arguably all this is doing is saving the 'current' key
|
// Do we really need an immediate hard save? Arguably all this is doing is saving the 'current' key
|
||||||
// and that's not quite so important, so we could coalesce for more performance.
|
// and that's not quite so important, so we could coalesce for more performance.
|
||||||
saveNow();
|
saveNow();
|
||||||
@ -316,7 +321,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
* An alias for calling {@link #freshKey(com.google.bitcoin.wallet.KeyChain.KeyPurpose)} with
|
* An alias for calling {@link #freshKey(com.google.bitcoin.wallet.KeyChain.KeyPurpose)} with
|
||||||
* {@link com.google.bitcoin.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS} as the parameter.
|
* {@link com.google.bitcoin.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS} as the parameter.
|
||||||
*/
|
*/
|
||||||
public ECKey freshReceiveKey() {
|
public DeterministicKey freshReceiveKey() {
|
||||||
return freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
return freshKey(KeyChain.KeyPurpose.RECEIVE_FUNDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,6 +461,20 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
return keychain.getLookaheadSize();
|
return keychain.getLookaheadSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a public-only DeterministicKey that can be used to set up a watching wallet: that is, a wallet that
|
||||||
|
* can import transactions from the block chain just as the normal wallet can, but which cannot spend. Watching
|
||||||
|
* wallets are very useful for things like web servers that accept payments.
|
||||||
|
*/
|
||||||
|
public DeterministicKey getWatchingKey() {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
return keychain.getActiveKeyChain().getWatchingKey();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if we are watching this address.
|
* Return true if we are watching this address.
|
||||||
*/
|
*/
|
||||||
|
@ -219,7 +219,10 @@ public class DeterministicKey extends ECKey {
|
|||||||
} else {
|
} else {
|
||||||
// If it's not encrypted, derive the private via the parents.
|
// If it's not encrypted, derive the private via the parents.
|
||||||
final BigInteger privateKey = findOrDerivePrivateKey();
|
final BigInteger privateKey = findOrDerivePrivateKey();
|
||||||
checkState(privateKey != null, "This key is a part of a public-key only heirarchy and cannot be used for signing");
|
if (privateKey == null) {
|
||||||
|
// This key is a part of a public-key only heirarchy and cannot be used for signing
|
||||||
|
throw new MissingPrivateKeyException();
|
||||||
|
}
|
||||||
return super.doSign(input, privateKey);
|
return super.doSign(input, privateKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,8 @@ import static com.google.common.collect.Lists.newLinkedList;
|
|||||||
* {@link com.google.bitcoin.crypto.DeterministicKey#serializePubB58()}. The resulting "xpub..." string encodes
|
* {@link com.google.bitcoin.crypto.DeterministicKey#serializePubB58()}. The resulting "xpub..." string encodes
|
||||||
* sufficient information about the account key to create a watching chain via
|
* sufficient information about the account key to create a watching chain via
|
||||||
* {@link com.google.bitcoin.crypto.DeterministicKey#deserializeB58(com.google.bitcoin.crypto.DeterministicKey, String)}
|
* {@link com.google.bitcoin.crypto.DeterministicKey#deserializeB58(com.google.bitcoin.crypto.DeterministicKey, String)}
|
||||||
* (with null as the first parameter) and then {@link #watch(com.google.bitcoin.crypto.DeterministicKey)}.</p>
|
* (with null as the first parameter) and then
|
||||||
|
* {@link DeterministicKeyChain#DeterministicKeyChain(com.google.bitcoin.crypto.DeterministicKey)}.</p>
|
||||||
*
|
*
|
||||||
* <p>This class builds on {@link com.google.bitcoin.crypto.DeterministicHierarchy} and
|
* <p>This class builds on {@link com.google.bitcoin.crypto.DeterministicHierarchy} and
|
||||||
* {@link com.google.bitcoin.crypto.DeterministicKey} by adding support for serialization to and from protobufs,
|
* {@link com.google.bitcoin.crypto.DeterministicKey} by adding support for serialization to and from protobufs,
|
||||||
@ -135,19 +136,18 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||||||
this(seed, null);
|
this(seed, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// c'tor for building watching chains, we keep it private and give it a static name to make the purpose clear.
|
|
||||||
private DeterministicKeyChain(DeterministicKey accountKey) {
|
|
||||||
checkArgument(accountKey.isPubKeyOnly(), "Private subtrees not currently supported");
|
|
||||||
checkArgument(accountKey.getPath().size() == 1, "You can only watch an account key currently");
|
|
||||||
basicKeyChain = new BasicKeyChain();
|
|
||||||
initializeHierarchyUnencrypted(accountKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a deterministic key chain that watches the given (public only) root key. You can use this to calculate
|
* Creates a deterministic key chain that watches the given (public only) root key. You can use this to calculate
|
||||||
* balances and generally follow along, but spending is not possible with such a chain. Currently you can't use
|
* balances and generally follow along, but spending is not possible with such a chain. Currently you can't use
|
||||||
* this method to watch an arbitrary fragment of some other tree, this limitation may be removed in future.
|
* this method to watch an arbitrary fragment of some other tree, this limitation may be removed in future.
|
||||||
*/
|
*/
|
||||||
|
public DeterministicKeyChain(DeterministicKey watchingKey) {
|
||||||
|
checkArgument(watchingKey.isPubKeyOnly(), "Private subtrees not currently supported");
|
||||||
|
checkArgument(watchingKey.getPath().size() == 1, "You can only watch an account key currently");
|
||||||
|
basicKeyChain = new BasicKeyChain();
|
||||||
|
initializeHierarchyUnencrypted(watchingKey);
|
||||||
|
}
|
||||||
|
|
||||||
public static DeterministicKeyChain watch(DeterministicKey accountKey) {
|
public static DeterministicKeyChain watch(DeterministicKey accountKey) {
|
||||||
return new DeterministicKeyChain(accountKey);
|
return new DeterministicKeyChain(accountKey);
|
||||||
}
|
}
|
||||||
@ -445,7 +445,7 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||||||
if (!accountKey.getPath().equals(ACCOUNT_ZERO_PATH))
|
if (!accountKey.getPath().equals(ACCOUNT_ZERO_PATH))
|
||||||
throw new UnreadableWalletException("Expecting account key but found key with path: " +
|
throw new UnreadableWalletException("Expecting account key but found key with path: " +
|
||||||
HDUtils.formatPath(accountKey.getPath()));
|
HDUtils.formatPath(accountKey.getPath()));
|
||||||
chain = DeterministicKeyChain.watch(accountKey);
|
chain = new DeterministicKeyChain(accountKey);
|
||||||
isWatchingAccountKey = true;
|
isWatchingAccountKey = true;
|
||||||
} else {
|
} else {
|
||||||
chain = new DeterministicKeyChain(seed, crypter);
|
chain = new DeterministicKeyChain(seed, crypter);
|
||||||
|
@ -66,7 +66,7 @@ public class DeterministicSeed implements EncryptableItem {
|
|||||||
/**
|
/**
|
||||||
* Constructs a seed from a BIP 39 mnemonic code. See {@link com.google.bitcoin.crypto.MnemonicCode} for more
|
* Constructs a seed from a BIP 39 mnemonic code. See {@link com.google.bitcoin.crypto.MnemonicCode} for more
|
||||||
* details on this scheme.
|
* details on this scheme.
|
||||||
* @param words A list of 12 words.
|
* @param words A list of words.
|
||||||
* @param creationTimeSeconds When the seed was originally created, UNIX time.
|
* @param creationTimeSeconds When the seed was originally created, UNIX time.
|
||||||
* @throws MnemonicException if there is a problem decoding the words.
|
* @throws MnemonicException if there is a problem decoding the words.
|
||||||
*/
|
*/
|
||||||
|
@ -67,6 +67,14 @@ public class KeyChainGroup {
|
|||||||
this(null, ImmutableList.of(new DeterministicKeyChain(seed)), null);
|
this(null, ImmutableList.of(new DeterministicKeyChain(seed)), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a keychain group with no basic chain, and an HD chain that is watching the given watching key.
|
||||||
|
* This HAS to be an account key as returned by {@link DeterministicKeyChain#getWatchingKey()}.
|
||||||
|
*/
|
||||||
|
public KeyChainGroup(DeterministicKey watchKey) {
|
||||||
|
this(null, ImmutableList.of(new DeterministicKeyChain(watchKey)), null);
|
||||||
|
}
|
||||||
|
|
||||||
// Used for deserialization.
|
// Used for deserialization.
|
||||||
private KeyChainGroup(@Nullable BasicKeyChain basicKeyChain, List<DeterministicKeyChain> chains, @Nullable KeyCrypter crypter) {
|
private KeyChainGroup(@Nullable BasicKeyChain basicKeyChain, List<DeterministicKeyChain> chains, @Nullable KeyCrypter crypter) {
|
||||||
this.basic = basicKeyChain == null ? new BasicKeyChain() : basicKeyChain;
|
this.basic = basicKeyChain == null ? new BasicKeyChain() : basicKeyChain;
|
||||||
@ -89,7 +97,7 @@ public class KeyChainGroup {
|
|||||||
* it's actually seen in a pending or confirmed transaction, at which point this method will start returning
|
* it's actually seen in a pending or confirmed transaction, at which point this method will start returning
|
||||||
* a different key (for each purpose independently).
|
* a different key (for each purpose independently).
|
||||||
*/
|
*/
|
||||||
public ECKey currentKey(KeyChain.KeyPurpose purpose) {
|
public DeterministicKey currentKey(KeyChain.KeyPurpose purpose) {
|
||||||
final DeterministicKey current = currentKeys.get(purpose);
|
final DeterministicKey current = currentKeys.get(purpose);
|
||||||
return current != null ? current : freshKey(purpose);
|
return current != null ? current : freshKey(purpose);
|
||||||
}
|
}
|
||||||
@ -102,7 +110,7 @@ public class KeyChainGroup {
|
|||||||
* into a receive coins wizard type UI. You should use this when the user is definitely going to hand this key out
|
* into a receive coins wizard type UI. You should use this when the user is definitely going to hand this key out
|
||||||
* to someone who wishes to send money.
|
* to someone who wishes to send money.
|
||||||
*/
|
*/
|
||||||
public ECKey freshKey(KeyChain.KeyPurpose purpose) {
|
public DeterministicKey freshKey(KeyChain.KeyPurpose purpose) {
|
||||||
DeterministicKeyChain chain = getActiveKeyChain();
|
DeterministicKeyChain chain = getActiveKeyChain();
|
||||||
DeterministicKey key = chain.getKey(purpose); // Always returns the next key along the key chain.
|
DeterministicKey key = chain.getKey(purpose); // Always returns the next key along the key chain.
|
||||||
currentKeys.put(purpose, key);
|
currentKeys.put(purpose, key);
|
||||||
@ -372,26 +380,27 @@ public class KeyChainGroup {
|
|||||||
for (ECKey key : basic.getKeys())
|
for (ECKey key : basic.getKeys())
|
||||||
formatKeyWithAddress(params, includePrivateKeys, key, builder);
|
formatKeyWithAddress(params, includePrivateKeys, key, builder);
|
||||||
}
|
}
|
||||||
final String newline = String.format("%n");
|
|
||||||
for (DeterministicKeyChain chain : chains) {
|
for (DeterministicKeyChain chain : chains) {
|
||||||
DeterministicSeed seed = chain.getSeed();
|
DeterministicSeed seed = chain.getSeed();
|
||||||
if (seed != null && !seed.isEncrypted()) {
|
if (seed != null) {
|
||||||
final List<String> words = seed.toMnemonicCode();
|
if (seed.isEncrypted()) {
|
||||||
builder.append("Seed as words: ");
|
builder.append(String.format("Seed is encrypted%n"));
|
||||||
builder.append(Joiner.on(' ').join(words));
|
} else if (includePrivateKeys) {
|
||||||
builder.append(newline);
|
final List<String> words = seed.toMnemonicCode();
|
||||||
builder.append("Seed as hex: ");
|
builder.append(
|
||||||
builder.append(seed.toHexString());
|
String.format("Seed as words: %s%nSeed as hex: %s%n", Joiner.on(' ').join(words),
|
||||||
builder.append(newline);
|
seed.toHexString())
|
||||||
builder.append("Seed birthday: ");
|
);
|
||||||
builder.append(seed.getCreationTimeSeconds());
|
}
|
||||||
builder.append(" [" + new Date(seed.getCreationTimeSeconds() * 1000) + "]");
|
builder.append(String.format("Seed birthday: %d [%s]%n", seed.getCreationTimeSeconds(), new Date(seed.getCreationTimeSeconds() * 1000)));
|
||||||
builder.append(newline);
|
}
|
||||||
builder.append(newline);
|
final DeterministicKey watchingKey = chain.getWatchingKey();
|
||||||
} else {
|
// Don't show if it's been imported from a watching wallet already, because it'd result in a weird/
|
||||||
builder.append("Seed is encrypted");
|
// unintuitive result where the watching key in a watching wallet is not the one it was created with
|
||||||
builder.append(newline);
|
// due to the parent fingerprint being missing/not stored. In future we could store the parent fingerprint
|
||||||
builder.append(newline);
|
// optionally as well to fix this, but it seems unimportant for now.
|
||||||
|
if (watchingKey.getParent() != null) {
|
||||||
|
builder.append(String.format("Key to watch: %s%n%n", watchingKey.serializePubB58()));
|
||||||
}
|
}
|
||||||
for (ECKey key : chain.getKeys())
|
for (ECKey key : chain.getKeys())
|
||||||
formatKeyWithAddress(params, includePrivateKeys, key, builder);
|
formatKeyWithAddress(params, includePrivateKeys, key, builder);
|
||||||
|
@ -19,10 +19,7 @@ package com.google.bitcoin.core;
|
|||||||
|
|
||||||
import com.google.bitcoin.core.Transaction.SigHash;
|
import com.google.bitcoin.core.Transaction.SigHash;
|
||||||
import com.google.bitcoin.core.Wallet.SendRequest;
|
import com.google.bitcoin.core.Wallet.SendRequest;
|
||||||
import com.google.bitcoin.crypto.KeyCrypter;
|
import com.google.bitcoin.crypto.*;
|
||||||
import com.google.bitcoin.crypto.KeyCrypterException;
|
|
||||||
import com.google.bitcoin.crypto.KeyCrypterScrypt;
|
|
||||||
import com.google.bitcoin.crypto.TransactionSignature;
|
|
||||||
import com.google.bitcoin.store.WalletProtobufSerializer;
|
import com.google.bitcoin.store.WalletProtobufSerializer;
|
||||||
import com.google.bitcoin.testing.FakeTxBuilder;
|
import com.google.bitcoin.testing.FakeTxBuilder;
|
||||||
import com.google.bitcoin.testing.MockTransactionBroadcaster;
|
import com.google.bitcoin.testing.MockTransactionBroadcaster;
|
||||||
@ -1098,6 +1095,23 @@ public class WalletTest extends TestWithWallet {
|
|||||||
log.info(t2.toString(chain));
|
log.info(t2.toString(chain));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = ECKey.MissingPrivateKeyException.class)
|
||||||
|
public void watchingWallet() throws Exception {
|
||||||
|
DeterministicKey key = wallet.freshReceiveKey();
|
||||||
|
DeterministicKey watchKey = wallet.getWatchingKey();
|
||||||
|
String serialized = watchKey.serializePubB58();
|
||||||
|
watchKey = DeterministicKey.deserializeB58(null, serialized);
|
||||||
|
Wallet watchingWallet = Wallet.fromWatchingKey(params, watchKey);
|
||||||
|
DeterministicKey key2 = watchingWallet.freshReceiveKey();
|
||||||
|
assertEquals(key, key2);
|
||||||
|
|
||||||
|
key = wallet.freshKey(KeyChain.KeyPurpose.CHANGE);
|
||||||
|
key2 = watchingWallet.freshKey(KeyChain.KeyPurpose.CHANGE);
|
||||||
|
assertEquals(key, key2);
|
||||||
|
key.sign(Sha256Hash.ZERO_HASH);
|
||||||
|
key2.sign(Sha256Hash.ZERO_HASH);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void watchingScripts() throws Exception {
|
public void watchingScripts() throws Exception {
|
||||||
// Verify that pending transactions to watched addresses are relevant
|
// Verify that pending transactions to watched addresses are relevant
|
||||||
|
@ -230,7 +230,7 @@ public class DeterministicKeyChainTest {
|
|||||||
final String pub58 = watchingKey.serializePubB58();
|
final String pub58 = watchingKey.serializePubB58();
|
||||||
assertEquals("xpub68KFnj3bqUx1s7mHejLDBPywCAKdJEu1b49uniEEn2WSbHmZ7xbLqFTjJbtx1LUcAt1DwhoqWHmo2s5WMJp6wi38CiF2hYD49qVViKVvAoi", pub58);
|
assertEquals("xpub68KFnj3bqUx1s7mHejLDBPywCAKdJEu1b49uniEEn2WSbHmZ7xbLqFTjJbtx1LUcAt1DwhoqWHmo2s5WMJp6wi38CiF2hYD49qVViKVvAoi", pub58);
|
||||||
watchingKey = DeterministicKey.deserializeB58(null, pub58);
|
watchingKey = DeterministicKey.deserializeB58(null, pub58);
|
||||||
chain = DeterministicKeyChain.watch(watchingKey);
|
chain = new DeterministicKeyChain(watchingKey);
|
||||||
chain.setLookaheadSize(10);
|
chain.setLookaheadSize(10);
|
||||||
|
|
||||||
assertEquals(key1.getPubKeyPoint(), chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).getPubKeyPoint());
|
assertEquals(key1.getPubKeyPoint(), chain.getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).getPubKeyPoint());
|
||||||
@ -241,7 +241,8 @@ public class DeterministicKeyChainTest {
|
|||||||
// Can't sign with a key from a watching chain.
|
// Can't sign with a key from a watching chain.
|
||||||
key.sign(Sha256Hash.ZERO_HASH);
|
key.sign(Sha256Hash.ZERO_HASH);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalStateException e) {
|
} catch (ECKey.MissingPrivateKeyException e) {
|
||||||
|
// Ignored.
|
||||||
}
|
}
|
||||||
// Test we can serialize and deserialize a watching chain OK.
|
// Test we can serialize and deserialize a watching chain OK.
|
||||||
List<Protos.Key> serialization = chain.serializeToProtobuf();
|
List<Protos.Key> serialization = chain.serializeToProtobuf();
|
||||||
@ -254,7 +255,7 @@ public class DeterministicKeyChainTest {
|
|||||||
@Test(expected = IllegalStateException.class)
|
@Test(expected = IllegalStateException.class)
|
||||||
public void watchingCannotEncrypt() throws Exception {
|
public void watchingCannotEncrypt() throws Exception {
|
||||||
final DeterministicKey accountKey = chain.getKeyByPath(DeterministicKeyChain.ACCOUNT_ZERO_PATH);
|
final DeterministicKey accountKey = chain.getKeyByPath(DeterministicKeyChain.ACCOUNT_ZERO_PATH);
|
||||||
chain = DeterministicKeyChain.watch(accountKey.getPubOnly());
|
chain = new DeterministicKeyChain(accountKey.getPubOnly());
|
||||||
chain = chain.toEncrypted("this doesn't make any sense");
|
chain = chain.toEncrypted("this doesn't make any sense");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
package com.google.bitcoin.tools;
|
package com.google.bitcoin.tools;
|
||||||
|
|
||||||
import com.google.bitcoin.core.*;
|
import com.google.bitcoin.core.*;
|
||||||
|
import com.google.bitcoin.crypto.DeterministicKey;
|
||||||
import com.google.bitcoin.crypto.KeyCrypterException;
|
import com.google.bitcoin.crypto.KeyCrypterException;
|
||||||
import com.google.bitcoin.crypto.MnemonicCode;
|
import com.google.bitcoin.crypto.MnemonicCode;
|
||||||
import com.google.bitcoin.crypto.MnemonicException;
|
import com.google.bitcoin.crypto.MnemonicException;
|
||||||
@ -74,7 +75,7 @@ public class WalletTool {
|
|||||||
private static OptionSet options;
|
private static OptionSet options;
|
||||||
private static OptionSpec<Date> dateFlag;
|
private static OptionSpec<Date> dateFlag;
|
||||||
private static OptionSpec<Integer> unixtimeFlag;
|
private static OptionSpec<Integer> unixtimeFlag;
|
||||||
private static OptionSpec<String> seedFlag;
|
private static OptionSpec<String> seedFlag, watchFlag;
|
||||||
|
|
||||||
private static NetworkParameters params;
|
private static NetworkParameters params;
|
||||||
private static File walletFile;
|
private static File walletFile;
|
||||||
@ -183,6 +184,7 @@ public class WalletTool {
|
|||||||
parser.accepts("debuglog");
|
parser.accepts("debuglog");
|
||||||
OptionSpec<String> walletFileName = parser.accepts("wallet").withRequiredArg().defaultsTo("wallet");
|
OptionSpec<String> walletFileName = parser.accepts("wallet").withRequiredArg().defaultsTo("wallet");
|
||||||
seedFlag = parser.accepts("seed").withRequiredArg();
|
seedFlag = parser.accepts("seed").withRequiredArg();
|
||||||
|
watchFlag = parser.accepts("watchkey").withRequiredArg();
|
||||||
OptionSpec<NetworkEnum> netFlag = parser.accepts("net").withOptionalArg().ofType(NetworkEnum.class).defaultsTo(NetworkEnum.PROD);
|
OptionSpec<NetworkEnum> netFlag = parser.accepts("net").withOptionalArg().ofType(NetworkEnum.class).defaultsTo(NetworkEnum.PROD);
|
||||||
dateFlag = parser.accepts("date").withRequiredArg().ofType(Date.class)
|
dateFlag = parser.accepts("date").withRequiredArg().ofType(Date.class)
|
||||||
.withValuesConvertedBy(DateConverter.datePattern("yyyy/MM/dd"));
|
.withValuesConvertedBy(DateConverter.datePattern("yyyy/MM/dd"));
|
||||||
@ -824,6 +826,9 @@ public class WalletTool {
|
|||||||
seed = new DeterministicSeed(bits, creationTimeSecs);
|
seed = new DeterministicSeed(bits, creationTimeSecs);
|
||||||
}
|
}
|
||||||
wallet = Wallet.fromSeed(params, seed);
|
wallet = Wallet.fromSeed(params, seed);
|
||||||
|
} else if (options.has(watchFlag)) {
|
||||||
|
DeterministicKey watchKey = DeterministicKey.deserializeB58(null, options.valueOf(watchFlag));
|
||||||
|
wallet = Wallet.fromWatchingKey(params, watchKey);
|
||||||
} else {
|
} else {
|
||||||
wallet = new Wallet(params);
|
wallet = new Wallet(params);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user