From a25d020e54543cd48be1501f4390cff4c8daeee1 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Tue, 2 Jun 2020 09:20:59 +0200 Subject: [PATCH] refactor transaction history storage --- .../drongo/wallet/BlockchainTransaction.java | 22 +++++++ .../wallet/BlockchainTransactionHash.java | 39 ++++++++---- .../BlockchainTransactionHashIndex.java | 44 +++++++++++--- .../sparrowwallet/drongo/wallet/Wallet.java | 60 +------------------ .../drongo/wallet/WalletNode.java | 53 ++++++++++------ .../drongo/wallet/WalletNodeHistory.java | 35 ----------- 6 files changed, 122 insertions(+), 131 deletions(-) create mode 100644 src/main/java/com/sparrowwallet/drongo/wallet/BlockchainTransaction.java delete mode 100644 src/main/java/com/sparrowwallet/drongo/wallet/WalletNodeHistory.java diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/BlockchainTransaction.java b/src/main/java/com/sparrowwallet/drongo/wallet/BlockchainTransaction.java new file mode 100644 index 0000000..65f644a --- /dev/null +++ b/src/main/java/com/sparrowwallet/drongo/wallet/BlockchainTransaction.java @@ -0,0 +1,22 @@ +package com.sparrowwallet.drongo.wallet; + +import com.sparrowwallet.drongo.protocol.Sha256Hash; +import com.sparrowwallet.drongo.protocol.Transaction; + +public class BlockchainTransaction extends BlockchainTransactionHash implements Comparable { + private final Transaction transaction; + + public BlockchainTransaction(Sha256Hash hash, int height, Long fee, Transaction transaction) { + super(hash, height, fee); + this.transaction = transaction; + } + + public Transaction getTransaction() { + return transaction; + } + + @Override + public int compareTo(BlockchainTransaction blockchainTransaction) { + return super.compareTo(blockchainTransaction); + } +} diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/BlockchainTransactionHash.java b/src/main/java/com/sparrowwallet/drongo/wallet/BlockchainTransactionHash.java index 70fca0b..5d49575 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/BlockchainTransactionHash.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/BlockchainTransactionHash.java @@ -4,20 +4,22 @@ import com.sparrowwallet.drongo.protocol.Sha256Hash; import java.util.Objects; -public class BlockchainTransactionHash implements Comparable { +public abstract class BlockchainTransactionHash { private final Sha256Hash hash; - private final Integer height; + private final int height; private final Long fee; + private String label; + public BlockchainTransactionHash(Sha256Hash hash) { this(hash, 0, 0L); } - public BlockchainTransactionHash(Sha256Hash hash, Integer height) { + public BlockchainTransactionHash(Sha256Hash hash, int height) { this(hash, height, 0L); } - public BlockchainTransactionHash(Sha256Hash hash, Integer height, Long fee) { + public BlockchainTransactionHash(Sha256Hash hash, int height, Long fee) { this.hash = hash; this.height = height; this.fee = fee; @@ -31,7 +33,7 @@ public class BlockchainTransactionHash implements Comparable { private final long index; - private final BlockchainTransactionHashIndex spentBy; + private final long value; + private BlockchainTransactionHashIndex spentBy; - public BlockchainTransactionHashIndex(Sha256Hash hash, Integer height, Long fee, long index) { - this(hash, height, fee, index, null); + public BlockchainTransactionHashIndex(Sha256Hash hash, int height, Long fee, long index, long value) { + this(hash, height, fee, index, value, null); } - public BlockchainTransactionHashIndex(Sha256Hash hash, Integer height, Long fee, long index, BlockchainTransactionHashIndex spentBy) { + public BlockchainTransactionHashIndex(Sha256Hash hash, int height, Long fee, long index, long value, BlockchainTransactionHashIndex spentBy) { super(hash, height, fee); this.index = index; + this.value = value; this.spentBy = spentBy; } @@ -22,6 +24,10 @@ public class BlockchainTransactionHashIndex extends BlockchainTransactionHash { return index; } + public long getValue() { + return value; + } + public boolean isSpent() { return spentBy != null; } @@ -30,18 +36,40 @@ public class BlockchainTransactionHashIndex extends BlockchainTransactionHash { return spentBy; } + public void setSpentBy(BlockchainTransactionHashIndex spentBy) { + this.spentBy = spentBy; + } + + @Override + public String toString() { + return getHash().toString() + ":" + index; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; BlockchainTransactionHashIndex that = (BlockchainTransactionHashIndex) o; - return index == that.index && - Objects.equals(spentBy, that.spentBy); + return index == that.index; } @Override public int hashCode() { - return Objects.hash(super.hashCode(), index, spentBy); + return Objects.hash(super.hashCode(), index); + } + + @Override + public int compareTo(BlockchainTransactionHashIndex reference) { + int diff = super.compareTo(reference); + if(diff != 0) { + return diff; + } + + return (int)(index - reference.index); + } + + public BlockchainTransactionHashIndex copy() { + return new BlockchainTransactionHashIndex(super.getHash(), super.getHeight(), super.getFee(), index, value, spentBy.copy()); } } diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java index 31ae176..1d89f5c 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java @@ -21,7 +21,7 @@ public class Wallet { private Policy defaultPolicy; private List keystores = new ArrayList<>(); private final Set purposeNodes = new TreeSet<>(); - private final Map transactions = new HashMap<>(); + private final Map transactions = new HashMap<>(); public Wallet() { } @@ -82,7 +82,7 @@ public class Wallet { return purposeNodes; } - public Map getTransactions() { + public Map getTransactions() { return transactions; } @@ -193,66 +193,12 @@ public class Wallet { public void clearHistory() { for(WalletNode purposeNode : purposeNodes) { - purposeNode.getHistory().clear(); - for(WalletNode addressNode : purposeNode.getChildren()) { - addressNode.getHistory().clear(); - } + purposeNode.clearHistory(); } transactions.clear(); } - public WalletNodeHistory getNodeHistory(WalletNode node) { - Set receivedTXOs = new TreeSet<>(); - Set spentTXOs = new TreeSet<>(); - Set spendingTXIs = new TreeSet<>(); - - Script nodeScript = getOutputScript(node); - for(BlockchainTransactionHash reference : node.getHistory()) { - Transaction transaction = transactions.get(reference.getHash()); - if(transaction == null) { - throw new IllegalStateException("Could not retrieve transaction for hash " + reference.getHashAsString()); - } - - for(int inputIndex = 0; inputIndex < transaction.getInputs().size(); inputIndex++) { - TransactionInput input = transaction.getInputs().get(inputIndex); - Sha256Hash previousHash = input.getOutpoint().getHash(); - Transaction previousTransaction = transactions.get(previousHash); - if(previousTransaction == null) { - //No referenced transaction found, cannot check if spends from wallet - //This is fine so long as all referenced transactions have been returned, in which case this refers to a transaction that does not affect this wallet - continue; - } - - Optional optionalTxHash = node.getHistory().stream().filter(txHash -> txHash.getHash().equals(previousHash)).findFirst(); - if(optionalTxHash.isEmpty()) { - //No previous transaction history found, cannot check if spends from wallet - //This is fine so long as all referenced transactions have been returned, in which case this refers to a transaction that does not affect this wallet node - continue; - } - - BlockchainTransactionHash spentTxHash = optionalTxHash.get(); - TransactionOutput spentOutput = previousTransaction.getOutputs().get((int)input.getOutpoint().getIndex()); - if(spentOutput.getScript().equals(nodeScript)) { - BlockchainTransactionHashIndex spendingTXI = new BlockchainTransactionHashIndex(reference.getHash(), reference.getHeight(), reference.getFee(), inputIndex); - spendingTXIs.add(spendingTXI); - BlockchainTransactionHashIndex spentTXO = new BlockchainTransactionHashIndex(spentTxHash.getHash(), spentTxHash.getHeight(), spentTxHash.getFee(), input.getOutpoint().getIndex(), spendingTXI); - spentTXOs.add(spentTXO); - } - } - - for(int outputIndex = 0; outputIndex < transaction.getOutputs().size(); outputIndex++) { - TransactionOutput output = transaction.getOutputs().get(outputIndex); - if(output.getScript().equals(nodeScript)) { - BlockchainTransactionHashIndex receivingTXO = new BlockchainTransactionHashIndex(reference.getHash(), reference.getHeight(), reference.getFee(), outputIndex); - receivedTXOs.add(receivingTXO); - } - } - } - - return new WalletNodeHistory(receivedTXOs, spentTXOs, spendingTXIs); - } - public boolean isValid() { if(policyType == null || scriptType == null || defaultPolicy == null || keystores.isEmpty()) { return false; diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/WalletNode.java b/src/main/java/com/sparrowwallet/drongo/wallet/WalletNode.java index 0e06aff..067a4f4 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/WalletNode.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/WalletNode.java @@ -4,17 +4,14 @@ import com.sparrowwallet.drongo.KeyDerivation; import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.crypto.ChildNumber; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; +import java.util.stream.Collectors; public class WalletNode implements Comparable { private final String derivationPath; private String label; - private Long amount; private Set children = new TreeSet<>(); - private Set history = new TreeSet<>(); + private Set transactionOutputs = new TreeSet<>(); private transient KeyPurpose keyPurpose; private transient int index = -1; @@ -81,12 +78,12 @@ public class WalletNode implements Comparable { this.label = label; } - public Long getAmount() { - return amount; - } + public Long getValue() { + if(transactionOutputs == null) { + return null; + } - public void setAmount(Long amount) { - this.amount = amount; + return getUnspentTransactionOutputs().stream().mapToLong(BlockchainTransactionHashIndex::getValue).sum(); } public Set getChildren() { @@ -97,12 +94,17 @@ public class WalletNode implements Comparable { this.children = children; } - public Set getHistory() { - return history; + public Set getTransactionOutputs() { + return transactionOutputs; } - public void setHistory(Set history) { - this.history = history; + public void setTransactionOutputs(Set transactionOutputs) { + this.transactionOutputs = transactionOutputs; + } + + public Set getUnspentTransactionOutputs() { + Set unspentTXOs = new TreeSet<>(transactionOutputs); + return unspentTXOs.stream().filter(txo -> !txo.isSpent()).collect(Collectors.toCollection(HashSet::new)); } public void fillToIndex(int index) { @@ -115,7 +117,7 @@ public class WalletNode implements Comparable { public Integer getHighestUsedIndex() { WalletNode highestNode = null; for(WalletNode childNode : getChildren()) { - if(!childNode.getHistory().isEmpty()) { + if(!childNode.getTransactionOutputs().isEmpty()) { highestNode = childNode; } } @@ -123,6 +125,11 @@ public class WalletNode implements Comparable { return highestNode == null ? null : highestNode.index; } + @Override + public String toString() { + return derivationPath; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -141,15 +148,23 @@ public class WalletNode implements Comparable { return getIndex() - node.getIndex(); } + public void clearHistory() { + transactionOutputs.clear(); + for(WalletNode childNode : getChildren()) { + childNode.clearHistory(); + } + } + public WalletNode copy() { WalletNode copy = new WalletNode(derivationPath); copy.setLabel(label); - copy.setAmount(amount); + for(WalletNode child : getChildren()) { copy.getChildren().add(child.copy()); } - for(BlockchainTransactionHash reference : getHistory()) { - copy.getHistory().add(reference.copy()); + + for(BlockchainTransactionHashIndex txo : getTransactionOutputs()) { + copy.getTransactionOutputs().add(txo.copy()); } return copy; diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/WalletNodeHistory.java b/src/main/java/com/sparrowwallet/drongo/wallet/WalletNodeHistory.java deleted file mode 100644 index 5585574..0000000 --- a/src/main/java/com/sparrowwallet/drongo/wallet/WalletNodeHistory.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.sparrowwallet.drongo.wallet; - -import java.util.Set; -import java.util.TreeSet; - -public class WalletNodeHistory { - private final Set receivedTXOs; - private final Set spentTXOs; - private final Set spendingTXIs; - - public WalletNodeHistory(Set receivedTXOs, Set spentTXOs, Set spendingTXIs) { - this.receivedTXOs = receivedTXOs; - this.spentTXOs = spentTXOs; - this.spendingTXIs = spendingTXIs; - } - - public Set getReceivedTXOs() { - return receivedTXOs; - } - - public Set getSpentTXOs() { - return spentTXOs; - } - - public Set getSpendingTXIs() { - return spendingTXIs; - } - - public Set getUnspentTXOs() { - Set unspentTXOs = new TreeSet<>(receivedTXOs); - unspentTXOs.removeAll(spentTXOs); - - return unspentTXOs; - } -}