From 800512ee5becdc08edcfdc3754d330742258faeb Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Thu, 18 Jun 2020 12:58:59 +0200 Subject: [PATCH] fix bugs with txes with many outputs --- .../sparrow/io/ElectrumServer.java | 33 ++++++++++++------- .../sparrow/transaction/OutputController.java | 4 +-- .../sparrow/transaction/OutputForm.java | 4 --- .../sparrow/transaction/TransactionData.java | 4 +++ .../sparrow/transaction/TransactionForm.java | 4 +++ 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java b/src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java index 01a4bc0b..073ec494 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java @@ -326,6 +326,7 @@ public class ElectrumServer { batchRequest.add(reference.getHashAsString(), "blockchain.transaction.get", reference.getHashAsString()); } + String strErrorTx = Sha256Hash.ZERO_HASH.toString(); Map result; try { result = batchRequest.execute(); @@ -333,21 +334,22 @@ public class ElectrumServer { result = (Map)e.getSuccesses(); for(Object hash : e.getErrors().keySet()) { String txhash = (String)hash; - result.put(txhash, Sha256Hash.ZERO_HASH.toString()); + result.put(txhash, strErrorTx); } } Map transactionMap = new HashMap<>(); for(String txid : result.keySet()) { Sha256Hash hash = Sha256Hash.wrap(txid); + String strRawTx = result.get(txid); - if(hash.equals(Sha256Hash.ZERO_HASH)) { + if(strRawTx.equals(strErrorTx)) { transactionMap.put(hash, UNFETCHABLE_BLOCK_TRANSACTION); checkReferences.removeIf(ref -> ref.getHash().equals(hash)); continue; } - byte[] rawtx = Utils.hexToBytes(result.get(txid)); + byte[] rawtx = Utils.hexToBytes(strRawTx); Transaction transaction = new Transaction(rawtx); Optional optionalReference = references.stream().filter(reference -> reference.getHash().equals(hash)).findFirst(); @@ -420,12 +422,12 @@ public class ElectrumServer { Sha256Hash previousHash = input.getOutpoint().getHash(); BlockTransaction previousTransaction = wallet.getTransactions().get(previousHash); - if(previousTransaction.equals(UNFETCHABLE_BLOCK_TRANSACTION)) { - throw new IllegalStateException("Could not retrieve transaction for hash " + reference.getHashAsString()); - } else if(previousTransaction == null) { + 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; + } else if(previousTransaction.equals(UNFETCHABLE_BLOCK_TRANSACTION)) { + throw new IllegalStateException("Could not retrieve transaction for hash " + reference.getHashAsString()); } Optional optionalTxHash = history.stream().filter(txHash -> txHash.getHash().equals(previousHash)).findFirst(); @@ -957,13 +959,22 @@ public class ElectrumServer { if(outputReferences != null) { for(BlockTransactionHash reference : outputReferences) { if(reference == UNFETCHABLE_BLOCK_TRANSACTION) { - blockTransactions.set(i, UNFETCHABLE_BLOCK_TRANSACTION); + if(blockTransactions.get(i) == null) { + blockTransactions.set(i, UNFETCHABLE_BLOCK_TRANSACTION); + } } else { BlockTransaction blockTransaction = transactionMap.get(reference.getHash()); - for(TransactionInput input : blockTransaction.getTransaction().getInputs()) { - if(input.getOutpoint().getHash().equals(transaction.getTxId()) && input.getOutpoint().getIndex() == i) { - if(blockTransactions.set(i, blockTransaction) != null) { - throw new IllegalStateException("Double spend detected for output #" + i + " on hash " + reference.getHash()); + if(blockTransaction.equals(UNFETCHABLE_BLOCK_TRANSACTION)) { + if(blockTransactions.get(i) == null) { + blockTransactions.set(i, UNFETCHABLE_BLOCK_TRANSACTION); + } + } else { + for(TransactionInput input : blockTransaction.getTransaction().getInputs()) { + if(input.getOutpoint().getHash().equals(transaction.getTxId()) && input.getOutpoint().getIndex() == i) { + BlockTransaction previousTx = blockTransactions.set(i, blockTransaction); + if(previousTx != null && !previousTx.equals(UNFETCHABLE_BLOCK_TRANSACTION)) { + throw new IllegalStateException("Double spend detected for output #" + i + " on hash " + reference.getHash()); + } } } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java index 0b4f8d22..c13564bf 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java @@ -85,7 +85,7 @@ public class OutputController extends TransactionFormController implements Initi if(outputForm.getPsbt() != null) { spent.setText("Unspent"); - } else if(outputForm.getOutputTransactions() != null) { + } else if(outputForm.getOutputTransactions() != null && outputForm.getIndex() < outputForm.getMaxOutputFetched()) { updateSpent(outputForm.getOutputTransactions()); } else { spent.setText("Unknown"); @@ -96,7 +96,7 @@ public class OutputController extends TransactionFormController implements Initi } private void updateSpent(List outputTransactions) { - int outputIndex = outputForm.getTransactionOutputIndex(); + int outputIndex = outputForm.getIndex(); spent.setText("Unspent"); if(outputIndex >= 0 && outputIndex < outputTransactions.size()) { diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java index 235f6851..67717240 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java @@ -30,10 +30,6 @@ public class OutputForm extends IndexedTransactionForm { return psbtOutput; } - public int getTransactionOutputIndex() { - return getTransaction().getOutputs().indexOf(transactionOutput); - } - @Override public Node getContents() throws IOException { FXMLLoader loader = new FXMLLoader(getClass().getResource("output.fxml")); diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionData.java b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionData.java index 09680c87..c9d22e3b 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionData.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionData.java @@ -102,6 +102,10 @@ public class TransactionData { this.maxOutputFetched = Math.max(maxOutputFetched, pageEnd); } + public int getMaxOutputFetched() { + return maxOutputFetched; + } + public boolean allOutputsFetched() { return minOutputFetched == 0 && maxOutputFetched == transaction.getOutputs().size(); } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java index 12517ff8..f99b5d96 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java @@ -45,6 +45,10 @@ public abstract class TransactionForm { return txdata.getOutputTransactions(); } + public int getMaxOutputFetched() { + return txdata.getMaxOutputFetched(); + } + public boolean allOutputsFetched() { return txdata.allOutputsFetched(); }