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:
parent
6278dc39b1
commit
d35583236a
@ -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();
|
||||||
|
@ -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<>();
|
||||||
|
@ -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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user