mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-04 21:36:45 +00:00
automatically increase gap limit if required by postmix handler
This commit is contained in:
parent
776fcb3044
commit
bad209ea5b
10 changed files with 110 additions and 92 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit 025af0575899d6e255cc0a1f875cad25ab4811b8
|
Subproject commit 61b2fd21e6f623850ad8b5df9f0cec4a0c0908cc
|
|
@ -2029,7 +2029,7 @@ public class AppController implements Initializable {
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void walletHistoryStarted(WalletHistoryStartedEvent event) {
|
public void walletHistoryStarted(WalletHistoryStartedEvent event) {
|
||||||
if(AppServices.isConnected() && getOpenWallets().containsKey(event.getWallet())) {
|
if(AppServices.isConnected() && getOpenWallets().containsKey(event.getWallet())) {
|
||||||
if(event.getWalletNode() == null && event.getWallet().getTransactions().isEmpty()) {
|
if(event.getWalletNodes() == null && event.getWallet().getTransactions().isEmpty()) {
|
||||||
statusUpdated(new StatusEvent(LOADING_TRANSACTIONS_MESSAGE, 120));
|
statusUpdated(new StatusEvent(LOADING_TRANSACTIONS_MESSAGE, 120));
|
||||||
if(statusTimeline == null || statusTimeline.getStatus() != Animation.Status.RUNNING) {
|
if(statusTimeline == null || statusTimeline.getStatus() != Animation.Status.RUNNING) {
|
||||||
statusBar.setProgress(-1);
|
statusBar.setProgress(-1);
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.sparrowwallet.sparrow.event;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is posted if the wallet's gap limit has changed, and triggers a history fetch for the new nodes.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class WalletGapLimitChangedEvent extends WalletChangedEvent {
|
||||||
|
public WalletGapLimitChangedEvent(Wallet wallet) {
|
||||||
|
super(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGapLimit() {
|
||||||
|
return getWallet().getGapLimit();
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,15 +3,17 @@ package com.sparrowwallet.sparrow.event;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.drongo.wallet.WalletNode;
|
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||||
|
|
||||||
public class WalletHistoryStartedEvent extends WalletHistoryStatusEvent {
|
import java.util.Set;
|
||||||
private final WalletNode walletNode;
|
|
||||||
|
|
||||||
public WalletHistoryStartedEvent(Wallet wallet, WalletNode walletNode) {
|
public class WalletHistoryStartedEvent extends WalletHistoryStatusEvent {
|
||||||
|
private final Set<WalletNode> walletNodes;
|
||||||
|
|
||||||
|
public WalletHistoryStartedEvent(Wallet wallet, Set<WalletNode> walletNodes) {
|
||||||
super(wallet, true);
|
super(wallet, true);
|
||||||
this.walletNode = walletNode;
|
this.walletNodes = walletNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WalletNode getWalletNode() {
|
public Set<WalletNode> getWalletNodes() {
|
||||||
return walletNode;
|
return walletNodes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,6 +240,10 @@ public class DbPersistence implements Persistence {
|
||||||
walletDao.updateStoredBlockHeight(wallet.getId(), dirtyPersistables.blockHeight);
|
walletDao.updateStoredBlockHeight(wallet.getId(), dirtyPersistables.blockHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(dirtyPersistables.gapLimit != null) {
|
||||||
|
walletDao.updateGapLimit(wallet.getId(), dirtyPersistables.gapLimit);
|
||||||
|
}
|
||||||
|
|
||||||
if(!dirtyPersistables.labelEntries.isEmpty()) {
|
if(!dirtyPersistables.labelEntries.isEmpty()) {
|
||||||
BlockTransactionDao blockTransactionDao = handle.attach(BlockTransactionDao.class);
|
BlockTransactionDao blockTransactionDao = handle.attach(BlockTransactionDao.class);
|
||||||
WalletNodeDao walletNodeDao = handle.attach(WalletNodeDao.class);
|
WalletNodeDao walletNodeDao = handle.attach(WalletNodeDao.class);
|
||||||
|
@ -644,6 +648,13 @@ public class DbPersistence implements Persistence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void walletGapLimitChanged(WalletGapLimitChangedEvent event) {
|
||||||
|
if(persistsFor(event.getWallet())) {
|
||||||
|
dirtyPersistablesMap.computeIfAbsent(event.getWallet(), key -> new DirtyPersistables()).gapLimit = event.getGapLimit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void walletEntryLabelsChanged(WalletEntryLabelsChangedEvent event) {
|
public void walletEntryLabelsChanged(WalletEntryLabelsChangedEvent event) {
|
||||||
if(persistsFor(event.getWallet())) {
|
if(persistsFor(event.getWallet())) {
|
||||||
|
@ -691,6 +702,7 @@ public class DbPersistence implements Persistence {
|
||||||
public boolean clearHistory;
|
public boolean clearHistory;
|
||||||
public final List<WalletNode> historyNodes = new ArrayList<>();
|
public final List<WalletNode> historyNodes = new ArrayList<>();
|
||||||
public Integer blockHeight = null;
|
public Integer blockHeight = null;
|
||||||
|
public Integer gapLimit = null;
|
||||||
public final List<Entry> labelEntries = new ArrayList<>();
|
public final List<Entry> labelEntries = new ArrayList<>();
|
||||||
public final List<BlockTransactionHashIndex> utxoStatuses = new ArrayList<>();
|
public final List<BlockTransactionHashIndex> utxoStatuses = new ArrayList<>();
|
||||||
public boolean mixConfig;
|
public boolean mixConfig;
|
||||||
|
@ -704,6 +716,7 @@ public class DbPersistence implements Persistence {
|
||||||
"\nClear history:" + clearHistory +
|
"\nClear history:" + clearHistory +
|
||||||
"\nNodes:" + historyNodes +
|
"\nNodes:" + historyNodes +
|
||||||
"\nBlockHeight:" + blockHeight +
|
"\nBlockHeight:" + blockHeight +
|
||||||
|
"\nGap limit:" + gapLimit +
|
||||||
"\nTx labels:" + labelEntries.stream().filter(entry -> entry instanceof TransactionEntry).map(entry -> ((TransactionEntry)entry).getBlockTransaction().getHash().toString()).collect(Collectors.toList()) +
|
"\nTx labels:" + labelEntries.stream().filter(entry -> entry instanceof TransactionEntry).map(entry -> ((TransactionEntry)entry).getBlockTransaction().getHash().toString()).collect(Collectors.toList()) +
|
||||||
"\nAddress labels:" + labelEntries.stream().filter(entry -> entry instanceof NodeEntry).map(entry -> ((NodeEntry)entry).getNode().toString() + " " + entry.getLabel()).collect(Collectors.toList()) +
|
"\nAddress labels:" + labelEntries.stream().filter(entry -> entry instanceof NodeEntry).map(entry -> ((NodeEntry)entry).getNode().toString() + " " + entry.getLabel()).collect(Collectors.toList()) +
|
||||||
"\nUTXO labels:" + labelEntries.stream().filter(entry -> entry instanceof HashIndexEntry).map(entry -> ((HashIndexEntry)entry).getHashIndex().toString()).collect(Collectors.toList()) +
|
"\nUTXO labels:" + labelEntries.stream().filter(entry -> entry instanceof HashIndexEntry).map(entry -> ((HashIndexEntry)entry).getHashIndex().toString()).collect(Collectors.toList()) +
|
||||||
|
|
|
@ -55,6 +55,9 @@ public interface WalletDao {
|
||||||
@SqlUpdate("update wallet set storedBlockHeight = :blockHeight where id = :id")
|
@SqlUpdate("update wallet set storedBlockHeight = :blockHeight where id = :id")
|
||||||
void updateStoredBlockHeight(@Bind("id") long id, @Bind("blockHeight") Integer blockHeight);
|
void updateStoredBlockHeight(@Bind("id") long id, @Bind("blockHeight") Integer blockHeight);
|
||||||
|
|
||||||
|
@SqlUpdate("update wallet set gapLimit = :gapLimit where id = :id")
|
||||||
|
void updateGapLimit(@Bind("id") long id, @Bind("gapLimit") Integer gapLimit);
|
||||||
|
|
||||||
@SqlUpdate("set schema ?")
|
@SqlUpdate("set schema ?")
|
||||||
int setSchema(String schema);
|
int setSchema(String schema);
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,6 @@ import com.github.arteam.simplejsonrpc.client.Transport;
|
||||||
import com.github.arteam.simplejsonrpc.client.builder.BatchRequestBuilder;
|
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.KeyDerivation;
|
|
||||||
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
|
||||||
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
|
@ -19,6 +17,8 @@ import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.sparrowwallet.drongo.wallet.WalletNode.nodeRangesToString;
|
||||||
|
|
||||||
public class BatchedElectrumServerRpc implements ElectrumServerRpc {
|
public class BatchedElectrumServerRpc implements ElectrumServerRpc {
|
||||||
private static final Logger log = LoggerFactory.getLogger(BatchedElectrumServerRpc.class);
|
private static final Logger log = LoggerFactory.getLogger(BatchedElectrumServerRpc.class);
|
||||||
private static final int MAX_RETRIES = 5;
|
private static final int MAX_RETRIES = 5;
|
||||||
|
@ -75,7 +75,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc {
|
||||||
public Map<String, ScriptHashTx[]> getScriptHashHistory(Transport transport, Wallet wallet, Map<String, String> pathScriptHashes, boolean failOnError) {
|
public Map<String, ScriptHashTx[]> getScriptHashHistory(Transport transport, Wallet wallet, 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(wallet, true, "Loading transactions for " + getScriptHashesAbbreviation(pathScriptHashes.keySet())));
|
EventManager.get().post(new WalletHistoryStatusEvent(wallet, true, "Loading transactions for " + nodeRangesToString(pathScriptHashes.keySet())));
|
||||||
|
|
||||||
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));
|
||||||
|
@ -85,7 +85,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc {
|
||||||
return new RetryLogic<Map<String, ScriptHashTx[]>>(MAX_RETRIES, RETRY_DELAY, List.of(IllegalStateException.class, IllegalArgumentException.class)).getResult(batchRequest::execute);
|
return new RetryLogic<Map<String, ScriptHashTx[]>>(MAX_RETRIES, RETRY_DELAY, List.of(IllegalStateException.class, IllegalArgumentException.class)).getResult(batchRequest::execute);
|
||||||
} catch (JsonRpcBatchException e) {
|
} catch (JsonRpcBatchException e) {
|
||||||
if(failOnError) {
|
if(failOnError) {
|
||||||
throw new ElectrumServerRpcException("Failed to retrieve transaction history for paths: " + getScriptHashesAbbreviation((Collection<String>)e.getErrors().keySet()), e);
|
throw new ElectrumServerRpcException("Failed to retrieve transaction history for paths: " + nodeRangesToString((Collection<String>)e.getErrors().keySet()), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, ScriptHashTx[]> result = (Map<String, ScriptHashTx[]>)e.getSuccesses();
|
Map<String, ScriptHashTx[]> result = (Map<String, ScriptHashTx[]>)e.getSuccesses();
|
||||||
|
@ -95,7 +95,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
throw new ElectrumServerRpcException("Failed to retrieve transaction history for paths: " + getScriptHashesAbbreviation(pathScriptHashes.keySet()), e);
|
throw new ElectrumServerRpcException("Failed to retrieve transaction history for paths: " + nodeRangesToString(pathScriptHashes.keySet()), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc {
|
||||||
return new RetryLogic<Map<String, ScriptHashTx[]>>(MAX_RETRIES, RETRY_DELAY, List.of(IllegalStateException.class, IllegalArgumentException.class)).getResult(batchRequest::execute);
|
return new RetryLogic<Map<String, ScriptHashTx[]>>(MAX_RETRIES, RETRY_DELAY, List.of(IllegalStateException.class, IllegalArgumentException.class)).getResult(batchRequest::execute);
|
||||||
} catch(JsonRpcBatchException e) {
|
} catch(JsonRpcBatchException e) {
|
||||||
if(failOnError) {
|
if(failOnError) {
|
||||||
throw new ElectrumServerRpcException("Failed to retrieve mempool transactions for paths: " + getScriptHashesAbbreviation((Collection<String>)e.getErrors().keySet()), e);
|
throw new ElectrumServerRpcException("Failed to retrieve mempool transactions for paths: " + nodeRangesToString((Collection<String>)e.getErrors().keySet()), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, ScriptHashTx[]> result = (Map<String, ScriptHashTx[]>)e.getSuccesses();
|
Map<String, ScriptHashTx[]> result = (Map<String, ScriptHashTx[]>)e.getSuccesses();
|
||||||
|
@ -123,7 +123,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
throw new ElectrumServerRpcException("Failed to retrieve mempool transactions for paths: " + getScriptHashesAbbreviation(pathScriptHashes.keySet()), e);
|
throw new ElectrumServerRpcException("Failed to retrieve mempool transactions for paths: " + nodeRangesToString(pathScriptHashes.keySet()), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc {
|
||||||
public Map<String, String> subscribeScriptHashes(Transport transport, Wallet wallet, Map<String, String> pathScriptHashes) {
|
public Map<String, String> subscribeScriptHashes(Transport transport, Wallet wallet, 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(wallet, true, "Finding transactions for " + getScriptHashesAbbreviation(pathScriptHashes.keySet())));
|
EventManager.get().post(new WalletHistoryStatusEvent(wallet, true, "Finding transactions for " + nodeRangesToString(pathScriptHashes.keySet())));
|
||||||
|
|
||||||
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));
|
||||||
|
@ -142,9 +142,9 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc {
|
||||||
return new RetryLogic<Map<String, String>>(MAX_RETRIES, RETRY_DELAY, List.of(IllegalStateException.class, IllegalArgumentException.class)).getResult(batchRequest::execute);
|
return new RetryLogic<Map<String, String>>(MAX_RETRIES, RETRY_DELAY, List.of(IllegalStateException.class, IllegalArgumentException.class)).getResult(batchRequest::execute);
|
||||||
} catch(JsonRpcBatchException e) {
|
} catch(JsonRpcBatchException e) {
|
||||||
//Even if we have some successes, failure to subscribe for all script hashes will result in outdated wallet view. Don't proceed.
|
//Even if we have some successes, failure to subscribe for all script hashes will result in outdated wallet view. Don't proceed.
|
||||||
throw new ElectrumServerRpcException("Failed to subscribe to paths: " + getScriptHashesAbbreviation((Collection<String>)e.getErrors().keySet()), e);
|
throw new ElectrumServerRpcException("Failed to subscribe to paths: " + nodeRangesToString((Collection<String>)e.getErrors().keySet()), e);
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
throw new ElectrumServerRpcException("Failed to subscribe to paths: " + getScriptHashesAbbreviation(pathScriptHashes.keySet()), e);
|
throw new ElectrumServerRpcException("Failed to subscribe to paths: " + nodeRangesToString(pathScriptHashes.keySet()), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,65 +276,4 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc {
|
||||||
throw new ElectrumServerRpcException("Error broadcasting transaction", e);
|
throw new ElectrumServerRpcException("Error broadcasting transaction", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getScriptHashesAbbreviation(Collection<String> scriptHashes) {
|
|
||||||
List<String> sortedHashes = new ArrayList<>(scriptHashes);
|
|
||||||
|
|
||||||
if(scriptHashes.isEmpty()) {
|
|
||||||
return "[]";
|
|
||||||
}
|
|
||||||
|
|
||||||
List<List<String>> contiguous = splitToContiguous(sortedHashes);
|
|
||||||
|
|
||||||
String abbrev = "[";
|
|
||||||
for(Iterator<List<String>> iter = contiguous.iterator(); iter.hasNext(); ) {
|
|
||||||
List<String> range = iter.next();
|
|
||||||
abbrev += range.get(0);
|
|
||||||
if(range.size() > 1) {
|
|
||||||
abbrev += "-" + range.get(range.size() - 1);
|
|
||||||
}
|
|
||||||
if(iter.hasNext()) {
|
|
||||||
abbrev += ", ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
abbrev += "]";
|
|
||||||
|
|
||||||
return abbrev;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<List<String>> splitToContiguous(List<String> input) {
|
|
||||||
List<List<String>> result = new ArrayList<>();
|
|
||||||
int prev = 0;
|
|
||||||
|
|
||||||
int keyPurpose = getKeyPurpose(input.get(0));
|
|
||||||
int index = getIndex(input.get(0));
|
|
||||||
|
|
||||||
for (int cur = 0; cur < input.size(); cur++) {
|
|
||||||
if(getKeyPurpose(input.get(cur)) != keyPurpose || getIndex(input.get(cur)) != index) {
|
|
||||||
result.add(input.subList(prev, cur));
|
|
||||||
prev = cur;
|
|
||||||
}
|
|
||||||
index = getIndex(input.get(cur)) + 1;
|
|
||||||
keyPurpose = getKeyPurpose(input.get(cur));
|
|
||||||
}
|
|
||||||
result.add(input.subList(prev, input.size()));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getKeyPurpose(String path) {
|
|
||||||
List<ChildNumber> childNumbers = KeyDerivation.parsePath(path);
|
|
||||||
if(childNumbers.isEmpty()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return childNumbers.get(0).num();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getIndex(String path) {
|
|
||||||
List<ChildNumber> childNumbers = KeyDerivation.parsePath(path);
|
|
||||||
if(childNumbers.isEmpty()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return childNumbers.get(childNumbers.size() - 1).num();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.sparrowwallet.drongo.wallet.WalletNode.nodeRangesToString;
|
||||||
|
|
||||||
public class WalletForm {
|
public class WalletForm {
|
||||||
private static final Logger log = LoggerFactory.getLogger(WalletForm.class);
|
private static final Logger log = LoggerFactory.getLogger(WalletForm.class);
|
||||||
|
|
||||||
|
@ -123,11 +125,14 @@ public class WalletForm {
|
||||||
refreshHistory(blockHeight, pastWallet, null);
|
refreshHistory(blockHeight, pastWallet, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshHistory(Integer blockHeight, Wallet pastWallet, WalletNode node) {
|
public void refreshHistory(Integer blockHeight, Wallet pastWallet, Set<WalletNode> nodes) {
|
||||||
Wallet previousWallet = wallet.copy();
|
Wallet previousWallet = wallet.copy();
|
||||||
if(wallet.isValid() && AppServices.isConnected()) {
|
if(wallet.isValid() && AppServices.isConnected()) {
|
||||||
log.debug(node == null ? wallet.getFullName() + " refreshing full wallet history" : wallet.getFullName() + " requesting node wallet history for " + node.getDerivationPath());
|
if(log.isDebugEnabled()) {
|
||||||
ElectrumServer.TransactionHistoryService historyService = new ElectrumServer.TransactionHistoryService(wallet, getWalletTransactionNodes(node));
|
log.debug(nodes == null ? wallet.getFullName() + " refreshing full wallet history" : wallet.getFullName() + " requesting node wallet history for " + nodeRangesToString(nodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
ElectrumServer.TransactionHistoryService historyService = new ElectrumServer.TransactionHistoryService(wallet, getWalletTransactionNodes(nodes));
|
||||||
historyService.setOnSucceeded(workerStateEvent -> {
|
historyService.setOnSucceeded(workerStateEvent -> {
|
||||||
if(historyService.getValue()) {
|
if(historyService.getValue()) {
|
||||||
EventManager.get().post(new WalletHistoryFinishedEvent(wallet));
|
EventManager.get().post(new WalletHistoryFinishedEvent(wallet));
|
||||||
|
@ -144,7 +149,7 @@ public class WalletForm {
|
||||||
EventManager.get().post(new WalletHistoryFailedEvent(wallet, workerStateEvent.getSource().getException()));
|
EventManager.get().post(new WalletHistoryFailedEvent(wallet, workerStateEvent.getSource().getException()));
|
||||||
});
|
});
|
||||||
|
|
||||||
EventManager.get().post(new WalletHistoryStartedEvent(wallet, node));
|
EventManager.get().post(new WalletHistoryStartedEvent(wallet, nodes));
|
||||||
historyService.start();
|
historyService.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,19 +254,21 @@ public class WalletForm {
|
||||||
walletTransactionNodes.add(transactionNodes);
|
walletTransactionNodes.add(transactionNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<WalletNode> getWalletTransactionNodes(WalletNode walletNode) {
|
private Set<WalletNode> getWalletTransactionNodes(Set<WalletNode> walletNodes) {
|
||||||
if(walletNode == null) {
|
if(walletNodes == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<WalletNode> allNodes = new LinkedHashSet<>();
|
Set<WalletNode> allNodes = new LinkedHashSet<>();
|
||||||
|
for(WalletNode walletNode : walletNodes) {
|
||||||
for(Set<WalletNode> nodes : walletTransactionNodes) {
|
for(Set<WalletNode> nodes : walletTransactionNodes) {
|
||||||
if(nodes.contains(walletNode)) {
|
if(nodes.contains(walletNode)) {
|
||||||
allNodes.addAll(nodes);
|
allNodes.addAll(nodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return allNodes.isEmpty() ? Set.of(walletNode) : allNodes;
|
return allNodes.isEmpty() ? walletNodes : allNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NodeEntry getNodeEntry(KeyPurpose keyPurpose) {
|
public NodeEntry getNodeEntry(KeyPurpose keyPurpose) {
|
||||||
|
@ -392,7 +399,7 @@ public class WalletForm {
|
||||||
WalletNode walletNode = event.getWalletNode(wallet);
|
WalletNode walletNode = event.getWalletNode(wallet);
|
||||||
if(walletNode != null) {
|
if(walletNode != null) {
|
||||||
log.debug(wallet.getFullName() + " history event for node " + walletNode + " (" + event.getScriptHash() + ")");
|
log.debug(wallet.getFullName() + " history event for node " + walletNode + " (" + event.getScriptHash() + ")");
|
||||||
refreshHistory(AppServices.getCurrentBlockHeight(), null, walletNode);
|
refreshHistory(AppServices.getCurrentBlockHeight(), null, Set.of(walletNode));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -503,6 +510,26 @@ public class WalletForm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void walletGapLimitChanged(WalletGapLimitChangedEvent event) {
|
||||||
|
if(event.getWallet() == wallet) {
|
||||||
|
Platform.runLater(() -> EventManager.get().post(new WalletDataChangedEvent(wallet)));
|
||||||
|
|
||||||
|
Set<WalletNode> newNodes = new LinkedHashSet<>();
|
||||||
|
for(KeyPurpose keyPurpose : KeyPurpose.DEFAULT_PURPOSES) {
|
||||||
|
Optional<WalletNode> optPurposeNode = wallet.getPurposeNodes().stream().filter(node -> node.getKeyPurpose() == keyPurpose).findFirst();
|
||||||
|
if(optPurposeNode.isPresent()) {
|
||||||
|
WalletNode purposeNode = optPurposeNode.get();
|
||||||
|
newNodes.addAll(purposeNode.fillToIndex(wallet.getLookAheadIndex(purposeNode)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!newNodes.isEmpty()) {
|
||||||
|
Platform.runLater(() -> refreshHistory(AppServices.getCurrentBlockHeight(), null, newNodes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void whirlpoolMixSuccess(WhirlpoolMixSuccessEvent event) {
|
public void whirlpoolMixSuccess(WhirlpoolMixSuccessEvent event) {
|
||||||
if(event.getWallet() == wallet && event.getWalletNode() != null) {
|
if(event.getWallet() == wallet && event.getWalletNode() != null) {
|
||||||
|
|
|
@ -294,9 +294,15 @@ public class Whirlpool {
|
||||||
|
|
||||||
public static Wallet getStandardAccountWallet(WhirlpoolAccount whirlpoolAccount, Wallet wallet) {
|
public static Wallet getStandardAccountWallet(WhirlpoolAccount whirlpoolAccount, Wallet wallet) {
|
||||||
StandardAccount standardAccount = getStandardAccount(whirlpoolAccount);
|
StandardAccount standardAccount = getStandardAccount(whirlpoolAccount);
|
||||||
if(StandardAccount.WHIRLPOOL_ACCOUNTS.contains(standardAccount)) {
|
if(StandardAccount.WHIRLPOOL_ACCOUNTS.contains(standardAccount) || wallet.getStandardAccountType() != standardAccount) {
|
||||||
wallet = wallet.getChildWallet(standardAccount);
|
Wallet standardWallet = wallet.getChildWallet(standardAccount);
|
||||||
|
if(standardWallet == null) {
|
||||||
|
throw new IllegalStateException("Cannot find " + standardAccount + " wallet");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return standardWallet;
|
||||||
|
}
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import com.sparrowwallet.drongo.KeyPurpose;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
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.event.WalletGapLimitChangedEvent;
|
||||||
import com.sparrowwallet.sparrow.event.WalletMixConfigChangedEvent;
|
import com.sparrowwallet.sparrow.event.WalletMixConfigChangedEvent;
|
||||||
|
|
||||||
public class SparrowIndexHandler extends AbstractIndexHandler {
|
public class SparrowIndexHandler extends AbstractIndexHandler {
|
||||||
|
@ -37,6 +38,7 @@ public class SparrowIndexHandler extends AbstractIndexHandler {
|
||||||
@Override
|
@Override
|
||||||
public synchronized void set(int value) {
|
public synchronized void set(int value) {
|
||||||
setStoredIndex(value);
|
setStoredIndex(value);
|
||||||
|
ensureSufficientGapLimit(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getCurrentIndex() {
|
private int getCurrentIndex() {
|
||||||
|
@ -67,4 +69,13 @@ public class SparrowIndexHandler extends AbstractIndexHandler {
|
||||||
EventManager.get().post(new WalletMixConfigChangedEvent(wallet));
|
EventManager.get().post(new WalletMixConfigChangedEvent(wallet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensureSufficientGapLimit(int index) {
|
||||||
|
int highestUsedIndex = getCurrentIndex() - 1;
|
||||||
|
int existingGapLimit = wallet.getGapLimit();
|
||||||
|
if(index > highestUsedIndex + existingGapLimit) {
|
||||||
|
wallet.setGapLimit(Math.max(wallet.getGapLimit(), index - highestUsedIndex));
|
||||||
|
EventManager.get().post(new WalletGapLimitChangedEvent(wallet));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue