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.
|
* to someone who wishes to send money.
|
||||||
*/
|
*/
|
||||||
public DeterministicKey freshKey(KeyChain.KeyPurpose purpose) {
|
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();
|
lock.lock();
|
||||||
try {
|
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
|
// 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();
|
||||||
return key;
|
return keys;
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
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. */
|
/** Returns a copy of the list of keys that this chain is managing. */
|
||||||
public List<ECKey> getKeys() {
|
public List<ECKey> getKeys() {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
|
@ -141,6 +171,12 @@ public class BasicKeyChain implements EncryptableKeyChain {
|
||||||
hashToKeys.put(ByteString.copyFrom(key.getPubKeyHash()), key);
|
hashToKeys.put(ByteString.copyFrom(key.getPubKeyHash()), key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void importKeysLocked(List<ECKey> keys) {
|
||||||
|
for (ECKey key : keys) {
|
||||||
|
importKeyLocked(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* package */ void importKey(ECKey key) {
|
/* package */ void importKey(ECKey key) {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -262,9 +262,16 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||||
/** Returns a freshly derived key that has not been returned by this method before. */
|
/** Returns a freshly derived key that has not been returned by this method before. */
|
||||||
@Override
|
@Override
|
||||||
public DeterministicKey getKey(KeyPurpose purpose) {
|
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();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
DeterministicKey key, parentKey;
|
DeterministicKey parentKey;
|
||||||
int index;
|
int index;
|
||||||
switch (purpose) {
|
switch (purpose) {
|
||||||
// Map both REFUND and RECEIVE_KEYS to the same branch for now. Refunds are a feature of the BIP 70
|
// 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 ...
|
// (i.e. spends) or refunds. Might be useful for auditing ...
|
||||||
case RECEIVE_FUNDS:
|
case RECEIVE_FUNDS:
|
||||||
case REFUND:
|
case REFUND:
|
||||||
index = ++issuedExternalKeys;
|
issuedExternalKeys += numberOfKeys;
|
||||||
|
index = issuedExternalKeys;
|
||||||
parentKey = externalKey;
|
parentKey = externalKey;
|
||||||
break;
|
break;
|
||||||
case CHANGE:
|
case CHANGE:
|
||||||
index = ++issuedInternalKeys;
|
issuedInternalKeys += numberOfKeys;
|
||||||
|
index = issuedInternalKeys;
|
||||||
parentKey = internalKey;
|
parentKey = internalKey;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -286,8 +295,12 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
|
||||||
// TODO: Handle the case where the derived key is >= curve order.
|
// TODO: Handle the case where the derived key is >= curve order.
|
||||||
List<DeterministicKey> lookahead = maybeLookAhead(parentKey, index);
|
List<DeterministicKey> lookahead = maybeLookAhead(parentKey, index);
|
||||||
basicKeyChain.importKeys(lookahead);
|
basicKeyChain.importKeys(lookahead);
|
||||||
key = hierarchy.get(HDUtils.append(parentKey.getPath(), new ChildNumber(index - 1, false)), false, false);
|
List<DeterministicKey> keys = new ArrayList<DeterministicKey>(numberOfKeys);
|
||||||
return key;
|
|
||||||
|
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 {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,9 @@ public interface KeyChain extends KeyBag {
|
||||||
REFUND
|
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. */
|
/** 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);
|
public ECKey getKey(KeyPurpose purpose);
|
||||||
|
|
||||||
|
|
|
@ -134,10 +134,22 @@ public class KeyChainGroup {
|
||||||
* to someone who wishes to send money.
|
* to someone who wishes to send money.
|
||||||
*/
|
*/
|
||||||
public DeterministicKey freshKey(KeyChain.KeyPurpose purpose) {
|
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();
|
DeterministicKeyChain chain = getActiveKeyChain();
|
||||||
DeterministicKey key = chain.getKey(purpose); // Always returns the next key along the key chain.
|
List<DeterministicKey> keys = chain.getKeys(purpose, numberOfKeys); // Always returns the next key along the key chain.
|
||||||
currentKeys.put(purpose, key);
|
currentKeys.put(purpose, keys.get(keys.size() - 1));
|
||||||
return key;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Reference in a new issue