reduce server calls on opening a transaction tab by using open wallet history when performing spent output lookup

This commit is contained in:
Craig Raw 2024-01-12 15:35:26 +02:00
parent 57dba5d6ae
commit 02a0a3277b
2 changed files with 66 additions and 20 deletions

View file

@ -504,19 +504,19 @@ public class ElectrumServer {
} }
} }
public List<Set<BlockTransactionHash>> getOutputTransactionReferences(Transaction transaction, int indexStart, int indexEnd) throws ServerException { public List<Set<BlockTransactionHash>> getOutputTransactionReferences(Transaction transaction, int indexStart, int indexEnd, List<Set<BlockTransactionHash>> blockTransactionHashes) throws ServerException {
try { try {
Map<String, String> pathScriptHashes = new LinkedHashMap<>(); Map<String, String> pathScriptHashes = new LinkedHashMap<>();
for(int i = indexStart; i < transaction.getOutputs().size() && i < indexEnd; i++) { for(int i = indexStart; i < transaction.getOutputs().size() && i < indexEnd; i++) {
TransactionOutput output = transaction.getOutputs().get(i); if(blockTransactionHashes.get(i) == null) {
pathScriptHashes.put(Integer.toString(i), getScriptHash(output)); TransactionOutput output = transaction.getOutputs().get(i);
pathScriptHashes.put(Integer.toString(i), getScriptHash(output));
}
} }
Map<String, ScriptHashTx[]> result = electrumServerRpc.getScriptHashHistory(getTransport(), null, pathScriptHashes, false); Map<String, ScriptHashTx[]> result = new HashMap<>();
if(!pathScriptHashes.isEmpty()) {
List<Set<BlockTransactionHash>> blockTransactionHashes = new ArrayList<>(transaction.getOutputs().size()); result = electrumServerRpc.getScriptHashHistory(getTransport(), null, pathScriptHashes, false);
for(int i = 0; i < transaction.getOutputs().size(); i++) {
blockTransactionHashes.add(null);
} }
for(String index : result.keySet()) { for(String index : result.keySet()) {
@ -1571,17 +1571,26 @@ public class ElectrumServer {
private final Transaction transaction; private final Transaction transaction;
private final int indexStart; private final int indexStart;
private final int indexEnd; private final int indexEnd;
private final List<Set<BlockTransactionHash>> blockTransactionHashes;
public TransactionOutputsReferenceService(Transaction transaction) { private final Map<Sha256Hash, BlockTransaction> transactionMap;
this.transaction = transaction;
this.indexStart = 0;
this.indexEnd = transaction.getOutputs().size();
}
public TransactionOutputsReferenceService(Transaction transaction, int indexStart, int indexEnd) { public TransactionOutputsReferenceService(Transaction transaction, int indexStart, int indexEnd) {
this.transaction = transaction; this.transaction = transaction;
this.indexStart = Math.min(transaction.getOutputs().size(), indexStart); this.indexStart = Math.min(transaction.getOutputs().size(), indexStart);
this.indexEnd = Math.min(transaction.getOutputs().size(), indexEnd); this.indexEnd = Math.min(transaction.getOutputs().size(), indexEnd);
this.blockTransactionHashes = new ArrayList<>(transaction.getOutputs().size());
for(int i = 0; i < transaction.getOutputs().size(); i++) {
blockTransactionHashes.add(null);
}
this.transactionMap = new HashMap<>();
}
public TransactionOutputsReferenceService(Transaction transaction, int indexStart, int indexEnd, List<Set<BlockTransactionHash>> blockTransactionHashes, Map<Sha256Hash, BlockTransaction> transactionMap) {
this.transaction = transaction;
this.indexStart = Math.min(transaction.getOutputs().size(), indexStart);
this.indexEnd = Math.min(transaction.getOutputs().size(), indexEnd);
this.blockTransactionHashes = blockTransactionHashes;
this.transactionMap = transactionMap;
} }
@Override @Override
@ -1589,7 +1598,7 @@ public class ElectrumServer {
return new Task<>() { return new Task<>() {
protected List<BlockTransaction> call() throws ServerException { protected List<BlockTransaction> call() throws ServerException {
ElectrumServer electrumServer = new ElectrumServer(); ElectrumServer electrumServer = new ElectrumServer();
List<Set<BlockTransactionHash>> outputTransactionReferences = electrumServer.getOutputTransactionReferences(transaction, indexStart, indexEnd); List<Set<BlockTransactionHash>> outputTransactionReferences = electrumServer.getOutputTransactionReferences(transaction, indexStart, indexEnd, blockTransactionHashes);
Set<BlockTransactionHash> setReferences = new HashSet<>(); Set<BlockTransactionHash> setReferences = new HashSet<>();
for(Set<BlockTransactionHash> outputReferences : outputTransactionReferences) { for(Set<BlockTransactionHash> outputReferences : outputTransactionReferences) {
@ -1599,16 +1608,16 @@ public class ElectrumServer {
} }
setReferences.remove(null); setReferences.remove(null);
setReferences.remove(UNFETCHABLE_BLOCK_TRANSACTION); setReferences.remove(UNFETCHABLE_BLOCK_TRANSACTION);
setReferences.removeIf(ref -> transactionMap.get(ref.getHash()) != null);
List<BlockTransaction> blockTransactions = new ArrayList<>(transaction.getOutputs().size()); List<BlockTransaction> blockTransactions = new ArrayList<>(transaction.getOutputs().size());
for(int i = 0; i < transaction.getOutputs().size(); i++) { for(int i = 0; i < transaction.getOutputs().size(); i++) {
blockTransactions.add(null); blockTransactions.add(null);
} }
Map<Sha256Hash, BlockTransaction> transactionMap = new HashMap<>();
if(!setReferences.isEmpty()) { if(!setReferences.isEmpty()) {
Map<Integer, BlockHeader> blockHeaderMap = electrumServer.getBlockHeaders(null, setReferences); Map<Integer, BlockHeader> blockHeaderMap = electrumServer.getBlockHeaders(null, setReferences);
transactionMap = electrumServer.getTransactions(null, setReferences, blockHeaderMap); transactionMap.putAll(electrumServer.getTransactions(null, setReferences, blockHeaderMap));
} }
for(int i = 0; i < outputTransactionReferences.size(); i++) { for(int i = 0; i < outputTransactionReferences.size(); i++) {

View file

@ -5,8 +5,7 @@ import com.sparrowwallet.drongo.protocol.*;
import com.sparrowwallet.drongo.psbt.PSBT; import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.psbt.PSBTInput; import com.sparrowwallet.drongo.psbt.PSBTInput;
import com.sparrowwallet.drongo.psbt.PSBTOutput; import com.sparrowwallet.drongo.psbt.PSBTOutput;
import com.sparrowwallet.drongo.wallet.BlockTransaction; import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.TransactionTabData; import com.sparrowwallet.sparrow.TransactionTabData;
@ -33,6 +32,7 @@ import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TransactionController implements Initializable { public class TransactionController implements Initializable {
private static final Logger log = LoggerFactory.getLogger(TransactionController.class); private static final Logger log = LoggerFactory.getLogger(TransactionController.class);
@ -426,7 +426,44 @@ public class TransactionController implements Initializable {
private void fetchOutputBlockTransactions(int indexStart, int indexEnd) { private void fetchOutputBlockTransactions(int indexStart, int indexEnd) {
if(AppServices.isConnected() && getPSBT() == null && indexStart < getTransaction().getOutputs().size()) { if(AppServices.isConnected() && getPSBT() == null && indexStart < getTransaction().getOutputs().size()) {
int maxIndex = Math.min(getTransaction().getOutputs().size(), indexEnd); int maxIndex = Math.min(getTransaction().getOutputs().size(), indexEnd);
ElectrumServer.TransactionOutputsReferenceService transactionOutputsReferenceService = new ElectrumServer.TransactionOutputsReferenceService(getTransaction(), indexStart, maxIndex);
Map<Sha256Hash, BlockTransaction> transactionMap = new HashMap<>();
List<Set<BlockTransactionHash>> blockTransactionHashes = new ArrayList<>(getTransaction().getOutputs().size());
for(int i = 0; i < getTransaction().getOutputs().size(); i++) {
blockTransactionHashes.add(null);
}
Map<Script, WalletNode> openWalletOutputScripts = new HashMap<>();
for(Wallet wallet : AppServices.get().getOpenWallets().keySet()) {
openWalletOutputScripts.putAll(wallet.getWalletOutputScripts());
for(Wallet childWallet : wallet.getChildWallets()) {
if(!childWallet.isNested()) {
openWalletOutputScripts.putAll(childWallet.getWalletOutputScripts());
}
}
}
for(int i = indexStart; i < getTransaction().getOutputs().size() && i < maxIndex; i++) {
TransactionOutput output = getTransaction().getOutputs().get(i);
List<ScriptChunk> chunks = output.getScript().getChunks();
if(!chunks.isEmpty() && chunks.get(0).isOpCode() && chunks.get(0).getOpcode() == ScriptOpCodes.OP_RETURN) {
blockTransactionHashes.set(i, Collections.emptySet());
} else if(openWalletOutputScripts.get(output.getScript()) != null) {
WalletNode walletNode = openWalletOutputScripts.get(output.getScript());
if(walletNode != null) {
Set<BlockTransactionHash> references = walletNode.getTransactionOutputs().stream().flatMap(txo -> txo.isSpent() ? Stream.of(txo.getSpentBy()) : Stream.empty()).collect(Collectors.toCollection(TreeSet::new));
blockTransactionHashes.set(i, references);
for(BlockTransactionHash reference : references) {
BlockTransaction blkTx = walletNode.getWallet().getWalletTransaction(reference.getHash());
if(blkTx != null) {
transactionMap.put(reference.getHash(), blkTx);
}
}
}
}
}
ElectrumServer.TransactionOutputsReferenceService transactionOutputsReferenceService = new ElectrumServer.TransactionOutputsReferenceService(getTransaction(), indexStart, maxIndex, blockTransactionHashes, transactionMap);
transactionOutputsReferenceService.setOnSucceeded(successEvent -> { transactionOutputsReferenceService.setOnSucceeded(successEvent -> {
List<BlockTransaction> outputTransactions = transactionOutputsReferenceService.getValue(); List<BlockTransaction> outputTransactions = transactionOutputsReferenceService.getValue();
Platform.runLater(() -> { Platform.runLater(() -> {