diff --git a/build.gradle b/build.gradle index 15bde121..49da4d7f 100644 --- a/build.gradle +++ b/build.gradle @@ -124,8 +124,8 @@ dependencies { exclude group: 'org.slf4j' } implementation('com.sparrowwallet.bokmakierie:bokmakierie:1.0') - implementation('io.samourai.code.whirlpool:whirlpool-client:1.0.0-beta10') - implementation('io.samourai.code.wallet:java-http-client:2.0.0-beta3') + implementation('io.samourai.code.whirlpool:whirlpool-client:1.0.0-beta13') + implementation('io.samourai.code.wallet:java-http-client:2.0.0-beta4') implementation('io.reactivex.rxjava2:rxjava:2.2.15') implementation('io.reactivex.rxjava2:rxjavafx:2.2.2') implementation('org.apache.commons:commons-lang3:3.7') @@ -490,7 +490,6 @@ extraJavaModuleInfo { exports('co.nstant.in.cbor.model') exports('co.nstant.in.cbor.builder') } - // begin samourai dependencies module('commons-codec-1.10.jar', 'commons.codec', '1.10') { exports('org.apache.commons.codec') } @@ -507,7 +506,6 @@ extraJavaModuleInfo { exports('com.lambdaworks.codec') exports('com.lambdaworks.crypto') } - // end samourai dependencies module('okio-1.6.0.jar', 'com.squareup.okio', '1.6.0') { exports('okio') } diff --git a/drongo b/drongo index c8165e15..3b8435ca 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit c8165e154a4088262cdf9428f8f8a6ef95db5140 +Subproject commit 3b8435ca37d00d370d859fa9dbc1631d3cdcae45 diff --git a/src/main/java/com/sparrowwallet/sparrow/control/MixStatusCell.java b/src/main/java/com/sparrowwallet/sparrow/control/MixStatusCell.java index 68dd71b8..b646f80f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/MixStatusCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/MixStatusCell.java @@ -70,7 +70,7 @@ public class MixStatusCell extends TreeTableCell { } private void setMixFail(MixFailReason mixFailReason, String mixError, Long mixErrorTimestamp) { - if(mixFailReason != MixFailReason.CANCEL) { + if(mixFailReason != MixFailReason.STOP_UTXO && !mixFailReason.isSilent()) { long elapsed = mixErrorTimestamp == null ? 0L : System.currentTimeMillis() - mixErrorTimestamp; if(elapsed >= ERROR_DISPLAY_MILLIS) { //Old error, don't set again. @@ -116,24 +116,10 @@ public class MixStatusCell extends TreeTableCell { progressIndicator.setProgress(mixProgress.getMixStep().getProgressPercent() == 100 ? -1 : mixProgress.getMixStep().getProgressPercent() / 100.0); setGraphic(progressIndicator); Tooltip tt = new Tooltip(); - String status = mixProgress.getMixStep().getMessage().substring(0, 1).toUpperCase(Locale.ROOT) + mixProgress.getMixStep().getMessage().substring(1); + String status = mixProgress.getMixStep().getMessage().replaceAll("_", " "); + status = status.substring(0, 1).toUpperCase(Locale.ROOT) + status.substring(1).toLowerCase(Locale.ROOT); tt.setText(status); setTooltip(tt); - - // TODO nbRegisteredInputs is not available anymore - /* - if(mixProgress.getMixStep() == MixStep.REGISTERED_INPUT) { - tt.setOnShowing(event -> { - Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(utxoEntry.getWallet()); - Whirlpool.RegisteredInputsService registeredInputsService = new Whirlpool.RegisteredInputsService(whirlpool, mixProgress.getPoolId()); - registeredInputsService.setOnSucceeded(eventStateHandler -> { - if(registeredInputsService.getValue() != null) { - tt.setText(status + " (1 of " + registeredInputsService.getValue() + ")"); - } - }); - registeredInputsService.start(); - }); - }*/ } else { setGraphic(null); setTooltip(null); diff --git a/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java b/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java index 7c3da094..8b214ee8 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java @@ -256,7 +256,10 @@ public class DbPersistence implements Persistence { } for(Sha256Hash txid : referencedTxIds) { BlockTransaction blkTx = wallet.getTransactions().get(txid); - blockTransactionDao.addOrUpdate(wallet, txid, blkTx); + //May be null for a nested wallet if still updating + if(blkTx != null) { + blockTransactionDao.addOrUpdate(wallet, txid, blkTx); + } } if(!dirtyPersistables.clearHistory) { DetachedLabelDao detachedLabelDao = handle.attach(DetachedLabelDao.class); diff --git a/src/main/java/com/sparrowwallet/sparrow/net/BroadcastSource.java b/src/main/java/com/sparrowwallet/sparrow/net/BroadcastSource.java index c2e4695f..d338736f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/BroadcastSource.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/BroadcastSource.java @@ -1,7 +1,7 @@ package com.sparrowwallet.sparrow.net; import com.google.common.net.HostAndPort; -import com.samourai.wallet.api.backend.beans.HttpException; +import com.samourai.wallet.httpClient.HttpResponseException; import com.sparrowwallet.drongo.Network; import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.protocol.Sha256Hash; @@ -158,7 +158,7 @@ public enum BroadcastSource { } catch(Exception e) { throw new BroadcastException("Could not retrieve txid from broadcast, server returned: " + response); } - } catch(HttpException e) { + } catch(HttpResponseException e) { throw new BroadcastException("Could not broadcast transaction, server returned " + e.getStatusCode() + ": " + e.getResponseBody()); } catch(Exception e) { log.error("Could not post transaction via " + getName(), e); diff --git a/src/main/java/com/sparrowwallet/sparrow/net/ExchangeSource.java b/src/main/java/com/sparrowwallet/sparrow/net/ExchangeSource.java index ee0dd064..8a62dd2f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/ExchangeSource.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/ExchangeSource.java @@ -1,6 +1,6 @@ package com.sparrowwallet.sparrow.net; -import com.samourai.wallet.api.backend.beans.HttpException; +import com.samourai.wallet.httpClient.HttpResponseException; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.event.ExchangeRatesUpdatedEvent; import javafx.concurrent.ScheduledService; @@ -107,7 +107,7 @@ public enum ExchangeSource { if(log.isDebugEnabled()) { log.warn("Error retrieving historical currency rates", e); } else { - if(e instanceof HttpException httpException && httpException.getStatusCode() == 404) { + if(e instanceof HttpResponseException httpException && httpException.getStatusCode() == 404) { log.warn("Error retrieving historical currency rates (" + e.getMessage() + "). BTC-" + currency.getCurrencyCode() + " may not be supported by " + this); } else { log.warn("Error retrieving historical currency rates (" + e.getMessage() + ")"); diff --git a/src/main/java/com/sparrowwallet/sparrow/net/HttpClientService.java b/src/main/java/com/sparrowwallet/sparrow/net/HttpClientService.java index 178e13f5..0eb37ef6 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/HttpClientService.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/HttpClientService.java @@ -5,6 +5,7 @@ import com.samourai.http.client.JettyHttpClientService; import com.samourai.wallet.httpClient.HttpUsage; import com.samourai.wallet.httpClient.IHttpClient; import com.samourai.wallet.util.AsyncUtil; +import com.samourai.wallet.util.ThreadUtil; import com.samourai.whirlpool.client.utils.ClientUtils; import io.reactivex.Observable; import javafx.concurrent.Service; @@ -17,25 +18,20 @@ public class HttpClientService extends JettyHttpClientService { private static final int REQUEST_TIMEOUT = 120000; public HttpClientService(HostAndPort torProxy) { - super(REQUEST_TIMEOUT, - ClientUtils.USER_AGENT, - new HttpProxySupplier(torProxy)); + super(REQUEST_TIMEOUT, ClientUtils.USER_AGENT, new HttpProxySupplier(torProxy)); } public T requestJson(String url, Class responseType, Map headers) throws Exception { - return getHttpClient(HttpUsage.BACKEND) - .getJson(url, responseType, headers); + return getHttpClient(HttpUsage.BACKEND).getJson(url, responseType, headers); } public Observable> postJson(String url, Class responseType, Map headers, Object body) { - return getHttpClient(HttpUsage.BACKEND) - .postJson(url, responseType, headers, body).toObservable(); + return getHttpClient(HttpUsage.BACKEND).postJson(url, responseType, headers, body).toObservable(); } public String postString(String url, Map headers, String contentType, String content) throws Exception { IHttpClient httpClient = getHttpClient(HttpUsage.BACKEND); - return AsyncUtil.getInstance().blockingGet( - httpClient.postString(url, headers, contentType, content)).get(); + return AsyncUtil.getInstance().blockingGet(httpClient.postString(url, headers, contentType, content)).get(); } public HostAndPort getTorProxy() { @@ -64,6 +60,7 @@ public class HttpClientService extends JettyHttpClientService { protected Task createTask() { return new Task<>() { protected Boolean call() throws Exception { + ThreadUtil.getInstance().getExecutorService().shutdown(); httpClientService.stop(); return true; } diff --git a/src/main/java/com/sparrowwallet/sparrow/net/HttpProxySupplier.java b/src/main/java/com/sparrowwallet/sparrow/net/HttpProxySupplier.java index 10566915..5a832652 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/HttpProxySupplier.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/HttpProxySupplier.java @@ -21,7 +21,7 @@ public class HttpProxySupplier implements IHttpProxySupplier { if (hostAndPort == null) { return null; } - // TODO verify + return new HttpProxy(HttpProxyProtocol.SOCKS, hostAndPort.getHost(), hostAndPort.getPort()); } diff --git a/src/main/java/com/sparrowwallet/sparrow/payjoin/Payjoin.java b/src/main/java/com/sparrowwallet/sparrow/payjoin/Payjoin.java index 970edd45..613af32e 100644 --- a/src/main/java/com/sparrowwallet/sparrow/payjoin/Payjoin.java +++ b/src/main/java/com/sparrowwallet/sparrow/payjoin/Payjoin.java @@ -2,7 +2,7 @@ package com.sparrowwallet.sparrow.payjoin; import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; -import com.samourai.wallet.api.backend.beans.HttpException; +import com.samourai.wallet.httpClient.HttpResponseException; import com.sparrowwallet.drongo.protocol.Script; import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.TransactionInput; @@ -87,7 +87,7 @@ public class Payjoin { checkProposal(psbt, proposalPsbt, changeOutputIndex, maxAdditionalFeeContribution, allowOutputSubstitution); return proposalPsbt; - } catch(HttpException e) { + } catch(HttpResponseException e) { Gson gson = new Gson(); PayjoinReceiverError payjoinReceiverError = gson.fromJson(e.getResponseBody(), PayjoinReceiverError.class); log.warn("Payjoin receiver returned an error of " + payjoinReceiverError.getErrorCode() + " (" + payjoinReceiverError.getMessage() + ")"); diff --git a/src/main/java/com/sparrowwallet/sparrow/soroban/CounterpartyController.java b/src/main/java/com/sparrowwallet/sparrow/soroban/CounterpartyController.java index b54bcc98..7ad8e841 100644 --- a/src/main/java/com/sparrowwallet/sparrow/soroban/CounterpartyController.java +++ b/src/main/java/com/sparrowwallet/sparrow/soroban/CounterpartyController.java @@ -25,6 +25,8 @@ import javafx.application.Platform; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; +import javafx.concurrent.Service; +import javafx.concurrent.Task; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.*; @@ -238,10 +240,19 @@ public class CounterpartyController extends SorobanController { SorobanWalletCounterparty sorobanWalletCounterparty = sorobanWalletService.getSorobanWalletCounterparty(cahootsWallet); sorobanWalletCounterparty.setTimeoutMeetingMs(TIMEOUT_MS); - try { - // TODO run in background thread? - SorobanRequestMessage requestMessage = sorobanWalletCounterparty.receiveMeetingRequest(); - + Service receiveMeetingService = new Service<>() { + @Override + protected Task createTask() { + return new Task<>() { + @Override + protected SorobanRequestMessage call() throws Exception { + return sorobanWalletCounterparty.receiveMeetingRequest(); + } + }; + } + }; + receiveMeetingService.setOnSucceeded(event -> { + SorobanRequestMessage requestMessage = receiveMeetingService.getValue(); PaymentCode paymentCodeInitiator = requestMessage.getSender(); CahootsType cahootsType = requestMessage.getType(); updateMixPartner(paymentCodeInitiator, cahootsType); @@ -261,11 +272,14 @@ public class CounterpartyController extends SorobanController { mixingPartner.setVisible(false); requestUserAttention(); }); - } catch(Exception e) { + }); + receiveMeetingService.setOnFailed(event -> { + Throwable e = event.getSource().getException(); log.error("Failed to receive meeting request", e); mixingPartner.setVisible(false); requestUserAttention(); - } + }); + receiveMeetingService.start(); } private void updateMixPartner(PaymentCode paymentCodeInitiator, CahootsType cahootsType) { @@ -308,32 +322,44 @@ public class CounterpartyController extends SorobanController { CahootsContext cahootsContext = CahootsContext.newCounterparty(cahootsWallet, cahootsType, account); Consumer onProgress = cahootsMessage -> { if(cahootsMessage != null) { - Cahoots cahoots = cahootsMessage.getCahoots(); - sorobanProgressBar.setProgress((double)(cahoots.getStep() + 1) / 5); + Platform.runLater(() -> { + Cahoots cahoots = cahootsMessage.getCahoots(); + sorobanProgressBar.setProgress((double)(cahoots.getStep() + 1) / 5); - if(cahoots.getStep() == 3) { - sorobanProgressLabel.setText("Your mix partner is reviewing the transaction..."); - step3Timer.start(); - } else if(cahoots.getStep() >= 4) { - try { - Transaction transaction = getTransaction(cahoots); - if(transaction != null) { - transactionProperty.set(transaction); - updateTransactionDiagram(transactionDiagram, wallet, null, transaction); - next(); + if(cahoots.getStep() == 3) { + sorobanProgressLabel.setText("Your mix partner is reviewing the transaction..."); + step3Timer.start(); + } else if(cahoots.getStep() >= 4) { + try { + Transaction transaction = getTransaction(cahoots); + if(transaction != null) { + transactionProperty.set(transaction); + updateTransactionDiagram(transactionDiagram, wallet, null, transaction); + next(); + } + } catch(PSBTParseException e) { + log.error("Invalid collaborative PSBT created", e); + step3Desc.setText("Invalid transaction created."); + sorobanProgressLabel.setVisible(false); } - } catch(PSBTParseException e) { - log.error("Invalid collaborative PSBT created", e); - step3Desc.setText("Invalid transaction created."); - sorobanProgressLabel.setVisible(false); } - } + }); } }; - try { - // TODO run in background thread? - Cahoots result = sorobanWalletCounterparty.counterparty(cahootsContext, initiatorPaymentCode, onProgress); - } catch (Exception error) { + + Service cahootsService = new Service<>() { + @Override + protected Task createTask() { + return new Task<>() { + @Override + protected Cahoots call() throws Exception { + return sorobanWalletCounterparty.counterparty(cahootsContext, initiatorPaymentCode, onProgress); + } + }; + } + }; + cahootsService.setOnFailed(event -> { + Throwable error = event.getSource().getException(); log.error("Error creating mix transaction", error); String cutFrom = "Exception: "; int index = error.getMessage().lastIndexOf(cutFrom); @@ -341,7 +367,8 @@ public class CounterpartyController extends SorobanController { msg = msg.replace("#Cahoots", "mix transaction"); step3Desc.setText(msg); sorobanProgressLabel.setVisible(false); - } + }); + cahootsService.start(); } catch(Exception e) { log.error("Error creating mix transaction", e); sorobanProgressLabel.setText(e.getMessage()); diff --git a/src/main/java/com/sparrowwallet/sparrow/soroban/InitiatorController.java b/src/main/java/com/sparrowwallet/sparrow/soroban/InitiatorController.java index c9e5bc07..e28bd9df 100644 --- a/src/main/java/com/sparrowwallet/sparrow/soroban/InitiatorController.java +++ b/src/main/java/com/sparrowwallet/sparrow/soroban/InitiatorController.java @@ -38,8 +38,6 @@ import com.sparrowwallet.sparrow.paynym.PayNymAddress; import com.sparrowwallet.sparrow.paynym.PayNymDialog; import com.sparrowwallet.sparrow.paynym.PayNymService; import io.reactivex.Observable; -import io.reactivex.rxjavafx.schedulers.JavaFxScheduler; -import io.reactivex.schedulers.Schedulers; import javafx.application.Platform; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -47,6 +45,8 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; +import javafx.concurrent.Service; +import javafx.concurrent.Task; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.*; @@ -431,79 +431,99 @@ public class InitiatorController extends SorobanController { public void onResponse(SorobanResponseMessage sorobanResponse) throws Exception { super.onResponse(sorobanResponse); - requestUserAttention(); - if(sorobanResponse.isAccept()) { - sorobanProgressBar.setProgress(0.1); - sorobanProgressLabel.setText("Mix partner accepted!"); - } else { - step2Desc.setText("Mix partner declined."); - sorobanProgressLabel.setVisible(false); - } + Platform.runLater(() -> { + requestUserAttention(); + if(sorobanResponse.isAccept()) { + sorobanProgressBar.setProgress(0.1); + sorobanProgressLabel.setText("Mix partner accepted!"); + } else { + step2Desc.setText("Mix partner declined."); + sorobanProgressLabel.setVisible(false); + } + }); } @Override public void onInteraction(OnlineSorobanInteraction interaction) throws Exception { SorobanInteraction originInteraction = interaction.getInteraction(); - if (originInteraction instanceof TxBroadcastInteraction) { - Boolean accepted = (Boolean)Platform.enterNestedEventLoop(transactionAccepted); - if(accepted) { - interaction.sorobanAccept(); - } else { - interaction.sorobanReject("Mix partner declined to broadcast the transaction."); - } + if(originInteraction instanceof TxBroadcastInteraction) { + Platform.runLater(() -> { + try { + Boolean accepted = (Boolean)Platform.enterNestedEventLoop(transactionAccepted); + if(accepted) { + interaction.sorobanAccept(); + } else { + interaction.sorobanReject("Mix partner declined to broadcast the transaction."); + } + } catch(Exception e) { + log.error("Error accepting Soroban interaction", e); + } + }); } else { throw new Exception("Unknown interaction: "+originInteraction.getTypeInteraction()); } } @Override - public void progress(OnlineCahootsMessage message) { - super.progress(message); + public void progress(OnlineCahootsMessage cahootsMessage) { + super.progress(cahootsMessage); - OnlineCahootsMessage cahootsMessage = (OnlineCahootsMessage)message; if(cahootsMessage != null) { - Cahoots cahoots = cahootsMessage.getCahoots(); - sorobanProgressBar.setProgress((double)(cahoots.getStep() + 1) / 5); + Platform.runLater(() -> { + Cahoots cahoots = cahootsMessage.getCahoots(); + sorobanProgressBar.setProgress((double)(cahoots.getStep() + 1) / 5); - if(cahoots.getStep() >= 3) { - try { - Transaction transaction = getTransaction(cahoots); - if(transaction != null) { - transactionProperty.set(transaction); - if(cahoots.getStep() == 3) { - next(); - step3Timer.start(e -> { - if(stepProperty.get() != Step.BROADCAST && stepProperty.get() != Step.REBROADCAST) { - step3Desc.setText("Transaction declined due to timeout."); - transactionAccepted.set(Boolean.FALSE); - } - }); - } else if(cahoots.getStep() == 4) { - next(); - broadcastTransaction(); + if(cahoots.getStep() >= 3) { + try { + Transaction transaction = getTransaction(cahoots); + if(transaction != null) { + transactionProperty.set(transaction); + if(cahoots.getStep() == 3) { + next(); + step3Timer.start(e -> { + if(stepProperty.get() != Step.BROADCAST && stepProperty.get() != Step.REBROADCAST) { + step3Desc.setText("Transaction declined due to timeout."); + transactionAccepted.set(Boolean.FALSE); + } + }); + } else if(cahoots.getStep() == 4) { + next(); + broadcastTransaction(); + } } + } catch(PSBTParseException e) { + log.error("Invalid collaborative PSBT created", e); + step2Desc.setText("Invalid transaction created."); + sorobanProgressLabel.setVisible(false); } - } catch(PSBTParseException e) { - log.error("Invalid collaborative PSBT created", e); - step2Desc.setText("Invalid transaction created."); - sorobanProgressLabel.setVisible(false); } - } + }); } } }; SorobanWalletService sorobanWalletService = soroban.getSorobanWalletService(); sorobanProgressLabel.setText("Waiting for mix partner..."); - try { - // TODO run in background thread? - Cahoots result = sorobanWalletService.getSorobanWalletInitiator(cahootsWallet).meetAndInitiate(cahootsContext, paymentCodeCounterparty, listener); - } catch (Exception error){ + + Service cahootsService = new Service<>() { + @Override + protected Task createTask() { + return new Task<>() { + @Override + protected Cahoots call() throws Exception { + return sorobanWalletService.getSorobanWalletInitiator(cahootsWallet).meetAndInitiate(cahootsContext, paymentCodeCounterparty, listener); + } + }; + } + }; + cahootsService.setOnFailed(event -> { + Throwable error = event.getSource().getException(); log.error("Error receiving meeting response", error); step2Desc.setText(getErrorMessage(error)); sorobanProgressLabel.setVisible(false); meetingFail.setVisible(true); requestUserAttention(); - } + }); + cahootsService.start(); }, error -> { log.error("Could not retrieve payment code", error); if(error.getMessage().endsWith("404")) { diff --git a/src/main/java/com/sparrowwallet/sparrow/soroban/Soroban.java b/src/main/java/com/sparrowwallet/sparrow/soroban/Soroban.java index 79dfe58a..78ec0647 100644 --- a/src/main/java/com/sparrowwallet/sparrow/soroban/Soroban.java +++ b/src/main/java/com/sparrowwallet/sparrow/soroban/Soroban.java @@ -79,26 +79,4 @@ public class Soroban { public SorobanWalletService getSorobanWalletService() { return sorobanWalletService; } - - public void stop() { - AppServices.getHttpClientService().stop(); - } - - public static class ShutdownService extends Service { - private final Soroban soroban; - - public ShutdownService(Soroban soroban) { - this.soroban = soroban; - } - - @Override - protected Task createTask() { - return new Task<>() { - protected Boolean call() throws Exception { - soroban.stop(); - return true; - } - }; - } - } } diff --git a/src/main/java/com/sparrowwallet/sparrow/soroban/SorobanServices.java b/src/main/java/com/sparrowwallet/sparrow/soroban/SorobanServices.java index ca2d33b8..6c45debf 100644 --- a/src/main/java/com/sparrowwallet/sparrow/soroban/SorobanServices.java +++ b/src/main/java/com/sparrowwallet/sparrow/soroban/SorobanServices.java @@ -53,14 +53,7 @@ public class SorobanServices { public void walletTabsClosed(WalletTabsClosedEvent event) { for(WalletTabData walletTabData : event.getClosedWalletTabData()) { String walletId = walletTabData.getStorage().getWalletId(walletTabData.getWallet()); - Soroban soroban = sorobanMap.remove(walletId); - if(soroban != null) { - Soroban.ShutdownService shutdownService = new Soroban.ShutdownService(soroban); - shutdownService.setOnFailed(failedEvent -> { - log.error("Failed to shutdown soroban", failedEvent.getSource().getException()); - }); - shutdownService.start(); - } + sorobanMap.remove(walletId); } } } \ No newline at end of file diff --git a/src/main/java/com/sparrowwallet/sparrow/soroban/SparrowCahootsWallet.java b/src/main/java/com/sparrowwallet/sparrow/soroban/SparrowCahootsWallet.java index d23615f6..b7179a6e 100644 --- a/src/main/java/com/sparrowwallet/sparrow/soroban/SparrowCahootsWallet.java +++ b/src/main/java/com/sparrowwallet/sparrow/soroban/SparrowCahootsWallet.java @@ -26,13 +26,12 @@ import java.util.List; public class SparrowCahootsWallet extends AbstractCahootsWallet { private final Wallet wallet; - private HD_Wallet bip84w; + private final HD_Wallet bip84w; private final int account; - private List utxos; + private final List utxos; public SparrowCahootsWallet(ChainSupplier chainSupplier, Wallet wallet, HD_Wallet bip84w, int bip47Account) { - super(chainSupplier, bip84w.getFingerprint(), - new BIP47Wallet(bip84w).getAccount(bip47Account)); + super(chainSupplier, bip84w.getFingerprint(), new BIP47Wallet(bip84w).getAccount(bip47Account)); this.wallet = wallet; this.bip84w = bip84w; this.account = wallet.getAccountIndex(); @@ -107,7 +106,7 @@ public class SparrowCahootsWallet extends AbstractCahootsWallet { throw new IllegalStateException("Cannot add BIP47 UTXO", e); } } else { - HD_Address hdAddress = bip84w.getAddressAt(index, unspentOutput); + HD_Address hdAddress = bip84w.getAddressAt(account, unspentOutput); cahootsUtxo = new CahootsUtxo(myTransactionOutPoint, node.getDerivationPath(), null, hdAddress.getECKey().getPrivKeyBytes()); } diff --git a/src/main/java/com/sparrowwallet/sparrow/terminal/wallet/table/MixTableCell.java b/src/main/java/com/sparrowwallet/sparrow/terminal/wallet/table/MixTableCell.java index b4d02397..49a822a1 100644 --- a/src/main/java/com/sparrowwallet/sparrow/terminal/wallet/table/MixTableCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/terminal/wallet/table/MixTableCell.java @@ -43,7 +43,7 @@ public class MixTableCell extends TableCell { private String getMixFail(UtxoEntry.MixStatus mixStatus) { long elapsed = mixStatus.getMixErrorTimestamp() == null ? 0L : System.currentTimeMillis() - mixStatus.getMixErrorTimestamp(); - if(mixStatus.getMixFailReason() == MixFailReason.CANCEL || elapsed >= ERROR_DISPLAY_MILLIS) { + if(mixStatus.getMixFailReason() == MixFailReason.STOP_UTXO || mixStatus.getMixFailReason().isSilent() || elapsed >= ERROR_DISPLAY_MILLIS) { return getMixCountOnly(mixStatus); } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletTransactionsEntry.java b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletTransactionsEntry.java index 72a30f17..5926f211 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletTransactionsEntry.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletTransactionsEntry.java @@ -129,9 +129,10 @@ public class WalletTransactionsEntry extends Entry { private static void getWalletTransactions(Wallet wallet, Map walletTransactionMap, WalletNode purposeNode) { KeyPurpose keyPurpose = purposeNode.getKeyPurpose(); List childNodes = new ArrayList<>(purposeNode.getChildren()); + Wallet transactionsWallet = wallet.isNested() ? wallet.getMasterWallet() : wallet; for(WalletNode addressNode : childNodes) { for(BlockTransactionHashIndex hashIndex : addressNode.getTransactionOutputs()) { - BlockTransaction inputTx = wallet.getWalletTransaction(hashIndex.getHash()); + BlockTransaction inputTx = transactionsWallet.getWalletTransaction(hashIndex.getHash()); //A null inputTx here means the wallet is still updating - ignore as the WalletHistoryChangedEvent will run this again if(inputTx != null) { WalletTransaction inputWalletTx = walletTransactionMap.get(inputTx); @@ -142,7 +143,7 @@ public class WalletTransactionsEntry extends Entry { inputWalletTx.incoming.put(hashIndex, keyPurpose); if(hashIndex.getSpentBy() != null) { - BlockTransaction outputTx = wallet.getWalletTransaction(hashIndex.getSpentBy().getHash()); + BlockTransaction outputTx = transactionsWallet.getWalletTransaction(hashIndex.getSpentBy().getHash()); if(outputTx != null) { WalletTransaction outputWalletTx = walletTransactionMap.get(outputTx); if(outputWalletTx == null) { diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletUtxosEntry.java b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletUtxosEntry.java index 6c3208ac..4b6f4ca0 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletUtxosEntry.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletUtxosEntry.java @@ -6,6 +6,7 @@ import com.sparrowwallet.drongo.wallet.WalletNode; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.io.Config; import com.sparrowwallet.sparrow.whirlpool.Whirlpool; +import javafx.application.Platform; import java.util.*; import java.util.stream.Collectors; @@ -92,7 +93,8 @@ public class WalletUtxosEntry extends Entry { calculateDuplicates(); calculateDust(); - updateMixProgress(); + //Update mix status after SparrowUtxoSupplier has refreshed + Platform.runLater(this::updateMixProgress); } public long getBalance() { diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java index bb6b38c0..6ec2e5dd 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java @@ -33,10 +33,7 @@ import com.sparrowwallet.drongo.ExtendedKey; import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.Network; import com.sparrowwallet.drongo.Utils; -import com.sparrowwallet.drongo.protocol.Sha256Hash; -import com.sparrowwallet.drongo.protocol.Transaction; -import com.sparrowwallet.drongo.protocol.TransactionInput; -import com.sparrowwallet.drongo.protocol.TransactionOutput; +import com.sparrowwallet.drongo.protocol.*; import com.sparrowwallet.drongo.wallet.*; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.EventManager; @@ -71,7 +68,6 @@ public class Whirlpool { public static final int DEFAULT_MIXTO_MIN_MIXES = 3; public static final int DEFAULT_MIXTO_RANDOM_FACTOR = 4; - private final WhirlpoolWalletService whirlpoolWalletService; private final WhirlpoolWalletConfig config; private WhirlpoolInfo whirlpoolInfo; @@ -89,11 +85,10 @@ public class Whirlpool { private final BooleanProperty stoppingProperty = new SimpleBooleanProperty(false); private final BooleanProperty mixingProperty = new SimpleBooleanProperty(false); - public Whirlpool() { + public Whirlpool(Integer storedBlockHeight) { this.whirlpoolWalletService = new WhirlpoolWalletService(); - Integer storedBlockHeight = null; // TODO this.config = computeWhirlpoolWalletConfig(storedBlockHeight); - this.whirlpoolInfo = null; // instanciated by getWhirlpoolInfo() + this.whirlpoolInfo = null; // instantiated by getWhirlpoolInfo() WhirlpoolEventService.getInstance().register(this); } @@ -112,17 +107,14 @@ public class Whirlpool { } private DataSourceConfig computeDataSourceConfig(Integer storedBlockHeight) { - return new DataSourceConfig( - SparrowMinerFeeSupplier.getInstance(), - new SparrowChainSupplier(storedBlockHeight), - BIP_FORMAT.PROVIDER, - BIP_WALLETS.WHIRLPOOL); + return new DataSourceConfig(SparrowMinerFeeSupplier.getInstance(), new SparrowChainSupplier(storedBlockHeight), BIP_FORMAT.PROVIDER, BIP_WALLETS.WHIRLPOOL); } private WhirlpoolInfo getWhirlpoolInfo() { - if (whirlpoolInfo == null) { + if(whirlpoolInfo == null) { whirlpoolInfo = new WhirlpoolInfo(SparrowMinerFeeSupplier.getInstance(), config); } + return whirlpoolInfo; } @@ -144,8 +136,7 @@ public class Whirlpool { public Tx0 broadcastTx0(Pool pool, Collection utxos) throws Exception { WhirlpoolWallet whirlpoolWallet = getWhirlpoolWallet(); - whirlpoolWallet.startAsync().subscribeOn(Schedulers.io()) - .observeOn(JavaFxScheduler.platform()); + whirlpoolWallet.startAsync().subscribeOn(Schedulers.io()).observeOn(JavaFxScheduler.platform()); UtxoSupplier utxoSupplier = whirlpoolWallet.getUtxoSupplier(); List whirlpoolUtxos = utxos.stream().map(ref -> utxoSupplier.findUtxo(ref.getHashAsString(), (int)ref.getIndex())).filter(Objects::nonNull).collect(Collectors.toList()); @@ -177,10 +168,13 @@ public class Whirlpool { try { Keystore keystore = wallet.getKeystores().get(0); - String words = keystore.getSeed().getMnemonicString().asString(); + ScriptType scriptType = wallet.getScriptType(); + int purpose = scriptType.getDefaultDerivation().get(0).num(); + List words = keystore.getSeed().getMnemonicCode(); String passphrase = keystore.getSeed().getPassphrase() == null ? "" : keystore.getSeed().getPassphrase().asString(); HD_WalletFactoryGeneric hdWalletFactory = HD_WalletFactoryGeneric.getInstance(); - return hdWalletFactory.restoreWalletFromWords(words, passphrase, params); + byte[] seed = hdWalletFactory.computeSeedFromWords(words); + return hdWalletFactory.getHD(purpose, seed, passphrase, params); } catch(Exception e) { throw new IllegalStateException("Could not create Whirlpool HD wallet ", e); } @@ -264,9 +258,7 @@ public class Whirlpool { public void refreshUtxos() { if(whirlpoolWalletService.whirlpoolWallet() != null) { - whirlpoolWalletService.whirlpoolWallet().refreshUtxosAsync() - .subscribeOn(Schedulers.io()) - .observeOn(JavaFxScheduler.platform()); + whirlpoolWalletService.whirlpoolWallet().refreshUtxosAsync().subscribeOn(Schedulers.io()).observeOn(JavaFxScheduler.platform()); } } @@ -352,7 +344,6 @@ public class Whirlpool { public void shutdown() { whirlpoolWalletService.closeWallet(); - AppServices.getHttpClientService().stop(); } public StartupService createStartupService() { @@ -517,7 +508,12 @@ public class Whirlpool { int mixes = minMixes == null ? DEFAULT_MIXTO_MIN_MIXES : minMixes; IPostmixHandler postmixHandler = new SparrowPostmixHandler(whirlpoolWalletService, mixToWallet, KeyPurpose.RECEIVE); - ExternalDestination externalDestination = new ExternalDestination(postmixHandler, 0, mixes, DEFAULT_MIXTO_RANDOM_FACTOR); + ExternalDestination externalDestination = new ExternalDestination(postmixHandler, 0, mixes, DEFAULT_MIXTO_RANDOM_FACTOR) { + @Override + public IPostmixHandler getPostmixHandlerCustomOrDefault(WhirlpoolWallet whirlpoolWallet) { + return postmixHandler; + } + }; config.setExternalDestination(externalDestination); } @@ -715,10 +711,7 @@ public class Whirlpool { whirlpool.startingProperty.set(true); WhirlpoolWallet whirlpoolWallet = whirlpool.getWhirlpoolWallet(); if(AppServices.onlineProperty().get()) { - whirlpoolWallet.startAsync() - .subscribeOn(Schedulers.io()) - .observeOn(JavaFxScheduler.platform()) - .subscribe(); + whirlpoolWallet.startAsync().subscribeOn(Schedulers.io()).observeOn(JavaFxScheduler.platform()).subscribe(); } return whirlpoolWallet; diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java index 44df9bb7..4b082da7 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java @@ -73,7 +73,8 @@ public class WhirlpoolServices { public Whirlpool getWhirlpool(String walletId) { Whirlpool whirlpool = whirlpoolMap.get(walletId); if(whirlpool == null) { - whirlpool = new Whirlpool(); + Wallet wallet = AppServices.get().getWallet(walletId); + whirlpool = new Whirlpool(wallet == null ? null : wallet.getStoredBlockHeight()); whirlpoolMap.put(walletId, whirlpool); } diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowChainSupplier.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowChainSupplier.java index 3bc7d80e..ec29da5c 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowChainSupplier.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowChainSupplier.java @@ -3,19 +3,18 @@ package com.sparrowwallet.sparrow.whirlpool.dataSource; import com.google.common.eventbus.Subscribe; import com.samourai.wallet.api.backend.beans.WalletResponse; import com.samourai.wallet.chain.ChainSupplier; +import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.protocol.Sha256Hash; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.event.NewBlockEvent; public class SparrowChainSupplier implements ChainSupplier { - private int storedBlockHeight; + private final int storedBlockHeight; private WalletResponse.InfoBlock latestBlock; public SparrowChainSupplier(Integer storedBlockHeight) { - this.storedBlockHeight = AppServices.getCurrentBlockHeight() == null ? - (storedBlockHeight!=null?storedBlockHeight:0) - : AppServices.getCurrentBlockHeight(); + this.storedBlockHeight = AppServices.getCurrentBlockHeight() == null ? (storedBlockHeight != null ? storedBlockHeight : 0) : AppServices.getCurrentBlockHeight(); this.latestBlock = computeLatestBlock(); EventManager.get().register(this); } @@ -27,7 +26,8 @@ public class SparrowChainSupplier implements ChainSupplier { private WalletResponse.InfoBlock computeLatestBlock() { WalletResponse.InfoBlock latestBlock = new WalletResponse.InfoBlock(); latestBlock.height = AppServices.getCurrentBlockHeight() == null ? storedBlockHeight : AppServices.getCurrentBlockHeight(); - latestBlock.hash = Sha256Hash.ZERO_HASH.toString(); + latestBlock.hash = AppServices.getLatestBlockHeader() == null ? Sha256Hash.ZERO_HASH.toString() : + Utils.bytesToHex(Sha256Hash.twiceOf(AppServices.getLatestBlockHeader().bitcoinSerialize()).getReversedBytes()); latestBlock.time = AppServices.getLatestBlockHeader() == null ? 1 : AppServices.getLatestBlockHeader().getTime(); return latestBlock; } diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowDataSource.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowDataSource.java index 7c6ab4e2..3de4b86f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowDataSource.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowDataSource.java @@ -40,7 +40,7 @@ public class SparrowDataSource extends AbstractDataSource { private final ISeenBackend seenBackend; private final IPushTx pushTx; - private SparrowUtxoSupplier utxoSupplier; + private final SparrowUtxoSupplier utxoSupplier; public SparrowDataSource( WhirlpoolWallet whirlpoolWallet, @@ -57,7 +57,7 @@ public class SparrowDataSource extends AbstractDataSource { private ISeenBackend computeSeenBackend(WhirlpoolWalletConfig whirlpoolWalletConfig) { IHttpClient httpClient = whirlpoolWalletConfig.getHttpClient(HttpUsage.BACKEND); - ISeenBackend sparrowSeenBackend = new SparrowSeenBackend(httpClient); + ISeenBackend sparrowSeenBackend = new SparrowSeenBackend(getWhirlpoolWallet().getWalletIdentifier(), httpClient); NetworkParameters params = whirlpoolWalletConfig.getSamouraiNetwork().getParams(); return SeenBackendWithFallback.withOxt(sparrowSeenBackend, params); } diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowPostmixHandler.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowPostmixHandler.java index 84074927..9f186397 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowPostmixHandler.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowPostmixHandler.java @@ -1,8 +1,10 @@ package com.sparrowwallet.sparrow.whirlpool.dataSource; -import com.samourai.whirlpool.client.mix.handler.AbstractPostmixHandler; +import com.samourai.wallet.client.indexHandler.IIndexHandler; import com.samourai.whirlpool.client.mix.handler.DestinationType; +import com.samourai.whirlpool.client.mix.handler.IPostmixHandler; import com.samourai.whirlpool.client.mix.handler.MixDestination; +import com.samourai.whirlpool.client.utils.ClientUtils; import com.samourai.whirlpool.client.wallet.WhirlpoolWalletService; import com.samourai.whirlpool.client.wallet.beans.IndexRange; import com.sparrowwallet.drongo.KeyPurpose; @@ -12,21 +14,21 @@ import com.sparrowwallet.drongo.wallet.WalletNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -// TODO maybe replace with XPubPostmixHandler -public class SparrowPostmixHandler extends AbstractPostmixHandler { +public class SparrowPostmixHandler implements IPostmixHandler { private static final Logger log = LoggerFactory.getLogger(SparrowPostmixHandler.class); + private final WhirlpoolWalletService whirlpoolWalletService; private final Wallet wallet; private final KeyPurpose keyPurpose; + protected MixDestination destination; + public SparrowPostmixHandler(WhirlpoolWalletService whirlpoolWalletService, Wallet wallet, KeyPurpose keyPurpose) { - super(whirlpoolWalletService.whirlpoolWallet().getWalletStateSupplier().getIndexHandlerExternal(), - whirlpoolWalletService.whirlpoolWallet().getConfig().getSamouraiNetwork().getParams()); + this.whirlpoolWalletService = whirlpoolWalletService; this.wallet = wallet; this.keyPurpose = keyPurpose; } - @Override protected IndexRange getIndexRange() { return IndexRange.FULL; } @@ -35,14 +37,50 @@ public class SparrowPostmixHandler extends AbstractPostmixHandler { return wallet; } + @Override + public final MixDestination computeDestinationNext() throws Exception { + // use "unconfirmed" index to avoid huge index gaps on multiple mix failures + int index = ClientUtils.computeNextReceiveAddressIndex(getIndexHandler(), getIndexRange()); + this.destination = computeDestination(index); + if (log.isDebugEnabled()) { + log.debug( + "Mixing to " + + destination.getType() + + " -> receiveAddress=" + + destination.getAddress() + + ", path=" + + destination.getPath()); + } + return destination; + } + @Override public MixDestination computeDestination(int index) throws Exception { // address WalletNode node = new WalletNode(wallet, keyPurpose, index); Address address = node.getAddress(); - String path = "xpub/"+keyPurpose.getPathIndex().num()+"/"+index; + String path = "xpub/" + keyPurpose.getPathIndex().num() + "/" + index; log.info("Mixing to external xPub -> receiveAddress=" + address + ", path=" + path); return new MixDestination(DestinationType.XPUB, index, address.toString(), path); } + + @Override + public void onMixFail() { + if(destination != null) { + // cancel unconfirmed postmix index if output was not registered yet + getIndexHandler().cancelUnconfirmed(destination.getIndex()); + } + } + + @Override + public void onRegisterOutput() { + // confirm postmix index on REGISTER_OUTPUT success + getIndexHandler().confirmUnconfirmed(destination.getIndex()); + } + + @Override + public IIndexHandler getIndexHandler() { + return whirlpoolWalletService.whirlpoolWallet().getWalletStateSupplier().getIndexHandlerExternal(); + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowSeenBackend.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowSeenBackend.java index bd458ec3..03c65721 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowSeenBackend.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowSeenBackend.java @@ -3,30 +3,51 @@ package com.sparrowwallet.sparrow.whirlpool.dataSource; import com.samourai.wallet.api.backend.seenBackend.ISeenBackend; import com.samourai.wallet.api.backend.seenBackend.SeenResponse; import com.samourai.wallet.httpClient.IHttpClient; +import com.sparrowwallet.drongo.address.Address; +import com.sparrowwallet.drongo.wallet.Wallet; +import com.sparrowwallet.drongo.wallet.WalletNode; +import com.sparrowwallet.sparrow.AppServices; import java.util.Collection; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; public class SparrowSeenBackend implements ISeenBackend { - private IHttpClient httpClient; + private final String walletId; + private final IHttpClient httpClient; - public SparrowSeenBackend(IHttpClient httpClient) { + public SparrowSeenBackend(String walletId, IHttpClient httpClient) { + this.walletId = walletId; this.httpClient = httpClient; } @Override public SeenResponse seen(Collection addresses) throws Exception { - Map map = new LinkedHashMap<>(); - for (String address : addresses) { - map.put(address, seen(address)); + Wallet wallet = AppServices.get().getWallet(walletId); + Map addressMap = wallet.getWalletAddresses(); + for(Wallet childWallet : wallet.getChildWallets()) { + if(!childWallet.isNested()) { + addressMap.putAll(childWallet.getWalletAddresses()); + } } + + Map map = new LinkedHashMap<>(); + for(String address : addresses) { + WalletNode walletNode = addressMap.get(Address.fromString(address)); + if(walletNode != null) { + int highestUsedIndex = walletNode.getWallet().getNode(walletNode.getKeyPurpose()).getHighestUsedIndex(); + map.put(address, walletNode.getIndex() <= highestUsedIndex); + } + } + return new SeenResponse(map); } @Override public boolean seen(String address) throws Exception { - return false; // TODO implement: return true if address already received funds + SeenResponse seenResponse = seen(List.of(address)); + return seenResponse.isSeen(address); } @Override diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowWalletStateSupplier.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowWalletStateSupplier.java index 2a186d06..878739e8 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowWalletStateSupplier.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/dataSource/SparrowWalletStateSupplier.java @@ -35,7 +35,7 @@ public class SparrowWalletStateSupplier implements WalletStateSupplier { SamouraiAccount samouraiAccount = bipWallet.getAccount(); String key = mapKey(bipWallet, chain); IIndexHandler indexHandler = indexHandlerWallets.get(key); - if (indexHandler == null) { + if(indexHandler == null) { Wallet wallet = findWallet(samouraiAccount); KeyPurpose keyPurpose = (chain == Chain.RECEIVE ? KeyPurpose.RECEIVE : KeyPurpose.CHANGE); WalletNode walletNode = wallet.getNode(keyPurpose); @@ -123,7 +123,7 @@ public class SparrowWalletStateSupplier implements WalletStateSupplier { private String mapKey(BipWallet bipWallet, Chain chain) { SamouraiAccount samouraiAccount = bipWallet.getAccount(); BipDerivation derivation = bipWallet.getDerivation(); - return samouraiAccount.name()+"_"+derivation.getPurpose()+"_"+chain.getIndex(); + return samouraiAccount.name() + "_" + derivation.getPurpose() + "_" + chain.getIndex(); } private Wallet findWallet(SamouraiAccount samouraiAccount) { diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index e54dbe68..79e1caaf 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -64,7 +64,6 @@ open module com.sparrowwallet.sparrow { requires com.sparrowwallet.bokmakierie; requires java.smartcardio; requires com.jcraft.jzlib; - // samourai dependencies requires com.samourai.whirlpool.client; requires com.samourai.whirlpool.protocol; requires com.samourai.extlibj;