mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-23 20:36:44 +00:00
fixes for encrypted whirlpool wallets and other issues
This commit is contained in:
parent
f30c00ba8f
commit
aa10bcfe1a
17 changed files with 142 additions and 69 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
|||
Subproject commit 7ac4bce14f04163c57b94e34945b5e4a1bf79eb6
|
||||
Subproject commit 71b5778226ef22881240143425325525c1a98d06
|
|
@ -1229,6 +1229,8 @@ public class AppController implements Initializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventManager.get().post(new WalletOpenedEvent(storage, wallet));
|
||||
}
|
||||
|
||||
public WalletForm addWalletSubTab(TabPane subTabs, Storage storage, Wallet wallet, Wallet backupWallet) {
|
||||
|
|
|
@ -968,7 +968,10 @@ public class AppServices {
|
|||
@Subscribe
|
||||
public void walletOpening(WalletOpeningEvent event) {
|
||||
restartBwt(event.getWallet());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletOpened(WalletOpenedEvent event) {
|
||||
String walletId = event.getStorage().getWalletId(event.getWallet());
|
||||
Whirlpool whirlpool = whirlpoolMap.get(walletId);
|
||||
if(whirlpool != null && !whirlpool.isStarted() && isConnected()) {
|
||||
|
|
|
@ -42,7 +42,13 @@ public class MainApp extends Application {
|
|||
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> LoggerFactory.getLogger(MainApp.class).error("Exception in thread \"" + t.getName() + "\"", e));
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
|
||||
if(e instanceof IndexOutOfBoundsException && Arrays.stream(e.getStackTrace()).anyMatch(element -> element.getClassName().equals("javafx.scene.chart.BarChart"))) {
|
||||
LoggerFactory.getLogger(MainApp.class).debug("Exception in thread \"" + t.getName() + "\"", e);;
|
||||
} else {
|
||||
LoggerFactory.getLogger(MainApp.class).error("Exception in thread \"" + t.getName() + "\"", e);
|
||||
}
|
||||
});
|
||||
super.init();
|
||||
}
|
||||
|
||||
|
|
|
@ -27,10 +27,10 @@ public class HelpLabel extends Label {
|
|||
}
|
||||
|
||||
private static Glyph getHelpGlyph() {
|
||||
Glyph lockGlyph = new Glyph("Font Awesome 5 Free Solid", FontAwesome5.Glyph.QUESTION_CIRCLE);
|
||||
lockGlyph.getStyleClass().add("help-icon");
|
||||
lockGlyph.setFontSize(12);
|
||||
return lockGlyph;
|
||||
Glyph glyph = new Glyph("Font Awesome 5 Free Solid", FontAwesome5.Glyph.QUESTION_CIRCLE);
|
||||
glyph.getStyleClass().add("help-icon");
|
||||
glyph.setFontSize(11);
|
||||
return glyph;
|
||||
}
|
||||
|
||||
public final StringProperty helpTextProperty() {
|
||||
|
|
|
@ -101,7 +101,9 @@ public class UtxosTreeTable extends CoinTreeTable {
|
|||
|
||||
public void updateHistory(List<WalletNode> updatedNodes) {
|
||||
//Utxo entries should have already been updated, so only a resort required
|
||||
sort();
|
||||
if(!getRoot().getChildren().isEmpty()) {
|
||||
sort();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLabel(Entry entry) {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.io.Storage;
|
||||
|
||||
public class WalletOpenedEvent {
|
||||
private final Storage storage;
|
||||
private final Wallet wallet;
|
||||
|
||||
public WalletOpenedEvent(Storage storage, Wallet wallet) {
|
||||
this.storage = storage;
|
||||
this.wallet = wallet;
|
||||
}
|
||||
|
||||
public Storage getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public Wallet getWallet() {
|
||||
return wallet;
|
||||
}
|
||||
}
|
|
@ -39,6 +39,7 @@ public class FontAwesome5 extends GlyphFont {
|
|||
HAND_HOLDING_MEDICAL('\ue05c'),
|
||||
HAND_HOLDING_WATER('\uf4c1'),
|
||||
HISTORY('\uf1da'),
|
||||
INFO_CIRCLE('\uf05a'),
|
||||
KEY('\uf084'),
|
||||
LAPTOP('\uf109'),
|
||||
LOCK('\uf023'),
|
||||
|
|
|
@ -128,7 +128,10 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
private ToggleButton privacyToggle;
|
||||
|
||||
@FXML
|
||||
private HelpLabel privacyAnalysis;
|
||||
private HelpLabel optimizationHelp;
|
||||
|
||||
@FXML
|
||||
private Label privacyAnalysis;
|
||||
|
||||
@FXML
|
||||
private Button clearButton;
|
||||
|
@ -417,6 +420,9 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
});
|
||||
setPreferredOptimizationStrategy();
|
||||
updatePrivacyAnalysis(null);
|
||||
optimizationHelp.managedProperty().bind(optimizationHelp.visibleProperty());
|
||||
privacyAnalysis.managedProperty().bind(privacyAnalysis.visibleProperty());
|
||||
optimizationHelp.visibleProperty().bind(privacyAnalysis.visibleProperty().not());
|
||||
|
||||
createButton.managedProperty().bind(createButton.visibleProperty());
|
||||
premixButton.managedProperty().bind(premixButton.visibleProperty());
|
||||
|
@ -971,11 +977,15 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
|
||||
private void updatePrivacyAnalysis(WalletTransaction walletTransaction) {
|
||||
if(walletTransaction == null) {
|
||||
privacyAnalysis.setHelpText("Determines whether to optimize the transaction for low fees or greater privacy");
|
||||
privacyAnalysis.setHelpGraphic(null);
|
||||
privacyAnalysis.setVisible(false);
|
||||
privacyAnalysis.setTooltip(null);
|
||||
} else {
|
||||
privacyAnalysis.setHelpText("");
|
||||
privacyAnalysis.setHelpGraphic(new PrivacyAnalysisTooltip(walletTransaction));
|
||||
privacyAnalysis.setVisible(true);
|
||||
Tooltip tooltip = new Tooltip();
|
||||
tooltip.setShowDelay(new Duration(50));
|
||||
tooltip.setShowDuration(Duration.INDEFINITE);
|
||||
tooltip.setGraphic(new PrivacyAnalysisTooltip(walletTransaction));
|
||||
privacyAnalysis.setTooltip(tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1012,6 +1022,9 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
validationSupport.setErrorDecorationEnabled(false);
|
||||
|
||||
setInputFieldsDisabled(false);
|
||||
|
||||
premixButton.setVisible(false);
|
||||
createButton.setDefaultButton(true);
|
||||
}
|
||||
|
||||
public UtxoSelector getUtxoSelector() {
|
||||
|
@ -1108,35 +1121,8 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
}
|
||||
}
|
||||
|
||||
Wallet copy = getWalletForm().getWallet().copy();
|
||||
String walletId = walletForm.getWalletId();
|
||||
|
||||
if(copy.isEncrypted()) {
|
||||
WalletPasswordDialog dlg = new WalletPasswordDialog(copy.getMasterName(), WalletPasswordDialog.PasswordRequirement.LOAD);
|
||||
Optional<SecureString> password = dlg.showAndWait();
|
||||
if(password.isPresent()) {
|
||||
Storage.DecryptWalletService decryptWalletService = new Storage.DecryptWalletService(copy, password.get());
|
||||
decryptWalletService.setOnSucceeded(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Done"));
|
||||
Wallet decryptedWallet = decryptWalletService.getValue();
|
||||
broadcastPremixUnencrypted(decryptedWallet);
|
||||
});
|
||||
decryptWalletService.setOnFailed(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Failed"));
|
||||
AppServices.showErrorDialog("Incorrect Password", decryptWalletService.getException().getMessage());
|
||||
});
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.START, "Decrypting wallet..."));
|
||||
decryptWalletService.start();
|
||||
}
|
||||
} else {
|
||||
broadcastPremixUnencrypted(copy);
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastPremixUnencrypted(Wallet decryptedWallet) {
|
||||
//The WhirlpoolWallet has already been configured for the tx0 preview
|
||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(getWalletForm().getWalletId());
|
||||
whirlpool.setScode(Config.get().getScode());
|
||||
whirlpool.setHDWallet(getWalletForm().getWalletId(), decryptedWallet);
|
||||
Map<BlockTransactionHashIndex, WalletNode> utxos = walletTransactionProperty.get().getSelectedUtxos();
|
||||
Whirlpool.Tx0BroadcastService tx0BroadcastService = new Whirlpool.Tx0BroadcastService(whirlpool, whirlpoolProperty.get(), utxos.keySet());
|
||||
tx0BroadcastService.setOnRunning(workerStateEvent -> {
|
||||
|
@ -1146,12 +1132,10 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
tx0BroadcastService.setOnSucceeded(workerStateEvent -> {
|
||||
premixButton.setDisable(false);
|
||||
Sha256Hash txid = tx0BroadcastService.getValue();
|
||||
decryptedWallet.clearPrivate();
|
||||
clear(null);
|
||||
});
|
||||
tx0BroadcastService.setOnFailed(workerStateEvent -> {
|
||||
premixButton.setDisable(false);
|
||||
decryptedWallet.clearPrivate();
|
||||
Throwable exception = workerStateEvent.getSource().getException();
|
||||
while(exception.getCause() != null) {
|
||||
exception = exception.getCause();
|
||||
|
|
|
@ -6,8 +6,10 @@ import com.samourai.whirlpool.client.tx0.Tx0Preview;
|
|||
import com.sparrowwallet.drongo.BitcoinUnit;
|
||||
import com.sparrowwallet.drongo.KeyPurpose;
|
||||
import com.sparrowwallet.drongo.Network;
|
||||
import com.sparrowwallet.drongo.SecureString;
|
||||
import com.sparrowwallet.drongo.address.Address;
|
||||
import com.sparrowwallet.drongo.address.InvalidAddressException;
|
||||
import com.sparrowwallet.drongo.crypto.*;
|
||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||
import com.sparrowwallet.drongo.wallet.*;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
|
@ -15,6 +17,7 @@ import com.sparrowwallet.sparrow.EventManager;
|
|||
import com.sparrowwallet.sparrow.control.*;
|
||||
import com.sparrowwallet.sparrow.event.*;
|
||||
import com.sparrowwallet.sparrow.io.Config;
|
||||
import com.sparrowwallet.sparrow.io.Storage;
|
||||
import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
|
||||
import com.sparrowwallet.sparrow.whirlpool.WhirlpoolDialog;
|
||||
import javafx.application.Platform;
|
||||
|
@ -160,17 +163,70 @@ public class UtxosController extends WalletFormController implements Initializab
|
|||
List<UtxoEntry> selectedEntries = getSelectedUtxos();
|
||||
WhirlpoolDialog whirlpoolDialog = new WhirlpoolDialog(getWalletForm().getWalletId(), getWalletForm().getWallet(), selectedEntries);
|
||||
Optional<Tx0Preview> optTx0Preview = whirlpoolDialog.showAndWait();
|
||||
optTx0Preview.ifPresent(tx0Preview -> previewPremixTransaction(getWalletForm().getWallet(), tx0Preview, selectedEntries));
|
||||
optTx0Preview.ifPresent(tx0Preview -> previewPremix(tx0Preview, selectedEntries));
|
||||
}
|
||||
|
||||
public void previewPremixTransaction(Wallet wallet, Tx0Preview tx0Preview, List<UtxoEntry> utxoEntries) {
|
||||
public void previewPremix(Tx0Preview tx0Preview, List<UtxoEntry> utxoEntries) {
|
||||
Wallet wallet = getWalletForm().getWallet();
|
||||
String walletId = walletForm.getWalletId();
|
||||
|
||||
if(!wallet.isWhirlpoolMasterWallet() && wallet.isEncrypted()) {
|
||||
WalletPasswordDialog dlg = new WalletPasswordDialog(wallet.getMasterName(), WalletPasswordDialog.PasswordRequirement.LOAD);
|
||||
Optional<SecureString> password = dlg.showAndWait();
|
||||
if(password.isPresent()) {
|
||||
Storage.KeyDerivationService keyDerivationService = new Storage.KeyDerivationService(walletForm.getStorage(), password.get());
|
||||
keyDerivationService.setOnSucceeded(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Done"));
|
||||
ECKey encryptionFullKey = keyDerivationService.getValue();
|
||||
Key key = new Key(encryptionFullKey.getPrivKeyBytes(), walletForm.getStorage().getKeyDeriver().getSalt(), EncryptionType.Deriver.ARGON2);
|
||||
wallet.decrypt(key);
|
||||
|
||||
try {
|
||||
prepareWhirlpoolWallet(wallet);
|
||||
} finally {
|
||||
wallet.encrypt(key);
|
||||
for(Wallet childWallet : wallet.getChildWallets()) {
|
||||
if(!childWallet.isEncrypted()) {
|
||||
childWallet.encrypt(key);
|
||||
}
|
||||
}
|
||||
key.clear();
|
||||
encryptionFullKey.clear();
|
||||
password.get().clear();
|
||||
}
|
||||
|
||||
previewPremix(wallet, tx0Preview, utxoEntries);
|
||||
});
|
||||
keyDerivationService.setOnFailed(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.END, "Failed"));
|
||||
AppServices.showErrorDialog("Incorrect Password", keyDerivationService.getException().getMessage());
|
||||
});
|
||||
EventManager.get().post(new StorageEvent(walletId, TimedEvent.Action.START, "Decrypting wallet..."));
|
||||
keyDerivationService.start();
|
||||
}
|
||||
} else {
|
||||
if(!wallet.isWhirlpoolMasterWallet()) {
|
||||
prepareWhirlpoolWallet(wallet);
|
||||
}
|
||||
|
||||
previewPremix(wallet, tx0Preview, utxoEntries);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareWhirlpoolWallet(Wallet decryptedWallet) {
|
||||
Whirlpool whirlpool = AppServices.get().getWhirlpool(getWalletForm().getWalletId());
|
||||
whirlpool.setScode(Config.get().getScode());
|
||||
whirlpool.setHDWallet(getWalletForm().getWalletId(), decryptedWallet);
|
||||
|
||||
for(StandardAccount whirlpoolAccount : StandardAccount.WHIRLPOOL_ACCOUNTS) {
|
||||
if(wallet.getChildWallet(whirlpoolAccount) == null) {
|
||||
Wallet childWallet = wallet.addChildWallet(whirlpoolAccount);
|
||||
EventManager.get().post(new ChildWalletAddedEvent(getWalletForm().getStorage(), wallet, childWallet));
|
||||
if(decryptedWallet.getChildWallet(whirlpoolAccount) == null) {
|
||||
Wallet childWallet = decryptedWallet.addChildWallet(whirlpoolAccount);
|
||||
EventManager.get().post(new ChildWalletAddedEvent(getWalletForm().getStorage(), decryptedWallet, childWallet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void previewPremix(Wallet wallet, Tx0Preview tx0Preview, List<UtxoEntry> utxoEntries) {
|
||||
Wallet premixWallet = wallet.getChildWallet(StandardAccount.WHIRLPOOL_PREMIX);
|
||||
Wallet badbankWallet = wallet.getChildWallet(StandardAccount.WHIRLPOOL_BADBANK);
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ public class Whirlpool {
|
|||
private HD_Wallet hdWallet;
|
||||
private String walletId;
|
||||
|
||||
private BooleanProperty mixingProperty = new SimpleBooleanProperty(false);
|
||||
private final BooleanProperty mixingProperty = new SimpleBooleanProperty(false);
|
||||
|
||||
public Whirlpool(Network network, HostAndPort torProxy, String sCode) {
|
||||
this.torProxy = torProxy;
|
||||
|
@ -134,14 +134,8 @@ public class Whirlpool {
|
|||
}
|
||||
|
||||
private Tx0ParamService getTx0ParamService() {
|
||||
try {
|
||||
SparrowMinerFeeSupplier minerFeeSupplier = SparrowMinerFeeSupplier.getInstance();
|
||||
return new Tx0ParamService(minerFeeSupplier, config);
|
||||
} catch(Exception e) {
|
||||
log.error("Error fetching miner fees", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
SparrowMinerFeeSupplier minerFeeSupplier = SparrowMinerFeeSupplier.getInstance();
|
||||
return new Tx0ParamService(minerFeeSupplier, config);
|
||||
}
|
||||
|
||||
public void setHDWallet(String walletId, Wallet wallet) {
|
||||
|
|
|
@ -9,8 +9,8 @@ import com.samourai.whirlpool.client.wallet.data.walletState.WalletStateSupplier
|
|||
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowWalletStateSupplier;
|
||||
|
||||
public class SparrowDataPersister implements DataPersister {
|
||||
private WalletStateSupplier walletStateSupplier;
|
||||
private UtxoConfigSupplier utxoConfigSupplier;
|
||||
private final WalletStateSupplier walletStateSupplier;
|
||||
private final UtxoConfigSupplier utxoConfigSupplier;
|
||||
|
||||
public SparrowDataPersister(WhirlpoolWallet whirlpoolWallet) throws Exception {
|
||||
WhirlpoolWalletConfig config = whirlpoolWallet.getConfig();
|
||||
|
|
|
@ -4,8 +4,8 @@ import com.samourai.wallet.client.indexHandler.AbstractIndexHandler;
|
|||
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||
|
||||
public class SparrowIndexHandler extends AbstractIndexHandler {
|
||||
private WalletNode walletNode;
|
||||
private int defaultValue;
|
||||
private final WalletNode walletNode;
|
||||
private final int defaultValue;
|
||||
|
||||
public SparrowIndexHandler(WalletNode walletNode) {
|
||||
this(walletNode, 0);
|
||||
|
@ -19,8 +19,7 @@ public class SparrowIndexHandler extends AbstractIndexHandler {
|
|||
@Override
|
||||
public synchronized int get() {
|
||||
Integer currentIndex = walletNode.getHighestUsedIndex();
|
||||
int nextIndex = currentIndex == null ? defaultValue : currentIndex + 1;
|
||||
return nextIndex;
|
||||
return currentIndex == null ? defaultValue : currentIndex + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,13 +15,13 @@ import java.util.LinkedHashMap;
|
|||
import java.util.Map;
|
||||
|
||||
public class SparrowWalletStateSupplier implements WalletStateSupplier {
|
||||
private String walletId;
|
||||
private Map<String, IIndexHandler> indexHandlerWallets;
|
||||
private final String walletId;
|
||||
private final Map<String, IIndexHandler> indexHandlerWallets;
|
||||
// private int externalIndexDefault;
|
||||
|
||||
public SparrowWalletStateSupplier(String walletId, ExternalDestination externalDestination) throws Exception {
|
||||
this.walletId = walletId;
|
||||
this.indexHandlerWallets = new LinkedHashMap();
|
||||
this.indexHandlerWallets = new LinkedHashMap<>();
|
||||
// this.externalIndexDefault = externalDestination != null ? externalDestination.getStartIndex() : 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
drop table if exists utxoMixData;
|
||||
create table utxoMixData (id identity not null, hash binary(32) not null, mixesDone integer not null default 0, expired bigint, wallet bigint not null);
|
||||
create table utxoMixData (id identity not null, hash binary(32) not null, mixesDone integer not null default 0, expired bigint, wallet bigint not null);
|
|
@ -176,7 +176,12 @@
|
|||
</ToggleButton>
|
||||
</buttons>
|
||||
</SegmentedButton>
|
||||
<HelpLabel fx:id="privacyAnalysis" />
|
||||
<HelpLabel fx:id="optimizationHelp" helpText="Determines whether to optimize the transaction for low fees or greater privacy" />
|
||||
<Label fx:id="privacyAnalysis" graphicTextGap="5" text="Analysis..." styleClass="help-label">
|
||||
<graphic>
|
||||
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="11" icon="INFO_CIRCLE" />
|
||||
</graphic>
|
||||
</Label>
|
||||
</HBox>
|
||||
<HBox AnchorPane.rightAnchor="10">
|
||||
<Button fx:id="clearButton" text="Clear" cancelButton="true" onAction="#clear" />
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="20" icon="RANDOM" styleClass="title-icon" />
|
||||
</graphic>
|
||||
</Label>
|
||||
<Label text="Choose which pool to use below. You will then be able to preview your premix transaction." wrapText="true" styleClass="content-text" />
|
||||
<Label text="Choose which pool to use below. You will then be able to preview your premix transaction. Your wallet password may be required to add the premix wallet." wrapText="true" styleClass="content-text" />
|
||||
<HBox spacing="20" alignment="CENTER_LEFT">
|
||||
<padding>
|
||||
<Insets top="20" bottom="5" />
|
||||
|
|
Loading…
Reference in a new issue