3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-14 11:15:51 +00:00

Wallet: Add static constructor createBasic() which creates a wallet with just a basic keychain, and no key derivation.

This commit is contained in:
Andreas Schildbach 2019-02-06 16:38:24 +01:00
parent 6278dc39b1
commit d35583236a
3 changed files with 113 additions and 60 deletions

View File

@ -72,7 +72,8 @@ public class KeyChainGroup implements KeyBag {
private BasicKeyChain basic; private BasicKeyChain basic;
private NetworkParameters params; private NetworkParameters params;
protected final LinkedList<DeterministicKeyChain> chains; // Keychains for deterministically derived keys. If this is null, no chains should be created automatically.
protected final @Nullable LinkedList<DeterministicKeyChain> chains;
// currentKeys is used for normal, non-multisig/married wallets. currentAddresses is used when we're handing out // currentKeys is used for normal, non-multisig/married wallets. currentAddresses is used when we're handing out
// P2SH addresses. They're mutually exclusive. // P2SH addresses. They're mutually exclusive.
private final EnumMap<KeyChain.KeyPurpose, DeterministicKey> currentKeys; private final EnumMap<KeyChain.KeyPurpose, DeterministicKey> currentKeys;
@ -81,6 +82,11 @@ public class KeyChainGroup implements KeyBag {
private int lookaheadSize = -1; private int lookaheadSize = -1;
private int lookaheadThreshold = -1; private int lookaheadThreshold = -1;
/** Creates a keychain group with just a basic chain. No deterministic chains will be created automatically. */
public static KeyChainGroup createBasic(NetworkParameters params) {
return new KeyChainGroup(params, new BasicKeyChain(), null, null, null);
}
/** Creates a keychain group with no basic chain, and a single, lazily created HD chain. */ /** Creates a keychain group with no basic chain, and a single, lazily created HD chain. */
public KeyChainGroup(NetworkParameters params) { public KeyChainGroup(NetworkParameters params) {
this(params, null, new ArrayList<DeterministicKeyChain>(1), null, null); this(params, null, new ArrayList<DeterministicKeyChain>(1), null, null);
@ -118,19 +124,19 @@ public class KeyChainGroup implements KeyBag {
: DeterministicKeyChain.builder().spend(accountKey).build()), null, null); : DeterministicKeyChain.builder().spend(accountKey).build()), null, null);
} }
private KeyChainGroup(NetworkParameters params, @Nullable BasicKeyChain basicKeyChain, List<DeterministicKeyChain> chains, private KeyChainGroup(NetworkParameters params, @Nullable BasicKeyChain basicKeyChain, @Nullable List<DeterministicKeyChain> chains,
@Nullable EnumMap<KeyChain.KeyPurpose, DeterministicKey> currentKeys, @Nullable KeyCrypter crypter) { @Nullable EnumMap<KeyChain.KeyPurpose, DeterministicKey> currentKeys, @Nullable KeyCrypter crypter) {
this.params = params; this.params = params;
this.basic = basicKeyChain == null ? new BasicKeyChain() : basicKeyChain; this.basic = basicKeyChain == null ? new BasicKeyChain() : basicKeyChain;
this.chains = new LinkedList<>(checkNotNull(chains)); this.chains = chains != null ? new LinkedList<DeterministicKeyChain>(chains) : null;
this.keyCrypter = crypter; this.keyCrypter = crypter;
this.currentKeys = currentKeys == null this.currentKeys = currentKeys == null
? new EnumMap<KeyChain.KeyPurpose, DeterministicKey>(KeyChain.KeyPurpose.class) ? new EnumMap<KeyChain.KeyPurpose, DeterministicKey>(KeyChain.KeyPurpose.class)
: currentKeys; : currentKeys;
this.currentAddresses = new EnumMap<>(KeyChain.KeyPurpose.class); this.currentAddresses = new EnumMap<>(KeyChain.KeyPurpose.class);
maybeLookaheadScripts();
if (isMarried()) { if (isMarried()) {
maybeLookaheadScripts();
for (Map.Entry<KeyChain.KeyPurpose, DeterministicKey> entry : this.currentKeys.entrySet()) { for (Map.Entry<KeyChain.KeyPurpose, DeterministicKey> entry : this.currentKeys.entrySet()) {
Address address = ScriptBuilder Address address = ScriptBuilder
.createP2SHOutputScript(getActiveKeyChain().getRedeemData(entry.getValue()).redeemScript) .createP2SHOutputScript(getActiveKeyChain().getRedeemData(entry.getValue()).redeemScript)
@ -140,6 +146,11 @@ public class KeyChainGroup implements KeyBag {
} }
} }
/** Returns true if it contains any deterministic keychain or one could be created. */
public boolean isSupportsDeterministicChains() {
return chains != null;
}
// This keeps married redeem data in sync with the number of keys issued // This keeps married redeem data in sync with the number of keys issued
private void maybeLookaheadScripts() { private void maybeLookaheadScripts() {
for (DeterministicKeyChain chain : chains) { for (DeterministicKeyChain chain : chains) {
@ -149,6 +160,7 @@ public class KeyChainGroup implements KeyBag {
/** Adds a new HD chain to the chains list, and make it the default chain (from which keys are issued). */ /** Adds a new HD chain to the chains list, and make it the default chain (from which keys are issued). */
public void createAndActivateNewHDChain() { public void createAndActivateNewHDChain() {
checkState(isSupportsDeterministicChains(), "doesn't support deterministic chains");
// We can't do auto upgrade here because we don't know the rotation time, if any. // We can't do auto upgrade here because we don't know the rotation time, if any.
final DeterministicKeyChain chain = DeterministicKeyChain.builder().random(new SecureRandom()).build(); final DeterministicKeyChain chain = DeterministicKeyChain.builder().random(new SecureRandom()).build();
addAndActivateHDChain(chain); addAndActivateHDChain(chain);
@ -159,6 +171,7 @@ public class KeyChainGroup implements KeyBag {
* Useful for adding a complex pre-configured keychain, such as a married wallet. * Useful for adding a complex pre-configured keychain, such as a married wallet.
*/ */
public void addAndActivateHDChain(DeterministicKeyChain chain) { public void addAndActivateHDChain(DeterministicKeyChain chain) {
checkState(isSupportsDeterministicChains(), "doesn't support deterministic chains");
log.info("Creating and activating a new HD chain: {}", chain); log.info("Creating and activating a new HD chain: {}", chain);
for (ListenerRegistration<KeyChainEventListener> registration : basic.getListeners()) for (ListenerRegistration<KeyChainEventListener> registration : basic.getListeners())
chain.addEventListener(registration.listener, registration.executor); chain.addEventListener(registration.listener, registration.executor);
@ -268,6 +281,7 @@ public class KeyChainGroup implements KeyBag {
/** Returns the key chain that's used for generation of fresh/current keys. This is always the newest HD chain. */ /** Returns the key chain that's used for generation of fresh/current keys. This is always the newest HD chain. */
public final DeterministicKeyChain getActiveKeyChain() { public final DeterministicKeyChain getActiveKeyChain() {
checkState(isSupportsDeterministicChains(), "doesn't support deterministic chains");
if (chains.isEmpty()) { if (chains.isEmpty()) {
if (basic.numKeys() > 0) { if (basic.numKeys() > 0) {
log.warn("No HD chain present but random keys are: you probably deserialized an old wallet."); log.warn("No HD chain present but random keys are: you probably deserialized an old wallet.");
@ -287,10 +301,10 @@ public class KeyChainGroup implements KeyBag {
* for more information. * for more information.
*/ */
public void setLookaheadSize(int lookaheadSize) { public void setLookaheadSize(int lookaheadSize) {
checkState(isSupportsDeterministicChains(), "doesn't support deterministic chains");
this.lookaheadSize = lookaheadSize; this.lookaheadSize = lookaheadSize;
for (DeterministicKeyChain chain : chains) { for (DeterministicKeyChain chain : chains)
chain.setLookaheadSize(lookaheadSize); chain.setLookaheadSize(lookaheadSize);
}
} }
/** /**
@ -299,6 +313,7 @@ public class KeyChainGroup implements KeyBag {
* for more information. * for more information.
*/ */
public int getLookaheadSize() { public int getLookaheadSize() {
checkState(isSupportsDeterministicChains(), "doesn't support deterministic chains");
if (lookaheadSize == -1) if (lookaheadSize == -1)
return getActiveKeyChain().getLookaheadSize(); return getActiveKeyChain().getLookaheadSize();
else else
@ -311,9 +326,9 @@ public class KeyChainGroup implements KeyBag {
* for more information. * for more information.
*/ */
public void setLookaheadThreshold(int num) { public void setLookaheadThreshold(int num) {
for (DeterministicKeyChain chain : chains) { checkState(isSupportsDeterministicChains(), "doesn't support deterministic chains");
for (DeterministicKeyChain chain : chains)
chain.setLookaheadThreshold(num); chain.setLookaheadThreshold(num);
}
} }
/** /**
@ -322,6 +337,7 @@ public class KeyChainGroup implements KeyBag {
* for more information. * for more information.
*/ */
public int getLookaheadThreshold() { public int getLookaheadThreshold() {
checkState(isSupportsDeterministicChains(), "doesn't support deterministic chains");
if (lookaheadThreshold == -1) if (lookaheadThreshold == -1)
return getActiveKeyChain().getLookaheadThreshold(); return getActiveKeyChain().getLookaheadThreshold();
else else
@ -397,10 +413,10 @@ public class KeyChainGroup implements KeyBag {
ECKey result; ECKey result;
if ((result = basic.findKeyFromPubHash(pubKeyHash)) != null) if ((result = basic.findKeyFromPubHash(pubKeyHash)) != null)
return result; return result;
for (DeterministicKeyChain chain : chains) { if (chains != null)
if ((result = chain.findKeyFromPubHash(pubKeyHash)) != null) for (DeterministicKeyChain chain : chains)
return result; if ((result = chain.findKeyFromPubHash(pubKeyHash)) != null)
} return result;
return null; return null;
} }
@ -409,11 +425,13 @@ public class KeyChainGroup implements KeyBag {
* See {@link DeterministicKeyChain#markKeyAsUsed(DeterministicKey)} for more info on this. * See {@link DeterministicKeyChain#markKeyAsUsed(DeterministicKey)} for more info on this.
*/ */
public void markPubKeyHashAsUsed(byte[] pubKeyHash) { public void markPubKeyHashAsUsed(byte[] pubKeyHash) {
for (DeterministicKeyChain chain : chains) { if (chains != null) {
DeterministicKey key; for (DeterministicKeyChain chain : chains) {
if ((key = chain.markPubHashAsUsed(pubKeyHash)) != null) { DeterministicKey key;
maybeMarkCurrentKeyAsUsed(key); if ((key = chain.markPubHashAsUsed(pubKeyHash)) != null) {
return; maybeMarkCurrentKeyAsUsed(key);
return;
}
} }
} }
} }
@ -446,9 +464,10 @@ public class KeyChainGroup implements KeyBag {
public boolean hasKey(ECKey key) { public boolean hasKey(ECKey key) {
if (basic.hasKey(key)) if (basic.hasKey(key))
return true; return true;
for (DeterministicKeyChain chain : chains) if (chains != null)
if (chain.hasKey(key)) for (DeterministicKeyChain chain : chains)
return true; if (chain.hasKey(key))
return true;
return false; return false;
} }
@ -458,10 +477,10 @@ public class KeyChainGroup implements KeyBag {
ECKey result; ECKey result;
if ((result = basic.findKeyFromPubKey(pubKey)) != null) if ((result = basic.findKeyFromPubKey(pubKey)) != null)
return result; return result;
for (DeterministicKeyChain chain : chains) { if (chains != null)
if ((result = chain.findKeyFromPubKey(pubKey)) != null) for (DeterministicKeyChain chain : chains)
return result; if ((result = chain.findKeyFromPubKey(pubKey)) != null)
} return result;
return null; return null;
} }
@ -470,11 +489,13 @@ public class KeyChainGroup implements KeyBag {
* See {@link DeterministicKeyChain#markKeyAsUsed(DeterministicKey)} for more info on this. * See {@link DeterministicKeyChain#markKeyAsUsed(DeterministicKey)} for more info on this.
*/ */
public void markPubKeyAsUsed(byte[] pubkey) { public void markPubKeyAsUsed(byte[] pubkey) {
for (DeterministicKeyChain chain : chains) { if (chains != null) {
DeterministicKey key; for (DeterministicKeyChain chain : chains) {
if ((key = chain.markPubKeyAsUsed(pubkey)) != null) { DeterministicKey key;
maybeMarkCurrentKeyAsUsed(key); if ((key = chain.markPubKeyAsUsed(pubkey)) != null) {
return; maybeMarkCurrentKeyAsUsed(key);
return;
}
} }
} }
} }
@ -482,8 +503,9 @@ public class KeyChainGroup implements KeyBag {
/** Returns the number of keys managed by this group, including the lookahead buffers. */ /** Returns the number of keys managed by this group, including the lookahead buffers. */
public int numKeys() { public int numKeys() {
int result = basic.numKeys(); int result = basic.numKeys();
for (DeterministicKeyChain chain : chains) if (chains != null)
result += chain.numKeys(); for (DeterministicKeyChain chain : chains)
result += chain.numKeys();
return result; return result;
} }
@ -503,7 +525,7 @@ public class KeyChainGroup implements KeyBag {
* @see org.bitcoinj.wallet.MarriedKeyChain * @see org.bitcoinj.wallet.MarriedKeyChain
*/ */
public final boolean isMarried() { public final boolean isMarried() {
return !chains.isEmpty() && getActiveKeyChain().isMarried(); return chains != null && !chains.isEmpty() && getActiveKeyChain().isMarried();
} }
/** /**
@ -520,13 +542,14 @@ public class KeyChainGroup implements KeyBag {
// This code must be exception safe. // This code must be exception safe.
BasicKeyChain newBasic = basic.toEncrypted(keyCrypter, aesKey); BasicKeyChain newBasic = basic.toEncrypted(keyCrypter, aesKey);
List<DeterministicKeyChain> newChains = new ArrayList<>(chains.size()); List<DeterministicKeyChain> newChains = new ArrayList<>(chains.size());
if (chains.isEmpty() && basic.numKeys() == 0) { if (chains != null && chains.isEmpty() && basic.numKeys() == 0) {
// No HD chains and no random keys: encrypting an entirely empty keychain group. But we can't do that, we // No HD chains and no random keys: encrypting an entirely empty keychain group. But we can't do that, we
// must have something to encrypt: so instantiate a new HD chain here. // must have something to encrypt: so instantiate a new HD chain here.
createAndActivateNewHDChain(); createAndActivateNewHDChain();
} }
for (DeterministicKeyChain chain : chains) if (chains != null)
newChains.add(chain.toEncrypted(keyCrypter, aesKey)); for (DeterministicKeyChain chain : chains)
newChains.add(chain.toEncrypted(keyCrypter, aesKey));
this.keyCrypter = keyCrypter; this.keyCrypter = keyCrypter;
basic = newBasic; basic = newBasic;
chains.clear(); chains.clear();
@ -544,8 +567,9 @@ public class KeyChainGroup implements KeyBag {
checkNotNull(aesKey); checkNotNull(aesKey);
BasicKeyChain newBasic = basic.toDecrypted(aesKey); BasicKeyChain newBasic = basic.toDecrypted(aesKey);
List<DeterministicKeyChain> newChains = new ArrayList<>(chains.size()); List<DeterministicKeyChain> newChains = new ArrayList<>(chains.size());
for (DeterministicKeyChain chain : chains) if (chains != null)
newChains.add(chain.toDecrypted(aesKey)); for (DeterministicKeyChain chain : chains)
newChains.add(chain.toDecrypted(aesKey));
this.keyCrypter = null; this.keyCrypter = null;
basic = newBasic; basic = newBasic;
@ -567,7 +591,7 @@ public class KeyChainGroup implements KeyBag {
public boolean isWatching() { public boolean isWatching() {
BasicKeyChain.State basicState = basic.isWatching(); BasicKeyChain.State basicState = basic.isWatching();
BasicKeyChain.State activeState = BasicKeyChain.State.EMPTY; BasicKeyChain.State activeState = BasicKeyChain.State.EMPTY;
if (!chains.isEmpty()) { if (chains != null && !chains.isEmpty()) {
if (getActiveKeyChain().isWatching()) if (getActiveKeyChain().isWatching())
activeState = BasicKeyChain.State.WATCHING; activeState = BasicKeyChain.State.WATCHING;
else else
@ -598,16 +622,17 @@ public class KeyChainGroup implements KeyBag {
public long getEarliestKeyCreationTime() { public long getEarliestKeyCreationTime() {
long time = basic.getEarliestKeyCreationTime(); // Long.MAX_VALUE if empty. long time = basic.getEarliestKeyCreationTime(); // Long.MAX_VALUE if empty.
for (DeterministicKeyChain chain : chains) if (chains != null)
time = Math.min(time, chain.getEarliestKeyCreationTime()); for (DeterministicKeyChain chain : chains)
time = Math.min(time, chain.getEarliestKeyCreationTime());
return time; return time;
} }
public int getBloomFilterElementCount() { public int getBloomFilterElementCount() {
int result = basic.numBloomFilterEntries(); int result = basic.numBloomFilterEntries();
for (DeterministicKeyChain chain : chains) { if (chains != null)
result += chain.numBloomFilterEntries(); for (DeterministicKeyChain chain : chains)
} result += chain.numBloomFilterEntries();
return result; return result;
} }
@ -615,10 +640,9 @@ public class KeyChainGroup implements KeyBag {
BloomFilter filter = new BloomFilter(size, falsePositiveRate, nTweak); BloomFilter filter = new BloomFilter(size, falsePositiveRate, nTweak);
if (basic.numKeys() > 0) if (basic.numKeys() > 0)
filter.merge(basic.getFilter(size, falsePositiveRate, nTweak)); filter.merge(basic.getFilter(size, falsePositiveRate, nTweak));
if (chains != null)
for (DeterministicKeyChain chain : chains) { for (DeterministicKeyChain chain : chains)
filter.merge(chain.getFilter(size, falsePositiveRate, nTweak)); filter.merge(chain.getFilter(size, falsePositiveRate, nTweak));
}
return filter; return filter;
} }
@ -636,15 +660,17 @@ public class KeyChainGroup implements KeyBag {
checkNotNull(listener); checkNotNull(listener);
checkNotNull(executor); checkNotNull(executor);
basic.addEventListener(listener, executor); basic.addEventListener(listener, executor);
for (DeterministicKeyChain chain : chains) if (chains != null)
chain.addEventListener(listener, executor); for (DeterministicKeyChain chain : chains)
chain.addEventListener(listener, executor);
} }
/** Removes a listener for events that are run when keys are added. */ /** Removes a listener for events that are run when keys are added. */
public boolean removeEventListener(KeyChainEventListener listener) { public boolean removeEventListener(KeyChainEventListener listener) {
checkNotNull(listener); checkNotNull(listener);
for (DeterministicKeyChain chain : chains) if (chains != null)
chain.removeEventListener(listener); for (DeterministicKeyChain chain : chains)
chain.removeEventListener(listener);
return basic.removeEventListener(listener); return basic.removeEventListener(listener);
} }
@ -655,10 +681,9 @@ public class KeyChainGroup implements KeyBag {
result = basic.serializeToProtobuf(); result = basic.serializeToProtobuf();
else else
result = Lists.newArrayList(); result = Lists.newArrayList();
for (DeterministicKeyChain chain : chains) { if (chains != null)
List<Protos.Key> protos = chain.serializeToProtobuf(); for (DeterministicKeyChain chain : chains)
result.addAll(protos); result.addAll(chain.serializeToProtobuf());
}
return result; return result;
} }
@ -709,6 +734,7 @@ public class KeyChainGroup implements KeyBag {
* @return the DeterministicKeyChain that was created by the upgrade. * @return the DeterministicKeyChain that was created by the upgrade.
*/ */
public DeterministicKeyChain upgradeToDeterministic(long keyRotationTimeSecs, @Nullable KeyParameter aesKey) throws DeterministicUpgradeRequiresPassword, AllRandomKeysRotating { public DeterministicKeyChain upgradeToDeterministic(long keyRotationTimeSecs, @Nullable KeyParameter aesKey) throws DeterministicUpgradeRequiresPassword, AllRandomKeysRotating {
checkState(isSupportsDeterministicChains(), "doesn't support deterministic chains");
checkState(basic.numKeys() > 0); checkState(basic.numKeys() > 0);
checkArgument(keyRotationTimeSecs >= 0); checkArgument(keyRotationTimeSecs >= 0);
// Subtract one because the key rotation time might have been set to the creation time of the first known good // Subtract one because the key rotation time might have been set to the creation time of the first known good
@ -764,7 +790,7 @@ public class KeyChainGroup implements KeyBag {
/** Returns true if the group contains random keys but no HD chains. */ /** Returns true if the group contains random keys but no HD chains. */
public boolean isDeterministicUpgradeRequired() { public boolean isDeterministicUpgradeRequired() {
return basic.numKeys() > 0 && chains.isEmpty(); return basic.numKeys() > 0 && chains != null && chains.isEmpty();
} }
private static EnumMap<KeyChain.KeyPurpose, DeterministicKey> createCurrentKeysMap(List<DeterministicKeyChain> chains) { private static EnumMap<KeyChain.KeyPurpose, DeterministicKey> createCurrentKeysMap(List<DeterministicKeyChain> chains) {
@ -814,13 +840,15 @@ public class KeyChainGroup implements KeyBag {
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
if (basic != null) if (basic != null)
builder.append(basic.toString(includePrivateKeys, aesKey, params)); builder.append(basic.toString(includePrivateKeys, aesKey, params));
for (DeterministicKeyChain chain : chains) if (chains != null)
builder.append(chain.toString(includePrivateKeys, aesKey, params)).append('\n'); for (DeterministicKeyChain chain : chains)
builder.append(chain.toString(includePrivateKeys, aesKey, params)).append('\n');
return builder.toString(); return builder.toString();
} }
/** Returns a copy of the current list of chains. */ /** Returns a copy of the current list of chains. */
public List<DeterministicKeyChain> getDeterministicKeyChains() { public List<DeterministicKeyChain> getDeterministicKeyChains() {
checkState(isSupportsDeterministicChains(), "doesn't support deterministic chains");
return new ArrayList<>(chains); return new ArrayList<>(chains);
} }
/** /**
@ -828,6 +856,7 @@ public class KeyChainGroup implements KeyBag {
* lookahead and thus the Bloom filter that was previously calculated has become stale. * lookahead and thus the Bloom filter that was previously calculated has become stale.
*/ */
public int getCombinedKeyLookaheadEpochs() { public int getCombinedKeyLookaheadEpochs() {
checkState(isSupportsDeterministicChains(), "doesn't support deterministic chains");
int epoch = 0; int epoch = 0;
for (DeterministicKeyChain chain : chains) for (DeterministicKeyChain chain : chains)
epoch += chain.getKeyLookaheadEpoch(); epoch += chain.getKeyLookaheadEpoch();

View File

@ -265,6 +265,15 @@ public class Wallet extends BaseTaggableObject
this(context, new KeyChainGroup(context.getParams())); this(context, new KeyChainGroup(context.getParams()));
} }
/**
* Creates a new, empty wallet with just a basic keychain and no transactions. No deterministic chains will be created
* automatically. This is meant for when you just want to import a few keys and operate on them.
* @param params network parameters
*/
public static Wallet createBasic(NetworkParameters params) {
return new Wallet(params, KeyChainGroup.createBasic(params));
}
/** /**
* @param params network parameters * @param params network parameters
* @param seed deterministic seed * @param seed deterministic seed
@ -352,12 +361,13 @@ public class Wallet extends BaseTaggableObject
this.context = checkNotNull(context); this.context = checkNotNull(context);
this.params = checkNotNull(context.getParams()); this.params = checkNotNull(context.getParams());
this.keyChainGroup = checkNotNull(keyChainGroup); this.keyChainGroup = checkNotNull(keyChainGroup);
if (params.getId().equals(NetworkParameters.ID_UNITTESTNET)) if (this.keyChainGroup.isSupportsDeterministicChains()
&& params.getId().equals(NetworkParameters.ID_UNITTESTNET))
this.keyChainGroup.setLookaheadSize(5); // Cut down excess computation for unit tests. this.keyChainGroup.setLookaheadSize(5); // Cut down excess computation for unit tests.
// If this keyChainGroup was created fresh just now (new wallet), make HD so a backup can be made immediately // If this keyChainGroup was created fresh just now (new wallet), make HD so a backup can be made immediately
// without having to call current/freshReceiveKey. If there are already keys in the chain of any kind then // without having to call current/freshReceiveKey. If there are already keys in the chain of any kind then
// we're probably being deserialized so leave things alone: the API user can upgrade later. // we're probably being deserialized so leave things alone: the API user can upgrade later.
if (this.keyChainGroup.numKeys() == 0) if (this.keyChainGroup.isSupportsDeterministicChains() && this.keyChainGroup.numKeys() == 0)
this.keyChainGroup.createAndActivateNewHDChain(); this.keyChainGroup.createAndActivateNewHDChain();
watchedScripts = Sets.newHashSet(); watchedScripts = Sets.newHashSet();
unspent = new HashMap<>(); unspent = new HashMap<>();

View File

@ -134,6 +134,20 @@ public class WalletTest extends TestWithWallet {
wallet.addAndActivateHDChain(chain); wallet.addAndActivateHDChain(chain);
} }
@Test
public void createBasic() {
Wallet wallet = Wallet.createBasic(UNITTEST);
assertEquals(0, wallet.getKeyChainGroupSize());
wallet.importKey(new ECKey());
assertEquals(1, wallet.getKeyChainGroupSize());
}
@Test(expected = IllegalStateException.class)
public void createBasic_noDerivation() {
Wallet wallet = Wallet.createBasic(UNITTEST);
wallet.currentReceiveAddress();
}
@Test @Test
public void getSeedAsWords1() { public void getSeedAsWords1() {
// Can't verify much here as the wallet is random each time. We could fix the RNG for the unit tests and solve. // Can't verify much here as the wallet is random each time. We could fix the RNG for the unit tests and solve.