mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +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) {
|
public static void addCalculatedScriptHashes(Wallet wallet) {
|
||||||
calculateScriptHashes(wallet, KeyPurpose.RECEIVE).forEach(retrievedScriptHashes::putIfAbsent);
|
getCalculatedScriptHashes(wallet).forEach(retrievedScriptHashes::putIfAbsent);
|
||||||
calculateScriptHashes(wallet, KeyPurpose.CHANGE).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) {
|
private static Map<String, String> calculateScriptHashes(Wallet wallet, KeyPurpose keyPurpose) {
|
||||||
|
@ -1188,14 +1194,30 @@ public class ElectrumServer {
|
||||||
synchronized(walletSynchronizeLocks.get(wallet)) {
|
synchronized(walletSynchronizeLocks.get(wallet)) {
|
||||||
if(isConnected()) {
|
if(isConnected()) {
|
||||||
ElectrumServer electrumServer = new ElectrumServer();
|
ElectrumServer electrumServer = new ElectrumServer();
|
||||||
|
Map<String, String> previousScriptHashes = getCalculatedScriptHashes(wallet);
|
||||||
Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap = (nodes == null ? electrumServer.getHistory(wallet) : electrumServer.getHistory(wallet, nodes));
|
Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap = (nodes == null ? electrumServer.getHistory(wallet) : electrumServer.getHistory(wallet, nodes));
|
||||||
electrumServer.getReferencedTransactions(wallet, nodeTransactionMap);
|
electrumServer.getReferencedTransactions(wallet, nodeTransactionMap);
|
||||||
electrumServer.calculateNodeHistory(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
|
//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);
|
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
|
//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.event.*;
|
||||||
import com.sparrowwallet.sparrow.io.Config;
|
import com.sparrowwallet.sparrow.io.Config;
|
||||||
import com.sparrowwallet.sparrow.io.StorageException;
|
import com.sparrowwallet.sparrow.io.StorageException;
|
||||||
|
import com.sparrowwallet.sparrow.net.AllHistoryChangedException;
|
||||||
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
import com.sparrowwallet.sparrow.io.Storage;
|
import com.sparrowwallet.sparrow.io.Storage;
|
||||||
import com.sparrowwallet.sparrow.net.ServerType;
|
import com.sparrowwallet.sparrow.net.ServerType;
|
||||||
|
@ -140,13 +141,25 @@ public class WalletForm {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
historyService.setOnFailed(workerStateEvent -> {
|
historyService.setOnFailed(workerStateEvent -> {
|
||||||
if(AppServices.isConnected()) {
|
if(workerStateEvent.getSource().getException() instanceof AllHistoryChangedException) {
|
||||||
log.error("Error retrieving wallet history", workerStateEvent.getSource().getException());
|
try {
|
||||||
} else {
|
storage.backupWallet();
|
||||||
log.debug("Disconnected while retrieving wallet history", workerStateEvent.getSource().getException());
|
} 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));
|
EventManager.get().post(new WalletHistoryStartedEvent(wallet, nodes));
|
||||||
|
|
Loading…
Reference in a new issue