cormorant: avoid calling listwalletdir rpc on initialization due to extremely slow response on windows

This commit is contained in:
Craig Raw 2025-04-18 09:43:57 +02:00
parent 71ac72e9f6
commit 94d15c09e6
3 changed files with 47 additions and 13 deletions

View file

@ -0,0 +1,7 @@
package com.sparrowwallet.sparrow.net.cormorant.bitcoind;
public class BitcoinRPCException extends RuntimeException {
public BitcoinRPCException(String message) {
super(message);
}
}

View file

@ -49,6 +49,12 @@ public class BitcoindClient {
private static final long PRUNED_RESCAN_TIMEGAP_MILLIS = 7200*1000; 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_METHOD_NOT_FOUND = -32601;
public static final int RPC_WALLET_NOT_FOUND = -18;
public static final String WALLET_ALREADY_LOADING_MESSAGE = "Wallet already loading.";
private final JsonRpcClient jsonRpcClient; private final JsonRpcClient jsonRpcClient;
private final Timer timer = new Timer(true); private final Timer timer = new Timer(true);
private final Store store = new Store(); private final Store store = new Store();
@ -140,21 +146,27 @@ public class BitcoindClient {
tip = blockHeader.getBlockHeader(); tip = blockHeader.getBlockHeader();
} }
ListWalletDirResult listWalletDirResult = getBitcoindService().listWalletDir(); List<String> loadedWallets;
if(listWalletDirResult == null) { try {
throw new RuntimeException("Wallet support must be enabled in Bitcoin Core"); loadedWallets = getBitcoindService().listWallets();
} legacyWalletExists = loadedWallets.contains(Bwt.DEFAULT_CORE_WALLET);
boolean exists = listWalletDirResult.wallets().stream().anyMatch(walletDirResult -> walletDirResult.name().equals(CORE_WALLET_NAME)); } catch(JsonRpcException e) {
legacyWalletExists = listWalletDirResult.wallets().stream().anyMatch(walletDirResult -> walletDirResult.name().equals(Bwt.DEFAULT_CORE_WALLET)); if(e.getErrorMessage().getCode() == RPC_METHOD_NOT_FOUND) {
throw new BitcoinRPCException("Wallet support must be enabled in Bitcoin Core");
List<String> loadedWallets = getBitcoindService().listWallets();
boolean loaded = loadedWallets.contains(CORE_WALLET_NAME);
if(!exists && !loaded) {
getBitcoindService().createWallet(CORE_WALLET_NAME, true, true, "", true, true, true, false);
} else { } else {
if(!loaded) { throw e;
}
}
if(!loadedWallets.contains(CORE_WALLET_NAME)) {
try {
getBitcoindService().loadWallet(CORE_WALLET_NAME, true); getBitcoindService().loadWallet(CORE_WALLET_NAME, true);
} catch(JsonRpcException e) {
if(e.getErrorMessage().getCode() == RPC_WALLET_NOT_FOUND) {
getBitcoindService().createWallet(CORE_WALLET_NAME, true, true, "", true, true, true, false);
} else if(!WALLET_ALREADY_LOADING_MESSAGE.equals(e.getErrorMessage().getMessage())) {
throw e;
}
} }
} }
@ -186,12 +198,24 @@ public class BitcoindClient {
.sorted(Comparator.comparingLong(o -> o.getBirthDate().getTime())).collect(Collectors.toList()); .sorted(Comparator.comparingLong(o -> o.getBirthDate().getTime())).collect(Collectors.toList());
if(!prePruneWallets.isEmpty()) { if(!prePruneWallets.isEmpty()) {
pruneWarnedDescriptors.add(e.getDescriptor()); pruneWarnedDescriptors.add(e.getDescriptor());
Platform.runLater(() -> EventManager.get().post(new CormorantPruneStatusEvent("Error: Wallet birthday earlier than Bitcoin Core prune date", prePruneWallets.get(0), e.getRescanSince(), e.getPrunedDate(), legacyWalletExists))); if(!legacyWalletExists) {
legacyWalletExists = checkLegacyWalletExists();
}
Platform.runLater(() -> EventManager.get().post(new CormorantPruneStatusEvent("Error: Wallet birthday earlier than Bitcoin Core prune date", prePruneWallets.getFirst(), e.getRescanSince(), e.getPrunedDate(), legacyWalletExists)));
} }
throw new ImportFailedException("Wallet birthday earlier than prune date"); throw new ImportFailedException("Wallet birthday earlier than prune date");
} }
} }
private boolean checkLegacyWalletExists() {
try {
getBitcoindService().loadWallet(Bwt.DEFAULT_CORE_WALLET);
return true;
} catch(Exception e) {
return false;
}
}
public void importWallet(Wallet wallet) throws ImportFailedException { public void importWallet(Wallet wallet) throws ImportFailedException {
//To avoid unnecessary rescans, get all related wallets //To avoid unnecessary rescans, get all related wallets
importWallets(wallet.isMasterWallet() ? wallet.getAllWallets() : wallet.getMasterWallet().getAllWallets()); importWallets(wallet.isMasterWallet() ? wallet.getAllWallets() : wallet.getMasterWallet().getAllWallets());

View file

@ -73,6 +73,9 @@ public interface BitcoindClientService {
@JsonRpcParam("passphrase") String passphrase, @JsonRpcParam("avoid_reuse") boolean avoidReuse, @JsonRpcParam("descriptors") boolean descriptors, @JsonRpcParam("passphrase") String passphrase, @JsonRpcParam("avoid_reuse") boolean avoidReuse, @JsonRpcParam("descriptors") boolean descriptors,
@JsonRpcParam("load_on_startup") boolean loadOnStartup, @JsonRpcParam("external_signer") boolean externalSigner); @JsonRpcParam("load_on_startup") boolean loadOnStartup, @JsonRpcParam("external_signer") boolean externalSigner);
@JsonRpcMethod("loadwallet")
CreateLoadWalletResult loadWallet(@JsonRpcParam("filename") String name);
@JsonRpcMethod("loadwallet") @JsonRpcMethod("loadwallet")
CreateLoadWalletResult loadWallet(@JsonRpcParam("filename") String name, @JsonRpcParam("load_on_startup") boolean loadOnStartup); CreateLoadWalletResult loadWallet(@JsonRpcParam("filename") String name, @JsonRpcParam("load_on_startup") boolean loadOnStartup);