mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-02-25 07:07:39 +01:00
Arbitrary number of key creation (batch)
Functionality added to create keys in batch (any arbitary number), previously keys were created/returned one at a time.
This commit is contained in:
parent
638e921e53
commit
4e568354be
5 changed files with 86 additions and 10 deletions
|
@ -329,13 +329,25 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||
* to someone who wishes to send money.
|
||||
*/
|
||||
public DeterministicKey freshKey(KeyChain.KeyPurpose purpose) {
|
||||
return freshKeys(purpose, 1).get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a key/s that has not been returned by this method before (fresh). You can think of this as being
|
||||
* a newly created key/s, although the notion of "create" is not really valid for a
|
||||
* {@link com.google.bitcoin.wallet.DeterministicKeyChain}. When the parameter is
|
||||
* {@link com.google.bitcoin.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS} the returned key is suitable for being put
|
||||
* into a receive coins wizard type UI. You should use this when the user is definitely going to hand this key/s out
|
||||
* to someone who wishes to send money.
|
||||
*/
|
||||
public List<DeterministicKey> freshKeys(KeyChain.KeyPurpose purpose, int numberOfKeys) {
|
||||
lock.lock();
|
||||
try {
|
||||
DeterministicKey key = keychain.freshKey(purpose);
|
||||
List<DeterministicKey> keys = keychain.freshKeys(purpose, numberOfKeys);
|
||||
// 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.
|
||||
saveNow();
|
||||
return key;
|
||||
return keys;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
|
|
@ -89,6 +89,36 @@ public class BasicKeyChain implements EncryptableKeyChain {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ECKey> getKeys(@Nullable KeyPurpose purpose, int numberOfKeys) {
|
||||
checkArgument(numberOfKeys > 0);
|
||||
lock.lock();
|
||||
try {
|
||||
if (hashToKeys.size() < numberOfKeys) {
|
||||
checkState(keyCrypter == null);
|
||||
|
||||
List<ECKey> keys = new ArrayList<ECKey>();
|
||||
for (int i = 0; i < numberOfKeys - hashToKeys.size(); i++) {
|
||||
keys.add(new ECKey());
|
||||
}
|
||||
|
||||
ImmutableList<ECKey> immutableKeys = ImmutableList.copyOf(keys);
|
||||
importKeysLocked(immutableKeys);
|
||||
queueOnKeysAdded(immutableKeys);
|
||||
}
|
||||
|
||||
List<ECKey> keysToReturn = new ArrayList<ECKey>();
|
||||
int count = 0;
|
||||
while (hashToKeys.values().iterator().hasNext() && numberOfKeys != count) {
|
||||
keysToReturn.add(hashToKeys.values().iterator().next());
|
||||
count++;
|
||||
}
|
||||
return keysToReturn;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a copy of the list of keys that this chain is managing. */
|
||||
public List<ECKey> getKeys() {
|
||||
lock.lock();
|
||||
|
@ -141,6 +171,12 @@ public class BasicKeyChain implements EncryptableKeyChain {
|
|||
hashToKeys.put(ByteString.copyFrom(key.getPubKeyHash()), key);
|
||||
}
|
||||
|
||||
private void importKeysLocked(List<ECKey> keys) {
|
||||
for (ECKey key : keys) {
|
||||
importKeyLocked(key);
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ void importKey(ECKey key) {
|
||||
lock.lock();
|
||||
try {
|
||||
|
|
|
@ -262,9 +262,16 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
/** Returns a freshly derived key that has not been returned by this method before. */
|
||||
@Override
|
||||
public DeterministicKey getKey(KeyPurpose purpose) {
|
||||
return getKeys(purpose,1).get(0);
|
||||
}
|
||||
|
||||
/** Returns freshly derived key/s that have not been returned by this method before. */
|
||||
@Override
|
||||
public List<DeterministicKey> getKeys(KeyPurpose purpose,int numberOfKeys) {
|
||||
checkArgument(numberOfKeys > 0);
|
||||
lock.lock();
|
||||
try {
|
||||
DeterministicKey key, parentKey;
|
||||
DeterministicKey parentKey;
|
||||
int index;
|
||||
switch (purpose) {
|
||||
// Map both REFUND and RECEIVE_KEYS to the same branch for now. Refunds are a feature of the BIP 70
|
||||
|
@ -273,11 +280,13 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
// (i.e. spends) or refunds. Might be useful for auditing ...
|
||||
case RECEIVE_FUNDS:
|
||||
case REFUND:
|
||||
index = ++issuedExternalKeys;
|
||||
issuedExternalKeys += numberOfKeys;
|
||||
index = issuedExternalKeys;
|
||||
parentKey = externalKey;
|
||||
break;
|
||||
case CHANGE:
|
||||
index = ++issuedInternalKeys;
|
||||
issuedInternalKeys += numberOfKeys;
|
||||
index = issuedInternalKeys;
|
||||
parentKey = internalKey;
|
||||
break;
|
||||
default:
|
||||
|
@ -286,8 +295,12 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
|||
// TODO: Handle the case where the derived key is >= curve order.
|
||||
List<DeterministicKey> lookahead = maybeLookAhead(parentKey, index);
|
||||
basicKeyChain.importKeys(lookahead);
|
||||
key = hierarchy.get(HDUtils.append(parentKey.getPath(), new ChildNumber(index - 1, false)), false, false);
|
||||
return key;
|
||||
List<DeterministicKey> keys = new ArrayList<DeterministicKey>(numberOfKeys);
|
||||
|
||||
for (int i = 1; i <= numberOfKeys; i++) {
|
||||
keys.add(hierarchy.get(HDUtils.append(parentKey.getPath(), new ChildNumber((index-numberOfKeys+i) - 1, false)), false, false));
|
||||
}
|
||||
return keys;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
|
|
@ -44,6 +44,9 @@ public interface KeyChain extends KeyBag {
|
|||
REFUND
|
||||
}
|
||||
|
||||
/** Obtains a number of key/s intended for the given purpose. The chain may create new key/s, derive, or re-use an old one. */
|
||||
public List<? extends ECKey> getKeys(KeyPurpose purpose, int numberOfKeys);
|
||||
|
||||
/** Obtains a key intended for the given purpose. The chain may create a new key, derive one, or re-use an old one. */
|
||||
public ECKey getKey(KeyPurpose purpose);
|
||||
|
||||
|
|
|
@ -134,10 +134,22 @@ public class KeyChainGroup {
|
|||
* to someone who wishes to send money.
|
||||
*/
|
||||
public DeterministicKey freshKey(KeyChain.KeyPurpose purpose) {
|
||||
return freshKeys(purpose,1).get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a key/s that have not been returned by this method before (fresh). You can think of this as being
|
||||
* newly created key/s, although the notion of "create" is not really valid for a
|
||||
* {@link com.google.bitcoin.wallet.DeterministicKeyChain}. When the parameter is
|
||||
* {@link com.google.bitcoin.wallet.KeyChain.KeyPurpose#RECEIVE_FUNDS} the returned key is suitable for being put
|
||||
* 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.
|
||||
*/
|
||||
public List<DeterministicKey> freshKeys(KeyChain.KeyPurpose purpose, int numberOfKeys) {
|
||||
DeterministicKeyChain chain = getActiveKeyChain();
|
||||
DeterministicKey key = chain.getKey(purpose); // Always returns the next key along the key chain.
|
||||
currentKeys.put(purpose, key);
|
||||
return key;
|
||||
List<DeterministicKey> keys = chain.getKeys(purpose, numberOfKeys); // Always returns the next key along the key chain.
|
||||
currentKeys.put(purpose, keys.get(keys.size() - 1));
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue