Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e9a5306
fix: change Wallet.lock to ReentrantReadWriteLock
HashEngineering Jul 22, 2025
3103b81
fix: use fair policy for reentrantReadWriteLock
HashEngineering Feb 2, 2026
fe9e2e7
fix: add correct read write locks to new functions
HashEngineering Feb 2, 2026
fe7291e
feat: add parallization for WalletProtobufSerializer.walletToProto
HashEngineering Feb 4, 2026
b96b582
fix: use numLeaveKeysIssued instead of numKeys to avoid maybeLookAhead
HashEngineering Feb 4, 2026
71260df
fix: use complexity to decide serial/parallel in walletToProto
HashEngineering Feb 4, 2026
eff7784
fix: add missing synchronization in TxConfidenceTable.get(StoredBlock)
HashEngineering Feb 5, 2026
b16a2be
fix: process ext and tx futures in order of completion
HashEngineering Feb 5, 2026
3cfa1d1
tests: add check for isLastSaveParallel
HashEngineering Feb 5, 2026
74415cb
fix: don't report BlockStoreException in handleChainLock
HashEngineering Feb 5, 2026
bd029a3
fix: handle RejectedExecutionException in CoinJoinManager
HashEngineering Feb 5, 2026
ca727bd
fix: make lastSaveParallel volatile
HashEngineering Feb 7, 2026
06523a5
fix: initialize mapOutpointRoundsCache size
HashEngineering Feb 7, 2026
6801fbd
tests: set "2g" for large wallet tests
HashEngineering Feb 7, 2026
f265935
fix: if there can't be a write lock, check that there isn't a read lock
HashEngineering Feb 8, 2026
e612856
Merge branch 'master' into fix/wallet-read-write-lock
HashEngineering Feb 17, 2026
102aeb3
fix: update lock system on markAsFullyMixed
HashEngineering Feb 17, 2026
838b3fc
fix: check for no read locks in sendCoinsOffline
HashEngineering Feb 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -507,10 +508,14 @@ public void processTransaction(Transaction tx) {
public final PreMessageReceivedEventListener preMessageReceivedEventListener = (peer, m) -> {
if (m instanceof CoinJoinQueue) {
// Offload DSQueue message processing to thread pool to avoid blocking network I/O thread
messageProcessingExecutor.execute(() -> {
processMessage(peer, m);
});
// Return null as dsq meessages are only processed above
try {
messageProcessingExecutor.execute(() -> {
processMessage(peer, m);
});
} catch (RejectedExecutionException e) {
// Executor was shutdown - ignore, we're shutting down
}
// Return null as dsq messages are only processed above
return null;
} else if (isCoinJoinMessage(m)) {
// Process other CoinJoin messages synchronously
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/org/bitcoinj/core/AbstractBlockChain.java
Original file line number Diff line number Diff line change
Expand Up @@ -1134,8 +1134,8 @@ public void handleChainLock(StoredBlock chainLockedBlock) {
handleNewBestChain(prevBlock, newTip, newTip.getHeader(), shouldVerifyTransactions());
}
} catch (BlockStoreException | PrunedException x) {
log.info("handle chain lock exception:", x);
// swallow
log.debug("handle chain lock exception:", x);
// swallow - expected during shutdown
}
}
} finally {
Expand Down
19 changes: 12 additions & 7 deletions core/src/main/java/org/bitcoinj/core/TxConfidenceTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -202,14 +202,19 @@ public TransactionConfidence get(Sha256Hash hash) {
}

public ArrayList<TransactionConfidence> get(StoredBlock block) {
ArrayList<TransactionConfidence> results = Lists.newArrayList();
for (Map.Entry<Sha256Hash, WeakConfidenceReference> entry: table.entrySet()) {
TransactionConfidence confidence = entry.getValue().get();
if (confidence != null && confidence.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING &&
confidence.getAppearedAtChainHeight() <= block.getHeight()) {
results.add(confidence);
lock.lock();
try {
ArrayList<TransactionConfidence> results = Lists.newArrayList();
for (Map.Entry<Sha256Hash, WeakConfidenceReference> entry: table.entrySet()) {
TransactionConfidence confidence = entry.getValue().get();
if (confidence != null && confidence.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING &&
confidence.getAppearedAtChainHeight() <= block.getHeight()) {
results.add(confidence);
}
}
return results;
} finally {
lock.unlock();
}
return results;
}
}
8 changes: 8 additions & 0 deletions core/src/main/java/org/bitcoinj/utils/Threading.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.lang.management.ThreadMXBean;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
* Various threading related utilities. Provides a wrapper around explicit lock creation that lets you control whether
Expand Down Expand Up @@ -192,6 +193,13 @@ public static CycleDetectingLockFactory.Policy getPolicy() {
return policy;
}

public static ReentrantReadWriteLock readWriteLock(String name) {
if (Utils.isAndroidRuntime() && useDefaultAndroidPolicy)
return new ReentrantReadWriteLock(true);
else
return factory.newReentrantReadWriteLock(name, true);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Generic worker pool.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -544,4 +544,9 @@ public void setWallet(Wallet wallet) {
public int getCombinedKeyLookaheadEpochs() {
return hasKeyChains() ? getKeyChainGroup().getCombinedKeyLookaheadEpochs() : 0;
}

@Override
public int getTotalIssuedKeys() {
return hasKeyChains() ? getKeyChainGroup().getTotalIssuedKeys() : 0;
}
}
11 changes: 11 additions & 0 deletions core/src/main/java/org/bitcoinj/wallet/AnyKeyChainGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,17 @@ public int numKeys() {
return result;
}

/**
* Returns number of keys used on external and internal paths.
*/
public int getTotalIssuedKeys() {
int result = basic.numKeys();
if (chains != null)
for (AnyDeterministicKeyChain chain : chains)
result += chain.numLeafKeysIssued();
return result;
}

/**
* Removes a key that was imported into the basic key chain. You cannot remove deterministic keys.
* @throws IllegalArgumentException if the key is deterministic.
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/java/org/bitcoinj/wallet/CoinJoinExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ public void deserializeWalletExtension(Wallet containingWallet, byte[] data) thr
if (containingWallet instanceof WalletEx && coinJoinProto.hasOutpointRoundsCache()) {
WalletEx walletEx = (WalletEx) containingWallet;
Protos.OutpointRoundsCache cacheProto = coinJoinProto.getOutpointRoundsCache();
// Pre-size HashMap to avoid resizing during deserialization
int entryCount = cacheProto.getEntriesCount();
walletEx.mapOutpointRoundsCache = new HashMap<>(entryCount * 4 / 3 + 1);
for (Protos.OutpointRoundsEntry entryProto : cacheProto.getEntriesList()) {
Sha256Hash txHash = Sha256Hash.wrap(entryProto.getTransactionHash().toByteArray());
long outputIndex = entryProto.getOutputIndex();
Expand Down
11 changes: 11 additions & 0 deletions core/src/main/java/org/bitcoinj/wallet/KeyChainGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,17 @@ public int numKeys() {
return result;
}

/**
* Returns number of keys used on external and internal paths.
*/
public int getTotalIssuedKeys() {
int result = basic.numKeys();
if (chains != null)
for (DeterministicKeyChain chain : chains)
result += chain.numLeafKeysIssued();
return result;
}

/**
* Removes a key that was imported into the basic key chain. You cannot remove deterministic keys.
* @throws java.lang.IllegalArgumentException if the key is deterministic.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,5 @@ default String toString(boolean includeLookahead, boolean includePrivateKeys, @N
boolean isTransactionRevelant(Transaction tx);

int getCombinedKeyLookaheadEpochs();
int getTotalIssuedKeys();
}
Loading
Loading