treat a bitcoin core chain that falls below the last polled tip as a reorg rather than failing every poll until reconnect
What changed, and why it matters
This change fixes how Sparrow Wallet detects blockchain reorganizations when connected to Bitcoin Core. Previously, if Bitcoin Core's active chain briefly dropped below the last known block height (for example during a reorg), Sparrow would throw an error on every poll and keep failing until the user reconnected. Now it correctly recognizes this situation as a reorg and resets its last known block, allowing normal polling to continue.
Apply the patch. It is a small, defensive fix that improves robustness against blockchain reorganizations. No immediate incident response is indicated unless users report being stuck in a reconnect loop during reorgs.
Security signals we found
Denial-of-service-like availability impact: repeated RPC exceptions could stall wallet synchronization until manual reconnect
Incorrect error handling for a known Bitcoin Core RPC error code (-8 RPC_INVALID_PARAMETER)
Reorg handling logic that failed to account for chain tip regression
Evidence from the diff
BitcoindClient’s polling loop previously called getBlockHash(tip.height()) and compared the returned hash to lastBlock. If Bitcoin Core’s chain tip fell below that height (e.g., during a reorg), getBlockHash would throw an RPC error with code -8 (RPC_INVALID_PARAMETER) because the requested height was above the current tip. The old code did not handle this exception, causing repeated JsonRpcException failures on each poll. The patch catches JsonRpcException, checks for RPC_INVALID_PARAMETER, logs a reorg warning, sets lastBlock = null, and rethrows any other RPC error. This restores correct reorg handling without requiring a manual reconnect.
Changed components
src/main/java/com/sparrowwallet/sparrow/net/cormorant/bitcoind/BitcoindClient.javaBitcoin Core RPC polling / chain tip synchronizationInspect captured patch +15 / −4
### src/main/java/com/sparrowwallet/sparrow/net/cormorant/bitcoind/BitcoindClient.java
@@ -52,6 +52,7 @@ public class BitcoindClient {
private static final long PRUNED_RESCAN_TIMEGAP_MILLIS = 7200*1000;
//Error codes from https://github.com/bitcoin/bitcoin/blob/master/src/rpc/protocol.h
+ public static final int RPC_INVALID_PARAMETER = -8;
public static final int RPC_WALLET_NOT_FOUND = -18;
public static final String WALLET_ALREADY_LOADING_MESSAGE = "Wallet already loading.";
@@ -721,10 +722,20 @@ public void run() {
}
if(lastBlock != null && tip != null) {
- String blockhash = getBitcoindService().getBlockHash(tip.height());
- if(!lastBlock.equals(blockhash)) {
- log.warn("Reorg detected, block height " + tip.height() + " was " + lastBlock + " and now is " + blockhash);
- lastBlock = null;
+ try {
+ String blockhash = getBitcoindService().getBlockHash(tip.height());
+ if(!lastBlock.equals(blockhash)) {
+ log.warn("Reorg detected, block height " + tip.height() + " was " + lastBlock + " and now is " + blockhash);
+ lastBlock = null;
+ }
+ } catch(JsonRpcException e) {
+ //The active chain no longer reaches the last seen tip height, so the block has been disconnected
+ if(e.getErrorMessage() != null && e.getErrorMessage().getCode() == RPC_INVALID_PARAMETER) {
+ log.warn("Reorg detected, block height " + tip.height() + " was " + lastBlock + " and is now above the chain tip");
+ lastBlock = null;
+ } else {
+ throw e;
+ }
}
}
Why this scored 31/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.