HD Wallets: mark the DeterministicKeys as issued, if seen in a Transaction

If a Transaction contains a DeterministicKey of our
DeterministicKeyChains, then we should mark this key as issued. This can
happen, when we replay/resync the blockchain or when another device uses
one of our keys.

Signed-off-by: Harald Hoyer <harald@harald-hoyer.de>
This commit is contained in:
Harald Hoyer 2014-04-11 11:58:14 +02:00 committed by Mike Hearn
parent 534a1e3a5c
commit 5726b97f70
3 changed files with 107 additions and 0 deletions

View File

@ -637,6 +637,33 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
return findKeyFromPubKey(pubkey) != null;
}
/**
* Marks all keys used in the transaction output as used in the wallet.
* See {@link com.google.bitcoin.wallet.DeterministicKeyChain#markKeyAsUsed(DeterministicKey)} for more info on this.
*/
private void markKeysAsUsed(Transaction tx) {
lock.lock();
try {
for (TransactionOutput o : tx.getOutputs()) {
try {
Script script = o.getScriptPubKey();
if (script.isSentToRawPubKey()) {
byte[] pubkey = script.getPubKey();
keychain.markPubKeyAsUsed(pubkey);
} else if (script.isSentToAddress()) {
byte[] pubkeyHash = script.getPubKeyHash();
keychain.markPubKeyHashAsUsed(pubkeyHash);
}
} catch (ScriptException e) {
// Just means we didn't understand the output of this transaction: ignore it.
log.warn("Could not parse tx output script: {}", e.toString());
}
}
} finally {
lock.unlock();
}
}
/**
* Returns the immutable seed for the current active HD chain.
* @throws com.google.bitcoin.core.ECKey.MissingPrivateKeyException if the seed is unavailable (watching wallet)
@ -1230,6 +1257,9 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
bitcoinValueToFriendlyString(valueDifference), tx.getHashAsString(), relativityOffset,
block != null ? block.getHeader().getHash() : "(unit test)");
// mark the deterministic keys in this transaction as used
markKeysAsUsed(tx);
onWalletChangedSuppressions++;
// If this transaction is already in the wallet we may need to move it into a different pool. At the very

View File

@ -295,6 +295,28 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
basicKeyChain.importKeys(ImmutableList.of(key));
}
/**
* Mark the DeterministicKey as used.
* Also correct the issued{Internal|External}Keys counter, because all lower children seem to be requested already.
* If the counter was updated, we also might want to update the lookahead keys.
*/
public DeterministicKey markKeyAsUsed(DeterministicKey k) {
int numchilds = k.getChildNumber().i() + 1;
if (k.getParent() == internalKey) {
if (issuedInternalKeys < numchilds) {
issuedInternalKeys = numchilds;
maybeLookAhead();
}
} else if (k.getParent() == externalKey) {
if (issuedExternalKeys < numchilds) {
issuedExternalKeys = numchilds;
maybeLookAhead();
}
}
return k;
}
@Override
public DeterministicKey findKeyFromPubHash(byte[] pubkeyHash) {
lock.lock();
@ -315,6 +337,38 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
}
}
/**
* Mark the DeterministicKeys as used, if they match the pubkeyHash
* See {@link com.google.bitcoin.wallet.DeterministicKeyChain#markKeyAsUsed(DeterministicKey)} for more info on this.
*/
public boolean markPubHashAsUsed(byte[] pubkeyHash) {
lock.lock();
try {
DeterministicKey k = (DeterministicKey) basicKeyChain.findKeyFromPubHash(pubkeyHash);
if (k != null)
markKeyAsUsed(k);
return k != null;
} finally {
lock.unlock();
}
}
/**
* Mark the DeterministicKeys as used, if they match the pubkey
* See {@link com.google.bitcoin.wallet.DeterministicKeyChain#markKeyAsUsed(DeterministicKey)} for more info on this.
*/
public boolean markPubKeyAsUsed(byte[] pubkey) {
lock.lock();
try {
DeterministicKey k = (DeterministicKey) basicKeyChain.findKeyFromPubKey(pubkey);
if (k != null)
markKeyAsUsed(k);
return k != null;
} finally {
lock.unlock();
}
}
@Override
public boolean hasKey(ECKey key) {
lock.lock();

View File

@ -227,6 +227,18 @@ public class KeyChainGroup {
return null;
}
/**
* Mark the DeterministicKeys as used, if they match the pubkeyHash
* See {@link com.google.bitcoin.wallet.DeterministicKeyChain#markKeyAsUsed(DeterministicKey)} for more info on this.
*/
public void markPubKeyHashAsUsed(byte[] pubkeyHash) {
for (DeterministicKeyChain chain : chains) {
if (chain.markPubHashAsUsed(pubkeyHash))
return;
}
}
public boolean hasKey(ECKey key) {
if (basic.hasKey(key))
return true;
@ -248,6 +260,17 @@ public class KeyChainGroup {
return null;
}
/**
* Mark the DeterministicKeys as used, if they match the pubkey
* See {@link com.google.bitcoin.wallet.DeterministicKeyChain#markKeyAsUsed(DeterministicKey)} for more info on this.
*/
public void markPubKeyAsUsed(byte[] pubkey) {
for (DeterministicKeyChain chain : chains) {
if (chain.markPubKeyAsUsed(pubkey))
return;
}
}
/** Returns the number of keys managed by this group, including the lookahead buffers. */
public int numKeys() {
int result = basic.numKeys();