diff --git a/core/src/main/java/com/google/bitcoin/core/Wallet.java b/core/src/main/java/com/google/bitcoin/core/Wallet.java index 862a06d7..10e207c2 100644 --- a/core/src/main/java/com/google/bitcoin/core/Wallet.java +++ b/core/src/main/java/com/google/bitcoin/core/Wallet.java @@ -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 diff --git a/core/src/main/java/com/google/bitcoin/wallet/DeterministicKeyChain.java b/core/src/main/java/com/google/bitcoin/wallet/DeterministicKeyChain.java index 6fb897eb..c83a77cc 100644 --- a/core/src/main/java/com/google/bitcoin/wallet/DeterministicKeyChain.java +++ b/core/src/main/java/com/google/bitcoin/wallet/DeterministicKeyChain.java @@ -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(); diff --git a/core/src/main/java/com/google/bitcoin/wallet/KeyChainGroup.java b/core/src/main/java/com/google/bitcoin/wallet/KeyChainGroup.java index 79ec433e..5a0465d1 100644 --- a/core/src/main/java/com/google/bitcoin/wallet/KeyChainGroup.java +++ b/core/src/main/java/com/google/bitcoin/wallet/KeyChainGroup.java @@ -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();