fix bugs with txes with many outputs

This commit is contained in:
Craig Raw 2020-06-18 12:58:59 +02:00
parent 0cd97649cc
commit 800512ee5b
5 changed files with 32 additions and 17 deletions

View file

@ -326,6 +326,7 @@ public class ElectrumServer {
batchRequest.add(reference.getHashAsString(), "blockchain.transaction.get", reference.getHashAsString()); batchRequest.add(reference.getHashAsString(), "blockchain.transaction.get", reference.getHashAsString());
} }
String strErrorTx = Sha256Hash.ZERO_HASH.toString();
Map<String, String> result; Map<String, String> result;
try { try {
result = batchRequest.execute(); result = batchRequest.execute();
@ -333,21 +334,22 @@ public class ElectrumServer {
result = (Map<String, String>)e.getSuccesses(); result = (Map<String, String>)e.getSuccesses();
for(Object hash : e.getErrors().keySet()) { for(Object hash : e.getErrors().keySet()) {
String txhash = (String)hash; String txhash = (String)hash;
result.put(txhash, Sha256Hash.ZERO_HASH.toString()); result.put(txhash, strErrorTx);
} }
} }
Map<Sha256Hash, BlockTransaction> transactionMap = new HashMap<>(); Map<Sha256Hash, BlockTransaction> transactionMap = new HashMap<>();
for(String txid : result.keySet()) { for(String txid : result.keySet()) {
Sha256Hash hash = Sha256Hash.wrap(txid); 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); transactionMap.put(hash, UNFETCHABLE_BLOCK_TRANSACTION);
checkReferences.removeIf(ref -> ref.getHash().equals(hash)); checkReferences.removeIf(ref -> ref.getHash().equals(hash));
continue; continue;
} }
byte[] rawtx = Utils.hexToBytes(result.get(txid)); byte[] rawtx = Utils.hexToBytes(strRawTx);
Transaction transaction = new Transaction(rawtx); Transaction transaction = new Transaction(rawtx);
Optional<BlockTransactionHash> optionalReference = references.stream().filter(reference -> reference.getHash().equals(hash)).findFirst(); Optional<BlockTransactionHash> optionalReference = references.stream().filter(reference -> reference.getHash().equals(hash)).findFirst();
@ -420,12 +422,12 @@ public class ElectrumServer {
Sha256Hash previousHash = input.getOutpoint().getHash(); Sha256Hash previousHash = input.getOutpoint().getHash();
BlockTransaction previousTransaction = wallet.getTransactions().get(previousHash); BlockTransaction previousTransaction = wallet.getTransactions().get(previousHash);
if(previousTransaction.equals(UNFETCHABLE_BLOCK_TRANSACTION)) { if(previousTransaction == null) {
throw new IllegalStateException("Could not retrieve transaction for hash " + reference.getHashAsString());
} else if(previousTransaction == null) {
//No referenced transaction found, cannot check if spends from wallet //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 //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; continue;
} else if(previousTransaction.equals(UNFETCHABLE_BLOCK_TRANSACTION)) {
throw new IllegalStateException("Could not retrieve transaction for hash " + reference.getHashAsString());
} }
Optional<BlockTransactionHash> optionalTxHash = history.stream().filter(txHash -> txHash.getHash().equals(previousHash)).findFirst(); Optional<BlockTransactionHash> optionalTxHash = history.stream().filter(txHash -> txHash.getHash().equals(previousHash)).findFirst();
@ -957,13 +959,22 @@ public class ElectrumServer {
if(outputReferences != null) { if(outputReferences != null) {
for(BlockTransactionHash reference : outputReferences) { for(BlockTransactionHash reference : outputReferences) {
if(reference == UNFETCHABLE_BLOCK_TRANSACTION) { if(reference == UNFETCHABLE_BLOCK_TRANSACTION) {
blockTransactions.set(i, UNFETCHABLE_BLOCK_TRANSACTION); if(blockTransactions.get(i) == null) {
blockTransactions.set(i, UNFETCHABLE_BLOCK_TRANSACTION);
}
} else { } else {
BlockTransaction blockTransaction = transactionMap.get(reference.getHash()); BlockTransaction blockTransaction = transactionMap.get(reference.getHash());
for(TransactionInput input : blockTransaction.getTransaction().getInputs()) { if(blockTransaction.equals(UNFETCHABLE_BLOCK_TRANSACTION)) {
if(input.getOutpoint().getHash().equals(transaction.getTxId()) && input.getOutpoint().getIndex() == i) { if(blockTransactions.get(i) == null) {
if(blockTransactions.set(i, blockTransaction) != null) { blockTransactions.set(i, UNFETCHABLE_BLOCK_TRANSACTION);
throw new IllegalStateException("Double spend detected for output #" + i + " on hash " + reference.getHash()); }
} 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());
}
} }
} }
} }

View file

@ -85,7 +85,7 @@ public class OutputController extends TransactionFormController implements Initi
if(outputForm.getPsbt() != null) { if(outputForm.getPsbt() != null) {
spent.setText("Unspent"); spent.setText("Unspent");
} else if(outputForm.getOutputTransactions() != null) { } else if(outputForm.getOutputTransactions() != null && outputForm.getIndex() < outputForm.getMaxOutputFetched()) {
updateSpent(outputForm.getOutputTransactions()); updateSpent(outputForm.getOutputTransactions());
} else { } else {
spent.setText("Unknown"); spent.setText("Unknown");
@ -96,7 +96,7 @@ public class OutputController extends TransactionFormController implements Initi
} }
private void updateSpent(List<BlockTransaction> outputTransactions) { private void updateSpent(List<BlockTransaction> outputTransactions) {
int outputIndex = outputForm.getTransactionOutputIndex(); int outputIndex = outputForm.getIndex();
spent.setText("Unspent"); spent.setText("Unspent");
if(outputIndex >= 0 && outputIndex < outputTransactions.size()) { if(outputIndex >= 0 && outputIndex < outputTransactions.size()) {

View file

@ -30,10 +30,6 @@ public class OutputForm extends IndexedTransactionForm {
return psbtOutput; return psbtOutput;
} }
public int getTransactionOutputIndex() {
return getTransaction().getOutputs().indexOf(transactionOutput);
}
@Override @Override
public Node getContents() throws IOException { public Node getContents() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("output.fxml")); FXMLLoader loader = new FXMLLoader(getClass().getResource("output.fxml"));

View file

@ -102,6 +102,10 @@ public class TransactionData {
this.maxOutputFetched = Math.max(maxOutputFetched, pageEnd); this.maxOutputFetched = Math.max(maxOutputFetched, pageEnd);
} }
public int getMaxOutputFetched() {
return maxOutputFetched;
}
public boolean allOutputsFetched() { public boolean allOutputsFetched() {
return minOutputFetched == 0 && maxOutputFetched == transaction.getOutputs().size(); return minOutputFetched == 0 && maxOutputFetched == transaction.getOutputs().size();
} }

View file

@ -45,6 +45,10 @@ public abstract class TransactionForm {
return txdata.getOutputTransactions(); return txdata.getOutputTransactions();
} }
public int getMaxOutputFetched() {
return txdata.getMaxOutputFetched();
}
public boolean allOutputsFetched() { public boolean allOutputsFetched() {
return txdata.allOutputsFetched(); return txdata.allOutputsFetched();
} }