mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-04 21:36:45 +00:00
handle server returning history with the same tx at multiple heights (electrs issue #316)
This commit is contained in:
parent
1392199f5c
commit
8d413e839c
3 changed files with 59 additions and 6 deletions
|
@ -233,7 +233,19 @@ public class ElectrumServer {
|
||||||
if(optionalNode.isPresent()) {
|
if(optionalNode.isPresent()) {
|
||||||
WalletNode node = optionalNode.get();
|
WalletNode node = optionalNode.get();
|
||||||
|
|
||||||
Set<BlockTransactionHash> references = Arrays.stream(txes).map(ScriptHashTx::getBlockchainTransactionHash).collect(Collectors.toCollection(TreeSet::new));
|
//Some servers can return the same tx as multiple ScriptHashTx entries with different heights. Take the highest height only
|
||||||
|
Set<BlockTransactionHash> references = Arrays.stream(txes).map(ScriptHashTx::getBlockchainTransactionHash)
|
||||||
|
.collect(TreeSet::new, (set, ref) -> {
|
||||||
|
Optional<BlockTransactionHash> optExisting = set.stream().filter(prev -> prev.getHash().equals(ref.getHash())).findFirst();
|
||||||
|
if(optExisting.isPresent()) {
|
||||||
|
if(optExisting.get().getHeight() < ref.getHeight()) {
|
||||||
|
set.remove(optExisting.get());
|
||||||
|
set.add(ref);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
set.add(ref);
|
||||||
|
}
|
||||||
|
}, TreeSet::addAll);
|
||||||
Set<BlockTransactionHash> existingReferences = nodeTransactionMap.get(node);
|
Set<BlockTransactionHash> existingReferences = nodeTransactionMap.get(node);
|
||||||
|
|
||||||
if(existingReferences == null) {
|
if(existingReferences == null) {
|
||||||
|
|
|
@ -2,10 +2,9 @@ package com.sparrowwallet.sparrow.wallet;
|
||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.sparrowwallet.drongo.KeyPurpose;
|
import com.sparrowwallet.drongo.KeyPurpose;
|
||||||
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
import com.sparrowwallet.drongo.protocol.TransactionInput;
|
||||||
import com.sparrowwallet.drongo.wallet.BlockTransactionHash;
|
import com.sparrowwallet.drongo.protocol.TransactionOutput;
|
||||||
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
import com.sparrowwallet.drongo.wallet.*;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.WalletTabData;
|
import com.sparrowwallet.sparrow.WalletTabData;
|
||||||
import com.sparrowwallet.sparrow.event.WalletBlockHeightChangedEvent;
|
import com.sparrowwallet.sparrow.event.WalletBlockHeightChangedEvent;
|
||||||
|
@ -85,6 +84,35 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isComplete() {
|
||||||
|
int validEntries = 0;
|
||||||
|
Map<BlockTransactionHashIndex, WalletNode> walletTxos = wallet.getWalletTxos();
|
||||||
|
for(TransactionInput txInput : blockTransaction.getTransaction().getInputs()) {
|
||||||
|
Optional<BlockTransactionHashIndex> optRef = walletTxos.keySet().stream().filter(ref -> ref.getHash().equals(txInput.getOutpoint().getHash()) && ref.getIndex() == txInput.getOutpoint().getIndex()).findFirst();
|
||||||
|
if(optRef.isPresent()) {
|
||||||
|
validEntries++;
|
||||||
|
if(getChildren().stream().noneMatch(entry -> ((HashIndexEntry)entry).getHashIndex().equals(optRef.get().getSpentBy()) && ((HashIndexEntry)entry).getType().equals(HashIndexEntry.Type.INPUT))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(TransactionOutput txOutput : blockTransaction.getTransaction().getOutputs()) {
|
||||||
|
Optional<BlockTransactionHashIndex> optRef = walletTxos.keySet().stream().filter(ref -> ref.getHash().equals(txOutput.getHash()) && ref.getIndex() == txOutput.getIndex()).findFirst();
|
||||||
|
if(optRef.isPresent()) {
|
||||||
|
validEntries++;
|
||||||
|
if(getChildren().stream().noneMatch(entry -> ((HashIndexEntry)entry).getHashIndex().equals(optRef.get()) && ((HashIndexEntry)entry).getType().equals(HashIndexEntry.Type.OUTPUT))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getChildren().size() != validEntries) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static List<Entry> createChildEntries(Wallet wallet, Map<BlockTransactionHashIndex, KeyPurpose> incoming, Map<BlockTransactionHashIndex, KeyPurpose> outgoing) {
|
private static List<Entry> createChildEntries(Wallet wallet, Map<BlockTransactionHashIndex, KeyPurpose> incoming, Map<BlockTransactionHashIndex, KeyPurpose> outgoing) {
|
||||||
List<Entry> incomingOutputEntries = incoming.entrySet().stream().map(input -> new TransactionHashIndexEntry(wallet, input.getKey(), HashIndexEntry.Type.OUTPUT, input.getValue())).collect(Collectors.toList());
|
List<Entry> incomingOutputEntries = incoming.entrySet().stream().map(input -> new TransactionHashIndexEntry(wallet, input.getKey(), HashIndexEntry.Type.OUTPUT, input.getValue())).collect(Collectors.toList());
|
||||||
List<Entry> outgoingInputEntries = outgoing.entrySet().stream().map(output -> new TransactionHashIndexEntry(wallet, output.getKey(), HashIndexEntry.Type.INPUT, output.getValue())).collect(Collectors.toList());
|
List<Entry> outgoingInputEntries = outgoing.entrySet().stream().map(output -> new TransactionHashIndexEntry(wallet, output.getKey(), HashIndexEntry.Type.INPUT, output.getValue())).collect(Collectors.toList());
|
||||||
|
|
|
@ -9,11 +9,15 @@ import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.event.NewWalletTransactionsEvent;
|
import com.sparrowwallet.sparrow.event.NewWalletTransactionsEvent;
|
||||||
import javafx.beans.property.LongProperty;
|
import javafx.beans.property.LongProperty;
|
||||||
import javafx.beans.property.LongPropertyBase;
|
import javafx.beans.property.LongPropertyBase;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class WalletTransactionsEntry extends Entry {
|
public class WalletTransactionsEntry extends Entry {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(WalletTransactionsEntry.class);
|
||||||
|
|
||||||
private final Wallet wallet;
|
private final Wallet wallet;
|
||||||
|
|
||||||
public WalletTransactionsEntry(Wallet wallet) {
|
public WalletTransactionsEntry(Wallet wallet) {
|
||||||
|
@ -67,12 +71,21 @@ public class WalletTransactionsEntry extends Entry {
|
||||||
|
|
||||||
calculateBalances();
|
calculateBalances();
|
||||||
|
|
||||||
if(!entriesAdded.isEmpty()) {
|
List<Entry> entriesComplete = entriesAdded.stream().filter(txEntry -> ((TransactionEntry)txEntry).isComplete()).collect(Collectors.toList());
|
||||||
|
if(!entriesComplete.isEmpty()) {
|
||||||
List<BlockTransaction> blockTransactions = entriesAdded.stream().map(txEntry -> ((TransactionEntry)txEntry).getBlockTransaction()).collect(Collectors.toList());
|
List<BlockTransaction> blockTransactions = entriesAdded.stream().map(txEntry -> ((TransactionEntry)txEntry).getBlockTransaction()).collect(Collectors.toList());
|
||||||
long totalBlockchainValue = entriesAdded.stream().filter(txEntry -> ((TransactionEntry)txEntry).getConfirmations() > 0).mapToLong(Entry::getValue).sum();
|
long totalBlockchainValue = entriesAdded.stream().filter(txEntry -> ((TransactionEntry)txEntry).getConfirmations() > 0).mapToLong(Entry::getValue).sum();
|
||||||
long totalMempoolValue = entriesAdded.stream().filter(txEntry -> ((TransactionEntry)txEntry).getConfirmations() == 0).mapToLong(Entry::getValue).sum();
|
long totalMempoolValue = entriesAdded.stream().filter(txEntry -> ((TransactionEntry)txEntry).getConfirmations() == 0).mapToLong(Entry::getValue).sum();
|
||||||
EventManager.get().post(new NewWalletTransactionsEvent(wallet, blockTransactions, totalBlockchainValue, totalMempoolValue));
|
EventManager.get().post(new NewWalletTransactionsEvent(wallet, blockTransactions, totalBlockchainValue, totalMempoolValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(entriesAdded.size() > entriesComplete.size()) {
|
||||||
|
entriesAdded.removeAll(entriesComplete);
|
||||||
|
for(Entry entry : entriesAdded) {
|
||||||
|
TransactionEntry txEntry = (TransactionEntry)entry;
|
||||||
|
log.warn("Not notifying for incomplete entry " + ((TransactionEntry)entry).getBlockTransaction().getHashAsString() + " value " + txEntry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Collection<WalletTransaction> getWalletTransactions(Wallet wallet) {
|
private static Collection<WalletTransaction> getWalletTransactions(Wallet wallet) {
|
||||||
|
|
Loading…
Reference in a new issue