show wallet loading progress

This commit is contained in:
Craig Raw 2020-09-18 10:50:41 +02:00
parent 4bad46c9e3
commit b7aa73e99f
9 changed files with 92 additions and 6 deletions

View file

@ -2,8 +2,11 @@ package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.BitcoinUnit; import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.event.WalletHistoryStatusEvent;
import com.sparrowwallet.sparrow.io.Config; import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.wallet.Entry; import com.sparrowwallet.sparrow.wallet.Entry;
import javafx.application.Platform;
import javafx.scene.control.Label;
import javafx.scene.control.TreeTableView; import javafx.scene.control.TreeTableView;
public class CoinTreeTable extends TreeTableView<Entry> { public class CoinTreeTable extends TreeTableView<Entry> {
@ -33,4 +36,20 @@ public class CoinTreeTable extends TreeTableView<Entry> {
refresh(); refresh();
} }
} }
public void updateHistoryStatus(WalletHistoryStatusEvent event) {
Platform.runLater(() -> {
if(event.getErrorMessage() != null) {
setPlaceholder(new Label("Error loading transactions: " + event.getErrorMessage()));
} else if(event.isLoading()) {
if(event.getStatusMessage() != null) {
setPlaceholder(new Label(event.getStatusMessage() + "..."));
} else {
setPlaceholder(new Label("Loading transactions..."));
}
} else {
setPlaceholder(new Label("No transactions"));
}
});
}
} }

View file

@ -0,0 +1,41 @@
package com.sparrowwallet.sparrow.event;
public class WalletHistoryStatusEvent {
private final boolean loaded;
private final String statusMessage;
private final String errorMessage;
public WalletHistoryStatusEvent(boolean loaded) {
this.loaded = loaded;
this.statusMessage = null;
this.errorMessage = null;
}
public WalletHistoryStatusEvent(boolean loaded, String statusMessage) {
this.loaded = false;
this.statusMessage = statusMessage;
this.errorMessage = null;
}
public WalletHistoryStatusEvent(String errorMessage) {
this.loaded = false;
this.statusMessage = null;
this.errorMessage = errorMessage;
}
public boolean isLoading() {
return !loaded;
}
public boolean isLoaded() {
return loaded;
}
public String getStatusMessage() {
return statusMessage;
}
public String getErrorMessage() {
return errorMessage;
}
}

View file

@ -6,6 +6,8 @@ import com.github.arteam.simplejsonrpc.client.builder.BatchRequestBuilder;
import com.github.arteam.simplejsonrpc.client.exception.JsonRpcBatchException; import com.github.arteam.simplejsonrpc.client.exception.JsonRpcBatchException;
import com.github.arteam.simplejsonrpc.client.exception.JsonRpcException; import com.github.arteam.simplejsonrpc.client.exception.JsonRpcException;
import com.sparrowwallet.drongo.protocol.Sha256Hash; import com.sparrowwallet.drongo.protocol.Sha256Hash;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.WalletHistoryStatusEvent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -61,6 +63,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc {
public Map<String, ScriptHashTx[]> getScriptHashHistory(Transport transport, Map<String, String> pathScriptHashes, boolean failOnError) { public Map<String, ScriptHashTx[]> getScriptHashHistory(Transport transport, Map<String, String> pathScriptHashes, boolean failOnError) {
JsonRpcClient client = new JsonRpcClient(transport); JsonRpcClient client = new JsonRpcClient(transport);
BatchRequestBuilder<String, ScriptHashTx[]> batchRequest = client.createBatchRequest().keysType(String.class).returnType(ScriptHashTx[].class); BatchRequestBuilder<String, ScriptHashTx[]> batchRequest = client.createBatchRequest().keysType(String.class).returnType(ScriptHashTx[].class);
EventManager.get().post(new WalletHistoryStatusEvent(false, "Loading transactions"));
for(String path : pathScriptHashes.keySet()) { for(String path : pathScriptHashes.keySet()) {
batchRequest.add(path, "blockchain.scripthash.get_history", pathScriptHashes.get(path)); batchRequest.add(path, "blockchain.scripthash.get_history", pathScriptHashes.get(path));
@ -112,6 +115,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc {
public Map<String, String> subscribeScriptHashes(Transport transport, Map<String, String> pathScriptHashes) { public Map<String, String> subscribeScriptHashes(Transport transport, Map<String, String> pathScriptHashes) {
JsonRpcClient client = new JsonRpcClient(transport); JsonRpcClient client = new JsonRpcClient(transport);
BatchRequestBuilder<String, String> batchRequest = client.createBatchRequest().keysType(String.class).returnType(String.class); BatchRequestBuilder<String, String> batchRequest = client.createBatchRequest().keysType(String.class).returnType(String.class);
EventManager.get().post(new WalletHistoryStatusEvent(false, "Finding transactions"));
for(String path : pathScriptHashes.keySet()) { for(String path : pathScriptHashes.keySet()) {
batchRequest.add(path, "blockchain.scripthash.subscribe", pathScriptHashes.get(path)); batchRequest.add(path, "blockchain.scripthash.subscribe", pathScriptHashes.get(path));
@ -130,6 +134,8 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc {
public Map<Integer, String> getBlockHeaders(Transport transport, Set<Integer> blockHeights) { public Map<Integer, String> getBlockHeaders(Transport transport, Set<Integer> blockHeights) {
JsonRpcClient client = new JsonRpcClient(transport); JsonRpcClient client = new JsonRpcClient(transport);
BatchRequestBuilder<Integer, String> batchRequest = client.createBatchRequest().keysType(Integer.class).returnType(String.class); BatchRequestBuilder<Integer, String> batchRequest = client.createBatchRequest().keysType(Integer.class).returnType(String.class);
EventManager.get().post(new WalletHistoryStatusEvent(false, "Retrieving blocks"));
for(Integer height : blockHeights) { for(Integer height : blockHeights) {
batchRequest.add(height, "blockchain.block.header", height); batchRequest.add(height, "blockchain.block.header", height);
} }
@ -146,6 +152,8 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc {
public Map<String, String> getTransactions(Transport transport, Set<String> txids) { public Map<String, String> getTransactions(Transport transport, Set<String> txids) {
JsonRpcClient client = new JsonRpcClient(transport); JsonRpcClient client = new JsonRpcClient(transport);
BatchRequestBuilder<String, String> batchRequest = client.createBatchRequest().keysType(String.class).returnType(String.class); BatchRequestBuilder<String, String> batchRequest = client.createBatchRequest().keysType(String.class).returnType(String.class);
EventManager.get().post(new WalletHistoryStatusEvent(false, "Retrieving transactions"));
for(String txid : txids) { for(String txid : txids) {
batchRequest.add(txid, "blockchain.transaction.get", txid); batchRequest.add(txid, "blockchain.transaction.get", txid);
} }

View file

@ -180,6 +180,10 @@ public class ElectrumServer {
pathScriptHashes.put(node.getDerivationPath(), getScriptHash(wallet, node)); pathScriptHashes.put(node.getDerivationPath(), getScriptHash(wallet, node));
} }
if(pathScriptHashes.isEmpty()) {
return;
}
//Even if we have some successes, failure to retrieve all references will result in an incomplete wallet history. Don't proceed if that's the case. //Even if we have some successes, failure to retrieve all references will result in an incomplete wallet history. Don't proceed if that's the case.
Map<String, ScriptHashTx[]> result = electrumServerRpc.getScriptHashHistory(getTransport(), pathScriptHashes, true); Map<String, ScriptHashTx[]> result = electrumServerRpc.getScriptHashHistory(getTransport(), pathScriptHashes, true);

View file

@ -7,13 +7,13 @@ import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.protocol.Sha256Hash; import com.sparrowwallet.drongo.protocol.Sha256Hash;
import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.sparrow.AppController; import com.sparrowwallet.sparrow.AppController;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.WalletHistoryStatusEvent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.*; import java.util.*;
import static com.sparrowwallet.drongo.protocol.Transaction.DUST_RELAY_TX_FEE;
public class SimpleElectrumServerRpc implements ElectrumServerRpc { public class SimpleElectrumServerRpc implements ElectrumServerRpc {
private static final Logger log = LoggerFactory.getLogger(SimpleElectrumServerRpc.class); private static final Logger log = LoggerFactory.getLogger(SimpleElectrumServerRpc.class);
private static final int MAX_TARGET_BLOCKS = 25; private static final int MAX_TARGET_BLOCKS = 25;
@ -65,6 +65,7 @@ public class SimpleElectrumServerRpc implements ElectrumServerRpc {
Map<String, ScriptHashTx[]> result = new LinkedHashMap<>(); Map<String, ScriptHashTx[]> result = new LinkedHashMap<>();
for(String path : pathScriptHashes.keySet()) { for(String path : pathScriptHashes.keySet()) {
EventManager.get().post(new WalletHistoryStatusEvent(false, "Loading transactions for " + path));
try { try {
ScriptHashTx[] scriptHashTxes = client.createRequest().returnAs(ScriptHashTx[].class).method("blockchain.scripthash.get_history").id(path).params(pathScriptHashes.get(path)).execute(); ScriptHashTx[] scriptHashTxes = client.createRequest().returnAs(ScriptHashTx[].class).method("blockchain.scripthash.get_history").id(path).params(pathScriptHashes.get(path)).execute();
result.put(path, scriptHashTxes); result.put(path, scriptHashTxes);
@ -107,6 +108,7 @@ public class SimpleElectrumServerRpc implements ElectrumServerRpc {
Map<String, String> result = new LinkedHashMap<>(); Map<String, String> result = new LinkedHashMap<>();
for(String path : pathScriptHashes.keySet()) { for(String path : pathScriptHashes.keySet()) {
EventManager.get().post(new WalletHistoryStatusEvent(false, "Finding transactions for " + path));
try { try {
String scriptHash = client.createRequest().returnAs(String.class).method("blockchain.scripthash.subscribe").id(path).params(pathScriptHashes.get(path)).executeNullable(); String scriptHash = client.createRequest().returnAs(String.class).method("blockchain.scripthash.subscribe").id(path).params(pathScriptHashes.get(path)).executeNullable();
result.put(path, scriptHash); result.put(path, scriptHash);
@ -125,6 +127,7 @@ public class SimpleElectrumServerRpc implements ElectrumServerRpc {
Map<Integer, String> result = new LinkedHashMap<>(); Map<Integer, String> result = new LinkedHashMap<>();
for(Integer blockHeight : blockHeights) { for(Integer blockHeight : blockHeights) {
EventManager.get().post(new WalletHistoryStatusEvent(false, "Retrieving block at height " + blockHeight));
try { try {
String blockHeader = client.createRequest().returnAs(String.class).method("blockchain.block.header").id(blockHeight).params(blockHeight).execute(); String blockHeader = client.createRequest().returnAs(String.class).method("blockchain.block.header").id(blockHeight).params(blockHeight).execute();
result.put(blockHeight, blockHeader); result.put(blockHeight, blockHeader);
@ -144,6 +147,7 @@ public class SimpleElectrumServerRpc implements ElectrumServerRpc {
Map<String, String> result = new LinkedHashMap<>(); Map<String, String> result = new LinkedHashMap<>();
for(String txid : txids) { for(String txid : txids) {
EventManager.get().post(new WalletHistoryStatusEvent(false, "Retrieving transaction [" + txid.substring(0, 6) + "]"));
try { try {
String rawTxHex = client.createRequest().returnAs(String.class).method("blockchain.transaction.get").id(txid).params(txid).execute(); String rawTxHex = client.createRequest().returnAs(String.class).method("blockchain.transaction.get").id(txid).params(txid).execute();
result.put(txid, rawTxHex); result.put(txid, rawTxHex);

View file

@ -5,10 +5,7 @@ import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.wallet.WalletNode; import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.AddressTreeTable; import com.sparrowwallet.sparrow.control.AddressTreeTable;
import com.sparrowwallet.sparrow.event.BitcoinUnitChangedEvent; import com.sparrowwallet.sparrow.event.*;
import com.sparrowwallet.sparrow.event.WalletEntryLabelChangedEvent;
import com.sparrowwallet.sparrow.event.WalletHistoryChangedEvent;
import com.sparrowwallet.sparrow.event.WalletNodesChangedEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;

View file

@ -122,4 +122,9 @@ public class TransactionsController extends WalletFormController implements Init
public void exchangeRatesUpdated(ExchangeRatesUpdatedEvent event) { public void exchangeRatesUpdated(ExchangeRatesUpdatedEvent event) {
setFiatBalance(event.getCurrencyRate(), getWalletForm().getWalletTransactionsEntry().getBalance()); setFiatBalance(event.getCurrencyRate(), getWalletForm().getWalletTransactionsEntry().getBalance());
} }
@Subscribe
public void walletHistoryStatus(WalletHistoryStatusEvent event) {
transactionsTable.updateHistoryStatus(event);
}
} }

View file

@ -125,4 +125,9 @@ public class UtxosController extends WalletFormController implements Initializab
utxosChart.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit()); utxosChart.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit());
updateSendSelected(event.getBitcoinUnit()); updateSendSelected(event.getBitcoinUnit());
} }
@Subscribe
public void walletHistoryStatus(WalletHistoryStatusEvent event) {
utxosTable.updateHistoryStatus(event);
}
} }

View file

@ -74,11 +74,14 @@ public class WalletForm {
log.debug(node == null ? "Refreshing full wallet history" : "Requesting node wallet history for " + node.getDerivationPath()); log.debug(node == null ? "Refreshing full wallet history" : "Requesting node wallet history for " + node.getDerivationPath());
ElectrumServer.TransactionHistoryService historyService = new ElectrumServer.TransactionHistoryService(wallet, node); ElectrumServer.TransactionHistoryService historyService = new ElectrumServer.TransactionHistoryService(wallet, node);
historyService.setOnSucceeded(workerStateEvent -> { historyService.setOnSucceeded(workerStateEvent -> {
EventManager.get().post(new WalletHistoryStatusEvent(true));
updateWallet(previousWallet, blockHeight); updateWallet(previousWallet, blockHeight);
}); });
historyService.setOnFailed(workerStateEvent -> { historyService.setOnFailed(workerStateEvent -> {
log.error("Error retrieving wallet history", workerStateEvent.getSource().getException()); log.error("Error retrieving wallet history", workerStateEvent.getSource().getException());
EventManager.get().post(new WalletHistoryStatusEvent(workerStateEvent.getSource().getException().getMessage()));
}); });
EventManager.get().post(new WalletHistoryStatusEvent(false));
historyService.start(); historyService.start();
} }
} }