From b578adf55d849b4143d6553edd256a9c805cd694 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Thu, 13 Nov 2014 22:30:35 +0100 Subject: [PATCH] Wallet: give up on using read/write locks for the keychain, the re-entrancy rules are too hard to follow. Switch back to a regular lock. --- .../main/java/org/bitcoinj/core/Wallet.java | 183 +++++++++--------- 1 file changed, 93 insertions(+), 90 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/core/Wallet.java b/core/src/main/java/org/bitcoinj/core/Wallet.java index f0aa58c1..41ea1943 100644 --- a/core/src/main/java/org/bitcoinj/core/Wallet.java +++ b/core/src/main/java/org/bitcoinj/core/Wallet.java @@ -109,7 +109,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha // Ordering: lock > keychainLock. Keychain is protected separately to allow fast querying of current receive address // even if the wallet itself is busy e.g. saving or processing a big reorg. Useful for reducing UI latency. protected final ReentrantLock lock = Threading.lock("wallet"); - protected final ReentrantReadWriteLock keychainLock = Threading.factory.newReentrantReadWriteLock("wallet-keychain"); + protected final ReentrantLock keychainLock = Threading.lock("wallet-keychain"); // The various pools below give quick access to wallet-relevant transactions by the state they're in: // @@ -340,12 +340,12 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha * a different key (for each purpose independently). */ public DeterministicKey currentKey(KeyChain.KeyPurpose purpose) { - keychainLock.readLock().lock(); + keychainLock.lock(); try { maybeUpgradeToHD(); return keychain.currentKey(purpose); } finally { - keychainLock.readLock().unlock(); + keychainLock.unlock(); } } @@ -361,12 +361,12 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha * Returns address for a {@link #currentKey(org.bitcoinj.wallet.KeyChain.KeyPurpose)} */ public Address currentAddress(KeyChain.KeyPurpose purpose) { - keychainLock.readLock().lock(); + keychainLock.lock(); try { maybeUpgradeToHD(); return keychain.currentAddress(purpose); } finally { - keychainLock.readLock().unlock(); + keychainLock.unlock(); } } @@ -400,12 +400,12 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha */ public List freshKeys(KeyChain.KeyPurpose purpose, int numberOfKeys) { List keys; - keychainLock.writeLock().lock(); + keychainLock.lock(); try { maybeUpgradeToHD(); keys = keychain.freshKeys(purpose, numberOfKeys); } finally { - keychainLock.writeLock().unlock(); + keychainLock.unlock(); } // 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. @@ -426,11 +426,11 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha */ public Address freshAddress(KeyChain.KeyPurpose purpose) { Address key; - keychainLock.writeLock().lock(); + keychainLock.lock(); try { key = keychain.freshAddress(purpose); } finally { - keychainLock.writeLock().unlock(); + keychainLock.unlock(); } saveNow(); return key; @@ -453,11 +453,11 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha * you automatically the first time a new key is requested (this happens when spending due to the change address). */ public void upgradeToDeterministic(@Nullable KeyParameter aesKey) throws DeterministicUpgradeRequiresPassword { - keychainLock.writeLock().lock(); + keychainLock.lock(); try { keychain.upgradeToDeterministic(vKeyRotationTimestamp, aesKey); } finally { - keychainLock.writeLock().unlock(); + keychainLock.unlock(); } } @@ -467,11 +467,11 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha * that would require a new address or key. */ public boolean isDeterministicUpgradeRequired() { - keychainLock.readLock().lock(); + keychainLock.lock(); try { return keychain.isDeterministicUpgradeRequired(); } finally { - keychainLock.readLock().unlock(); + keychainLock.unlock(); } } @@ -479,7 +479,9 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha maybeUpgradeToHD(null); } + @GuardedBy("keychainLock") private void maybeUpgradeToHD(@Nullable KeyParameter aesKey) throws DeterministicUpgradeRequiresPassword { + checkState(keychainLock.isHeldByCurrentThread()); if (keychain.isDeterministicUpgradeRequired()) { log.info("Upgrade to HD wallets is required, attempting to do so."); try { @@ -496,11 +498,11 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha * Returns a snapshot of the watched scripts. This view is not live. */ public List