From e11e26f050270e19b2809ce38cd9a221fb375291 Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Sat, 13 Jun 2026 14:19:10 -0700 Subject: [PATCH] fix(wallet): avoid lazy RegTestParams construction in ZeroConfCoinSelector isTransactionSelectable() compared a pending tx's params against RegTestParams.get() for every pending output it evaluated. RegTestParams.get() lazily runs the RegTestParams constructor, which recomputes the regtest genesis X11 hash and checkState()s it against a hardcoded value. When that lazy construction is first triggered on a contended background thread (e.g. CoinJoin's IO coroutines doing heavy concurrent X11 hashing) a transient bad X11 result makes the checkState throw, crashing coin selection / balance calculation - even on mainnet/testnet wallets that never use regtest. Compare against NetworkParameters.ID_REGTEST instead (a plain field lookup, no construction or genesis recompute). Behavior is unchanged: RegTestParams sets id = ID_REGTEST, so the regtest special-case still applies. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../java/org/bitcoinj/wallet/ZeroConfCoinSelector.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/wallet/ZeroConfCoinSelector.java b/core/src/main/java/org/bitcoinj/wallet/ZeroConfCoinSelector.java index 391e63ff58..e194c5e17b 100644 --- a/core/src/main/java/org/bitcoinj/wallet/ZeroConfCoinSelector.java +++ b/core/src/main/java/org/bitcoinj/wallet/ZeroConfCoinSelector.java @@ -14,9 +14,9 @@ package org.bitcoinj.wallet; +import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionConfidence; -import org.bitcoinj.params.RegTestParams; public class ZeroConfCoinSelector extends DefaultCoinSelector { @@ -45,6 +45,9 @@ boolean isTransactionSelectable(Transaction tx) { return type.equals(TransactionConfidence.ConfidenceType.BUILDING) || type.equals(TransactionConfidence.ConfidenceType.PENDING) && // In regtest mode we expect to have only one peer, so we won't see transactions propagate. // TODO: The value 1 below dates from a time when transactions we broadcast *to* were counted, set to 0 - (confidence.numBroadcastPeers() > 1 || tx.getParams() == RegTestParams.get()); + // Compare against the network id rather than RegTestParams.get(): the latter lazily constructs + // RegTestParams (recomputing and asserting the regtest genesis X11 hash), which can throw when first + // triggered on a contended background thread (e.g. CoinJoin) even on mainnet/testnet wallets. + (confidence.numBroadcastPeers() > 1 || NetworkParameters.ID_REGTEST.equals(tx.getParams().getId())); } }