mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-04 21:36:45 +00:00
fix bugs with txes with many outputs
This commit is contained in:
parent
0cd97649cc
commit
800512ee5b
5 changed files with 32 additions and 17 deletions
|
@ -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,12 +959,20 @@ 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) {
|
||||||
|
if(blockTransactions.get(i) == null) {
|
||||||
blockTransactions.set(i, UNFETCHABLE_BLOCK_TRANSACTION);
|
blockTransactions.set(i, UNFETCHABLE_BLOCK_TRANSACTION);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
BlockTransaction blockTransaction = transactionMap.get(reference.getHash());
|
BlockTransaction blockTransaction = transactionMap.get(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()) {
|
for(TransactionInput input : blockTransaction.getTransaction().getInputs()) {
|
||||||
if(input.getOutpoint().getHash().equals(transaction.getTxId()) && input.getOutpoint().getIndex() == i) {
|
if(input.getOutpoint().getHash().equals(transaction.getTxId()) && input.getOutpoint().getIndex() == i) {
|
||||||
if(blockTransactions.set(i, blockTransaction) != null) {
|
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());
|
throw new IllegalStateException("Double spend detected for output #" + i + " on hash " + reference.getHash());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -971,6 +981,7 @@ public class ElectrumServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return blockTransactions;
|
return blockTransactions;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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"));
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue