diff --git a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java index 11e86b98..9f3a9527 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java @@ -17,8 +17,6 @@ import com.sparrowwallet.sparrow.wallet.SendController; import javafx.application.Platform; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; import javafx.concurrent.ScheduledService; import javafx.concurrent.Service; import javafx.concurrent.Task; @@ -26,6 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; @@ -89,7 +88,7 @@ public class ElectrumServer { } //If changing server, don't rely on previous transaction history - if(!electrumServer.equals(previousServerAddress)) { + if(previousServerAddress != null && !electrumServer.equals(previousServerAddress)) { retrievedScriptHashes.clear(); } previousServerAddress = electrumServer; @@ -160,6 +159,51 @@ public class ElectrumServer { } } + public static void addCalculatedScriptHashes(Wallet wallet) { + calculateScriptHashes(wallet, KeyPurpose.RECEIVE).forEach(retrievedScriptHashes::putIfAbsent); + calculateScriptHashes(wallet, KeyPurpose.CHANGE).forEach(retrievedScriptHashes::putIfAbsent); + } + + private static Map calculateScriptHashes(Wallet wallet, KeyPurpose keyPurpose) { + Map calculatedScriptHashes = new LinkedHashMap<>(); + for(WalletNode walletNode : wallet.getNode(keyPurpose).getChildren()) { + String scriptHash = getScriptHash(wallet, walletNode); + + List txos = new ArrayList<>(walletNode.getTransactionOutputs()); + txos.addAll(walletNode.getTransactionOutputs().stream().filter(BlockTransactionHashIndex::isSpent).map(BlockTransactionHashIndex::getSpentBy).collect(Collectors.toList())); + Set unique = new HashSet<>(txos.size()); + txos.removeIf(ref -> !unique.add(ref.getHash())); + txos.sort((txo1, txo2) -> { + if(txo1.getHeight() != txo2.getHeight()) { + return txo1.getComparisonHeight() - txo2.getComparisonHeight(); + } + + if(txo1.isSpent() && txo1.getSpentBy().equals(txo2)) { + return -1; + } + + if(txo2.isSpent() && txo2.getSpentBy().equals(txo1)) { + return 1; + } + + //We cannot further sort by order within a block, so sometimes multiple txos to an address will mean an incorrect status + return 0; + }); + if(!txos.isEmpty()) { + StringBuilder scriptHashStatus = new StringBuilder(); + for(BlockTransactionHashIndex txo : txos) { + scriptHashStatus.append(txo.getHash().toString()).append(":").append(txo.getHeight()).append(":"); + } + + calculatedScriptHashes.put(scriptHash, Utils.bytesToHex(Sha256Hash.hash(scriptHashStatus.toString().getBytes(StandardCharsets.UTF_8)))); + } else { + calculatedScriptHashes.put(scriptHash, null); + } + } + + return calculatedScriptHashes; + } + 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)); diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java index f6acb7ec..39e303fc 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java @@ -46,7 +46,8 @@ public class WalletForm { //Unencrypted wallets load before isConnected is true, waiting for the ConnectionEvent to refresh history - save the backup for this event savedPastWallet = backupWallet; - if(refreshHistory) { + if(refreshHistory && wallet.isValid()) { + ElectrumServer.addCalculatedScriptHashes(wallet); refreshHistory(AppServices.getCurrentBlockHeight(), backupWallet); } }