mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-23 20:36:44 +00:00
trigger full wallet refresh when all transaction history has changed on loading
This commit is contained in:
parent
0302913c3f
commit
3013688447
4 changed files with 64 additions and 11 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
|||
Subproject commit 4a4a62f239f5de1e25e927ee9996326383ea7f89
|
||||
Subproject commit da14a9bf34945713494cfbef86f8f44aedbc727f
|
|
@ -0,0 +1,18 @@
|
|||
package com.sparrowwallet.sparrow.net;
|
||||
|
||||
public class AllHistoryChangedException extends RuntimeException {
|
||||
public AllHistoryChangedException() {
|
||||
}
|
||||
|
||||
public AllHistoryChangedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AllHistoryChangedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public AllHistoryChangedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -167,8 +167,14 @@ public class ElectrumServer {
|
|||
}
|
||||
|
||||
public static void addCalculatedScriptHashes(Wallet wallet) {
|
||||
calculateScriptHashes(wallet, KeyPurpose.RECEIVE).forEach(retrievedScriptHashes::putIfAbsent);
|
||||
calculateScriptHashes(wallet, KeyPurpose.CHANGE).forEach(retrievedScriptHashes::putIfAbsent);
|
||||
getCalculatedScriptHashes(wallet).forEach(retrievedScriptHashes::putIfAbsent);
|
||||
}
|
||||
|
||||
private static Map<String, String> getCalculatedScriptHashes(Wallet wallet) {
|
||||
Map<String, String> storedScriptHashStatuses = new HashMap<>();
|
||||
storedScriptHashStatuses.putAll(calculateScriptHashes(wallet, KeyPurpose.RECEIVE));
|
||||
storedScriptHashStatuses.putAll(calculateScriptHashes(wallet, KeyPurpose.CHANGE));
|
||||
return storedScriptHashStatuses;
|
||||
}
|
||||
|
||||
private static Map<String, String> calculateScriptHashes(Wallet wallet, KeyPurpose keyPurpose) {
|
||||
|
@ -1188,14 +1194,30 @@ public class ElectrumServer {
|
|||
synchronized(walletSynchronizeLocks.get(wallet)) {
|
||||
if(isConnected()) {
|
||||
ElectrumServer electrumServer = new ElectrumServer();
|
||||
Map<String, String> previousScriptHashes = getCalculatedScriptHashes(wallet);
|
||||
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 : (nodes == null ? nodeTransactionMap.keySet() : nodes)) {
|
||||
Set<WalletNode> updatedNodes = new HashSet<>();
|
||||
Map<WalletNode, Set<BlockTransactionHashIndex>> walletNodes = wallet.getWalletNodes();
|
||||
for(WalletNode node : (nodes == null ? walletNodes.keySet() : nodes)) {
|
||||
String scriptHash = getScriptHash(wallet, node);
|
||||
retrievedScriptHashes.put(scriptHash, getSubscribedScriptHashStatus(scriptHash));
|
||||
String subscribedStatus = getSubscribedScriptHashStatus(scriptHash);
|
||||
if(!Objects.equals(subscribedStatus, retrievedScriptHashes.get(scriptHash))) {
|
||||
updatedNodes.add(node);
|
||||
}
|
||||
retrievedScriptHashes.put(scriptHash, subscribedStatus);
|
||||
}
|
||||
|
||||
//If wallet was not empty, check if all used updated nodes have changed history
|
||||
if(nodes == null && previousScriptHashes.values().stream().anyMatch(Objects::nonNull)) {
|
||||
if(!updatedNodes.isEmpty() && updatedNodes.equals(walletNodes.entrySet().stream().filter(entry -> !entry.getValue().isEmpty()).map(Map.Entry::getKey).collect(Collectors.toSet()))) {
|
||||
//All used nodes on a non-empty wallet have changed history. Abort and trigger a full refresh.
|
||||
log.info("All used nodes on a non-empty wallet have changed history. Triggering a full wallet refresh.");
|
||||
throw new AllHistoryChangedException();
|
||||
}
|
||||
}
|
||||
|
||||
//Clear transaction outputs for nodes that have no history - this is useful when a transaction is replaced in the mempool
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.sparrowwallet.sparrow.WalletTabData;
|
|||
import com.sparrowwallet.sparrow.event.*;
|
||||
import com.sparrowwallet.sparrow.io.Config;
|
||||
import com.sparrowwallet.sparrow.io.StorageException;
|
||||
import com.sparrowwallet.sparrow.net.AllHistoryChangedException;
|
||||
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||
import com.sparrowwallet.sparrow.io.Storage;
|
||||
import com.sparrowwallet.sparrow.net.ServerType;
|
||||
|
@ -140,13 +141,25 @@ public class WalletForm {
|
|||
}
|
||||
});
|
||||
historyService.setOnFailed(workerStateEvent -> {
|
||||
if(AppServices.isConnected()) {
|
||||
log.error("Error retrieving wallet history", workerStateEvent.getSource().getException());
|
||||
} else {
|
||||
log.debug("Disconnected while retrieving wallet history", workerStateEvent.getSource().getException());
|
||||
}
|
||||
if(workerStateEvent.getSource().getException() instanceof AllHistoryChangedException) {
|
||||
try {
|
||||
storage.backupWallet();
|
||||
} catch(IOException e) {
|
||||
log.error("Error backing up wallet", e);
|
||||
}
|
||||
|
||||
EventManager.get().post(new WalletHistoryFailedEvent(wallet, workerStateEvent.getSource().getException()));
|
||||
wallet.clearHistory();
|
||||
AppServices.clearTransactionHistoryCache(wallet);
|
||||
EventManager.get().post(new WalletHistoryClearedEvent(wallet, pastWallet, getWalletId()));
|
||||
} else {
|
||||
if(AppServices.isConnected()) {
|
||||
log.error("Error retrieving wallet history", workerStateEvent.getSource().getException());
|
||||
} else {
|
||||
log.debug("Disconnected while retrieving wallet history", workerStateEvent.getSource().getException());
|
||||
}
|
||||
|
||||
EventManager.get().post(new WalletHistoryFailedEvent(wallet, workerStateEvent.getSource().getException()));
|
||||
}
|
||||
});
|
||||
|
||||
EventManager.get().post(new WalletHistoryStartedEvent(wallet, nodes));
|
||||
|
|
Loading…
Reference in a new issue