From 279b0e0ac11565e9a4aa785180aef061357dceb9 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Wed, 22 Jul 2020 14:56:59 +0200 Subject: [PATCH] sign psbt inputs --- drongo | 2 +- .../sparrowwallet/sparrow/AppController.java | 6 +- .../sparrow/event/OpenWalletsEvent.java | 19 +++-- .../transaction/HeadersController.java | 71 ++++++++++++++++--- .../sparrow/transaction/TransactionData.java | 7 ++ .../sparrow/transaction/TransactionForm.java | 6 ++ 6 files changed, 93 insertions(+), 18 deletions(-) diff --git a/drongo b/drongo index f6dcdb6d..af562dad 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit f6dcdb6d26c3b40ae1c0f7502b3e526aa8959564 +Subproject commit af562dad1022c24d5ff851100cc3958a852fbec8 diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index f98e1abc..14a4bf7b 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -409,14 +409,14 @@ public class AppController implements Initializable { return fiatCurrencyExchangeRate; } - public List getOpenWallets() { - List openWallets = new ArrayList<>(); + public Map getOpenWallets() { + Map openWallets = new LinkedHashMap<>(); for(Tab tab : tabs.getTabs()) { TabData tabData = (TabData)tab.getUserData(); if(tabData.getType() == TabData.TabType.WALLET) { WalletTabData walletTabData = (WalletTabData) tabData; - openWallets.add(walletTabData.getWallet()); + openWallets.put(walletTabData.getWallet(), walletTabData.getStorage()); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/event/OpenWalletsEvent.java b/src/main/java/com/sparrowwallet/sparrow/event/OpenWalletsEvent.java index b096d349..04cb423e 100644 --- a/src/main/java/com/sparrowwallet/sparrow/event/OpenWalletsEvent.java +++ b/src/main/java/com/sparrowwallet/sparrow/event/OpenWalletsEvent.java @@ -1,17 +1,28 @@ package com.sparrowwallet.sparrow.event; import com.sparrowwallet.drongo.wallet.Wallet; +import com.sparrowwallet.sparrow.io.Storage; +import java.util.ArrayList; import java.util.List; +import java.util.Map; public class OpenWalletsEvent { - private final List wallets; + private final Map walletsMap; - public OpenWalletsEvent(List wallets) { - this.wallets = wallets; + public OpenWalletsEvent(Map walletsMap) { + this.walletsMap = walletsMap; } public List getWallets() { - return wallets; + return new ArrayList<>(walletsMap.keySet()); + } + + public Storage getStorage(Wallet wallet) { + return walletsMap.get(wallet); + } + + public Map getWalletsMap() { + return walletsMap; } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java index 815d8079..ef4f53fc 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java @@ -1,5 +1,6 @@ package com.sparrowwallet.sparrow.transaction; +import com.sparrowwallet.drongo.SecureString; import com.sparrowwallet.drongo.protocol.*; import com.sparrowwallet.drongo.psbt.PSBT; import com.sparrowwallet.drongo.psbt.PSBTInput; @@ -9,12 +10,10 @@ import com.sparrowwallet.drongo.wallet.KeystoreSource; import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.AppController; import com.sparrowwallet.sparrow.EventManager; -import com.sparrowwallet.sparrow.control.CoinLabel; -import com.sparrowwallet.sparrow.control.IdLabel; -import com.sparrowwallet.sparrow.control.CopyableLabel; -import com.sparrowwallet.sparrow.control.SignaturesProgressBar; +import com.sparrowwallet.sparrow.control.*; import com.sparrowwallet.sparrow.event.*; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands; +import com.sparrowwallet.sparrow.io.Storage; import javafx.collections.FXCollections; import javafx.event.ActionEvent; import javafx.fxml.FXML; @@ -29,6 +28,7 @@ import tornadofx.control.Fieldset; import com.google.common.eventbus.Subscribe; import tornadofx.control.Form; +import java.io.File; import java.net.URL; import java.text.SimpleDateFormat; import java.time.*; @@ -315,10 +315,7 @@ public class HeadersController extends TransactionFormController implements Init headersForm.signingWalletProperty().addListener((observable, oldValue, signingWallet) -> { initializeSignButton(signingWallet); - - Map> signedKeystoresMap = signingWallet.getSignedKeystores(headersForm.getPsbt()); - Optional> optSignedKeystores = signedKeystoresMap.values().stream().filter(list -> !list.isEmpty()).min(Comparator.comparingInt(List::size)); - optSignedKeystores.ifPresent(keystores -> headersForm.getSignedKeystores().setAll(keystores)); + updateSignedKeystores(signingWallet); int threshold = signingWallet.getDefaultPolicy().getNumSignaturesRequired(); signaturesProgressBar.initialize(headersForm.getSignedKeystores(), threshold); @@ -437,7 +434,7 @@ public class HeadersController extends TransactionFormController implements Init } public void showPSBT(ActionEvent event) { - + headersForm.getSignedKeystores().add(headersForm.getSigningWallet().getKeystores().get(0)); } public void savePSBT(ActionEvent event) { @@ -445,7 +442,56 @@ public class HeadersController extends TransactionFormController implements Init } public void signPSBT(ActionEvent event) { - headersForm.getSignedKeystores().add(headersForm.getSigningWallet().getKeystores().get(0)); + signSoftwareKeystores(); + } + + private void signSoftwareKeystores() { + if(headersForm.getSigningWallet().getKeystores().stream().noneMatch(Keystore::hasSeed)) { + return; + } + + Wallet copy = headersForm.getSigningWallet().copy(); + File file = headersForm.getAvailableWallets().get(headersForm.getSigningWallet()).getWalletFile(); + + if(copy.isEncrypted()) { + WalletPasswordDialog dlg = new WalletPasswordDialog(WalletPasswordDialog.PasswordRequirement.LOAD); + Optional password = dlg.showAndWait(); + if(password.isPresent()) { + Storage.DecryptWalletService decryptWalletService = new Storage.DecryptWalletService(copy, password.get()); + decryptWalletService.setOnSucceeded(workerStateEvent -> { + EventManager.get().post(new StorageEvent(file, TimedEvent.Action.END, "Done")); + Wallet decryptedWallet = decryptWalletService.getValue(); + signUnencryptedKeystores(decryptedWallet); + }); + decryptWalletService.setOnFailed(workerStateEvent -> { + EventManager.get().post(new StorageEvent(file, TimedEvent.Action.END, "Failed")); + AppController.showErrorDialog("Incorrect Password", decryptWalletService.getException().getMessage()); + }); + EventManager.get().post(new StorageEvent(file, TimedEvent.Action.START, "Decrypting wallet...")); + decryptWalletService.start(); + } + } else { + signUnencryptedKeystores(copy); + } + } + + private void signUnencryptedKeystores(Wallet unencryptedWallet) { + try { + unencryptedWallet.sign(headersForm.getPsbt()); + updateSignedKeystores(headersForm.getSigningWallet()); + } catch(Exception e) { + AppController.showErrorDialog("Failed to Sign", e.getMessage()); + } + } + + private void updateSignedKeystores(Wallet signingWallet) { + Map> signedKeystoresMap = signingWallet.getSignedKeystores(headersForm.getPsbt()); + Optional> optSignedKeystores = signedKeystoresMap.values().stream().filter(list -> !list.isEmpty()).min(Comparator.comparingInt(List::size)); + optSignedKeystores.ifPresent(signedKeystores -> { + List newSignedKeystores = new ArrayList<>(signedKeystores); + newSignedKeystores.removeAll(headersForm.getSignedKeystores()); + headersForm.getSignedKeystores().addAll(newSignedKeystores); + }); } @Subscribe @@ -484,6 +530,11 @@ public class HeadersController extends TransactionFormController implements Init public void openWallets(OpenWalletsEvent event) { if(headersForm.getPsbt() != null && headersForm.isEditable()) { List availableWallets = event.getWallets().stream().filter(wallet -> wallet.canSign(headersForm.getPsbt())).collect(Collectors.toList()); + Map availableWalletsMap = new LinkedHashMap<>(event.getWalletsMap()); + availableWalletsMap.keySet().retainAll(availableWallets); + headersForm.getAvailableWallets().keySet().retainAll(availableWallets); + headersForm.getAvailableWallets().putAll(availableWalletsMap); + signingWallet.setItems(FXCollections.observableList(availableWallets)); if(!availableWallets.isEmpty()) { if(availableWallets.contains(headersForm.getSigningWallet())) { diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionData.java b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionData.java index 1f0b3bdb..f5053b2a 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionData.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionData.java @@ -6,9 +6,11 @@ import com.sparrowwallet.drongo.psbt.PSBT; import com.sparrowwallet.drongo.wallet.BlockTransaction; import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.Wallet; +import com.sparrowwallet.sparrow.io.Storage; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.collections.ObservableMap; import java.util.List; import java.util.Map; @@ -25,6 +27,7 @@ public class TransactionData { private int minOutputFetched; private int maxOutputFetched; + private final ObservableMap availableWallets = FXCollections.observableHashMap(); private final SimpleObjectProperty signingWallet = new SimpleObjectProperty<>(this, "signingWallet", null); private final ObservableList signedKeystores = FXCollections.observableArrayList(); @@ -118,6 +121,10 @@ public class TransactionData { return minOutputFetched == 0 && maxOutputFetched == transaction.getOutputs().size(); } + public ObservableMap getAvailableWallets() { + return availableWallets; + } + public Wallet getSigningWallet() { return signingWallet.get(); } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java index 1ef22d9a..2acf2cea 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java @@ -6,8 +6,10 @@ import com.sparrowwallet.drongo.psbt.PSBT; import com.sparrowwallet.drongo.wallet.BlockTransaction; import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.Wallet; +import com.sparrowwallet.sparrow.io.Storage; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.ObservableList; +import javafx.collections.ObservableMap; import javafx.scene.Node; import java.io.IOException; @@ -57,6 +59,10 @@ public abstract class TransactionForm { return txdata.allOutputsFetched(); } + public ObservableMap getAvailableWallets() { + return txdata.getAvailableWallets(); + } + public Wallet getSigningWallet() { return txdata.getSigningWallet(); }