mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-04 21:36:45 +00:00
cache fetched script hash status and dont retrieve history again until it changes
This commit is contained in:
parent
55181165c8
commit
d6c7a0b757
4 changed files with 57 additions and 9 deletions
|
@ -1010,6 +1010,7 @@ public class AppController implements Initializable {
|
|||
Wallet pastWallet = wallet.copy();
|
||||
walletTabData.getStorage().backupTempWallet();
|
||||
wallet.clearHistory();
|
||||
AppServices.clearTransactionHistoryCache(wallet);
|
||||
EventManager.get().post(new WalletAddressesChangedEvent(wallet, pastWallet, walletTabData.getStorage().getWalletFile()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -496,6 +496,10 @@ public class AppServices {
|
|||
payjoinURIs.put(bitcoinURI.getAddress(), bitcoinURI);
|
||||
}
|
||||
|
||||
public static void clearTransactionHistoryCache(Wallet wallet) {
|
||||
ElectrumServer.clearRetrievedScriptHashes(wallet);
|
||||
}
|
||||
|
||||
public static Optional<ButtonType> showWarningDialog(String title, String content, ButtonType... buttons) {
|
||||
return showAlertDialog(title, content, Alert.AlertType.WARNING, buttons);
|
||||
}
|
||||
|
|
|
@ -44,6 +44,10 @@ public class ElectrumServer {
|
|||
|
||||
private static final Map<String, Set<String>> subscribedScriptHashes = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
private static String previousServerAddress;
|
||||
|
||||
private static Map<String, String> retrievedScriptHashes = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
private static ElectrumServerRpc electrumServerRpc = new SimpleElectrumServerRpc();
|
||||
|
||||
private static String bwtElectrumServer;
|
||||
|
@ -84,6 +88,12 @@ public class ElectrumServer {
|
|||
throw new ServerConfigException("Electrum server URL must start with " + Protocol.TCP.toUrlString() + " or " + Protocol.SSL.toUrlString());
|
||||
}
|
||||
|
||||
//If changing server, don't rely on previous transaction history
|
||||
if(!electrumServer.equals(previousServerAddress)) {
|
||||
retrievedScriptHashes.clear();
|
||||
}
|
||||
previousServerAddress = electrumServer;
|
||||
|
||||
HostAndPort server = protocol.getServerHostAndPort(electrumServer);
|
||||
|
||||
if(Config.get().isUseProxy() && proxyServer != null && !proxyServer.isBlank()) {
|
||||
|
@ -150,6 +160,11 @@ public class ElectrumServer {
|
|||
}
|
||||
}
|
||||
|
||||
public static void clearRetrievedScriptHashes(Wallet wallet) {
|
||||
wallet.getNode(KeyPurpose.RECEIVE).getChildren().stream().map(node -> getScriptHash(wallet, node)).forEach(scriptHash -> retrievedScriptHashes.remove(scriptHash));
|
||||
wallet.getNode(KeyPurpose.CHANGE).getChildren().stream().map(node -> getScriptHash(wallet, node)).forEach(scriptHash -> retrievedScriptHashes.remove(scriptHash));
|
||||
}
|
||||
|
||||
public Map<WalletNode, Set<BlockTransactionHash>> getHistory(Wallet wallet) throws ServerException {
|
||||
Map<WalletNode, Set<BlockTransactionHash>> receiveTransactionMap = new TreeMap<>();
|
||||
getHistory(wallet, KeyPurpose.RECEIVE, receiveTransactionMap);
|
||||
|
@ -177,6 +192,15 @@ public class ElectrumServer {
|
|||
Set<BlockTransactionHash> newReferences = nodeTransactionMap.values().stream().flatMap(Collection::stream).filter(ref -> !wallet.getTransactions().containsKey(ref.getHash())).collect(Collectors.toSet());
|
||||
getReferencedTransactions(wallet, nodeTransactionMap);
|
||||
|
||||
//Subscribe and retrieve transaction history from child nodes if necessary to maintain gap limit
|
||||
Set<KeyPurpose> keyPurposes = nodes.stream().map(WalletNode::getKeyPurpose).collect(Collectors.toUnmodifiableSet());
|
||||
for(KeyPurpose keyPurpose : keyPurposes) {
|
||||
WalletNode purposeNode = wallet.getNode(keyPurpose);
|
||||
getHistoryToGapLimit(wallet, nodeTransactionMap, purposeNode);
|
||||
}
|
||||
|
||||
log.debug("Fetched nodes history for: " + nodeTransactionMap.keySet());
|
||||
|
||||
if(!newReferences.isEmpty()) {
|
||||
//Look for additional nodes to fetch history for by considering the inputs and outputs of new transactions found
|
||||
log.debug(wallet.getName() + " found new transactions: " + newReferences);
|
||||
|
@ -216,13 +240,22 @@ public class ElectrumServer {
|
|||
|
||||
public void getHistory(Wallet wallet, KeyPurpose keyPurpose, Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap) throws ServerException {
|
||||
WalletNode purposeNode = wallet.getNode(keyPurpose);
|
||||
//Subscribe to all existing address WalletNodes and add them to nodeTransactionMap as keys to empty sets if they have history
|
||||
//Subscribe to all existing address WalletNodes and add them to nodeTransactionMap as keys to empty sets if they have history that needs to be fetched
|
||||
subscribeWalletNodes(wallet, purposeNode.getChildren(), nodeTransactionMap, 0);
|
||||
//All WalletNode keys in nodeTransactionMap need to have their history fetched (nodes without history will not be keys in the map yet)
|
||||
getReferences(wallet, nodeTransactionMap.keySet(), nodeTransactionMap, 0);
|
||||
//Fetch all referenced transaction to wallet transactions map. We do this now even though it is done again later to get it done before too many script hashes are subscribed
|
||||
getReferencedTransactions(wallet, nodeTransactionMap);
|
||||
//Increase child nodes if necessary to maintain gap limit, and ensure they are subscribed and history is fetched
|
||||
getHistoryToGapLimit(wallet, nodeTransactionMap, purposeNode);
|
||||
|
||||
log.debug("Fetched history for: " + nodeTransactionMap.keySet());
|
||||
|
||||
//Set the remaining WalletNode keys in nodeTransactionMap to empty sets to indicate no history (if no script hash history has already been retrieved in a previous call)
|
||||
purposeNode.getChildren().stream().filter(node -> !nodeTransactionMap.containsKey(node) && retrievedScriptHashes.get(getScriptHash(wallet, node)) == null).forEach(node -> nodeTransactionMap.put(node, Collections.emptySet()));
|
||||
}
|
||||
|
||||
private void getHistoryToGapLimit(Wallet wallet, Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap, WalletNode purposeNode) throws ServerException {
|
||||
//Because node children are added sequentially in WalletNode.fillToIndex, we can simply look at the number of children to determine the highest filled index
|
||||
int historySize = purposeNode.getChildren().size();
|
||||
//The gap limit size takes the highest used index in the retrieved history and adds the gap limit (plus one to be comparable to the number of children since index is zero based)
|
||||
|
@ -235,9 +268,6 @@ public class ElectrumServer {
|
|||
historySize = purposeNode.getChildren().size();
|
||||
gapLimitSize = getGapLimitSize(wallet, nodeTransactionMap);
|
||||
}
|
||||
|
||||
//Set the remaining WalletNode keys in nodeTransactionMap to empty sets to indicate no history
|
||||
purposeNode.getChildren().stream().filter(node -> !nodeTransactionMap.containsKey(node)).forEach(node -> nodeTransactionMap.put(node, Collections.emptySet()));
|
||||
}
|
||||
|
||||
private int getGapLimitSize(Wallet wallet, Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap) {
|
||||
|
@ -319,9 +349,12 @@ public class ElectrumServer {
|
|||
|
||||
if(node != null && node.getIndex() >= startIndex) {
|
||||
String scriptHash = getScriptHash(wallet, node);
|
||||
if(getSubscribedScriptHashStatus(scriptHash) != null) {
|
||||
//Already subscribed, but still need to fetch history from a used node
|
||||
nodeTransactionMap.put(node, new TreeSet<>());
|
||||
String subscribedStatus = getSubscribedScriptHashStatus(scriptHash);
|
||||
if(subscribedStatus != null) {
|
||||
//Already subscribed, but still need to fetch history from a used node if not previously fetched
|
||||
if(!subscribedStatus.equals(retrievedScriptHashes.get(scriptHash))) {
|
||||
nodeTransactionMap.put(node, new TreeSet<>());
|
||||
}
|
||||
} else if(!subscribedScriptHashes.containsKey(scriptHash) && scriptHashes.add(scriptHash)) {
|
||||
//Unique script hash we are not yet subscribed to
|
||||
pathScriptHashes.put(node.getDerivationPath(), scriptHash);
|
||||
|
@ -329,6 +362,8 @@ public class ElectrumServer {
|
|||
}
|
||||
}
|
||||
|
||||
log.debug("Subscribe to: " + pathScriptHashes.keySet());
|
||||
|
||||
if(pathScriptHashes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -343,8 +378,8 @@ public class ElectrumServer {
|
|||
WalletNode node = optionalNode.get();
|
||||
String scriptHash = getScriptHash(wallet, node);
|
||||
|
||||
//Check if there is history for this script hash
|
||||
if(status != null) {
|
||||
//Check if there is history for this script hash, and if the history has changed since last fetched
|
||||
if(status != null && !status.equals(retrievedScriptHashes.get(scriptHash))) {
|
||||
//Set the value for this node to be an empty set to mark it as requiring a get_history RPC call for this wallet
|
||||
nodeTransactionMap.put(node, new TreeSet<>());
|
||||
}
|
||||
|
@ -1037,6 +1072,13 @@ public class ElectrumServer {
|
|||
Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap = (nodes == null ? electrumServer.getHistory(wallet) : electrumServer.getHistory(wallet, nodes));
|
||||
electrumServer.getReferencedTransactions(wallet, nodeTransactionMap);
|
||||
electrumServer.calculateNodeHistory(wallet, nodeTransactionMap);
|
||||
|
||||
//Add all of the script hashes we have now fetched the history for so we don't need to fetch again until the script hash status changes
|
||||
for(WalletNode node : nodeTransactionMap.keySet()) {
|
||||
String scriptHash = getScriptHash(wallet, node);
|
||||
retrievedScriptHashes.put(scriptHash, getSubscribedScriptHashStatus(scriptHash));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -318,6 +318,7 @@ public class WalletForm {
|
|||
public void walletTabsClosed(WalletTabsClosedEvent event) {
|
||||
for(WalletTabData tabData : event.getClosedWalletTabData()) {
|
||||
if(tabData.getWalletForm() == this) {
|
||||
AppServices.clearTransactionHistoryCache(wallet);
|
||||
EventManager.get().unregister(this);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue