deduplicate signed keystores on signature

This commit is contained in:
Craig Raw 2020-08-31 10:14:48 +02:00
parent 4d7d51f3f8
commit b808222faa
7 changed files with 43 additions and 26 deletions

2
drongo

@ -1 +1 @@
Subproject commit 55717c31bf8046e558ee90a5997c1de769517813 Subproject commit 10035278543d9ca90b11ae3d396edc3e6131fee2

View file

@ -1,5 +1,6 @@
package com.sparrowwallet.sparrow.control; package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.protocol.TransactionSignature;
import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.KeystoreSignedEvent; import com.sparrowwallet.sparrow.event.KeystoreSignedEvent;
@ -7,8 +8,8 @@ import javafx.animation.KeyFrame;
import javafx.animation.KeyValue; import javafx.animation.KeyValue;
import javafx.animation.Timeline; import javafx.animation.Timeline;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ListChangeListener; import javafx.collections.MapChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableMap;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Orientation; import javafx.geometry.Orientation;
import javafx.geometry.Pos; import javafx.geometry.Pos;
@ -18,6 +19,9 @@ import javafx.scene.layout.StackPane;
import javafx.util.Duration; import javafx.util.Duration;
import org.controlsfx.control.SegmentedBar; import org.controlsfx.control.SegmentedBar;
import java.util.ArrayList;
import java.util.List;
public class SignaturesProgressBar extends SegmentedBar<SignaturesProgressBar.SignatureProgressSegment> { public class SignaturesProgressBar extends SegmentedBar<SignaturesProgressBar.SignatureProgressSegment> {
public SignaturesProgressBar() { public SignaturesProgressBar() {
setOrientation(Orientation.HORIZONTAL); setOrientation(Orientation.HORIZONTAL);
@ -25,10 +29,11 @@ public class SignaturesProgressBar extends SegmentedBar<SignaturesProgressBar.Si
setInfoNodeFactory(segment -> segment.getKeystore() == null ? null : new SignatureProgressSegmentLabel(segment.getKeystore().getLabel())); setInfoNodeFactory(segment -> segment.getKeystore() == null ? null : new SignatureProgressSegmentLabel(segment.getKeystore().getLabel()));
} }
public void initialize(ObservableList<Keystore> signedKeystores, int threshold) { public void initialize(ObservableMap<TransactionSignature, Keystore> signatureKeystoreMap, int threshold) {
getStyleClass().add("signatures-progress-bar"); getStyleClass().add("signatures-progress-bar");
getSegments().clear(); getSegments().clear();
List<Keystore> signedKeystores = new ArrayList<>(signatureKeystoreMap.values());
int numSegments = Math.max(threshold, signedKeystores.size()); int numSegments = Math.max(threshold, signedKeystores.size());
double segmentSize = 100d / numSegments; double segmentSize = 100d / numSegments;
for(int i = 0; i < numSegments; i++) { for(int i = 0; i < numSegments; i++) {
@ -39,19 +44,20 @@ public class SignaturesProgressBar extends SegmentedBar<SignaturesProgressBar.Si
} }
} }
signedKeystores.addListener((ListChangeListener<Keystore>) c -> { signatureKeystoreMap.addListener((MapChangeListener<TransactionSignature, Keystore>) c -> {
int numSegments1 = Math.max(threshold, c.getList().size()); List<Keystore> newSignedKeystores = new ArrayList<>(c.getMap().values());
double newSegmentSize = 100d / numSegments1; int newNumSegments = Math.max(threshold, newSignedKeystores.size());
double newSegmentSize = 100d / newNumSegments;
for(int i = 0; i < numSegments1; i++) { for(int i = 0; i < newNumSegments; i++) {
SignatureProgressSegment segment = null; SignatureProgressSegment segment = null;
if(i < getSegments().size()) { if(i < getSegments().size()) {
segment = getSegments().get(i); segment = getSegments().get(i);
} }
Keystore signedKeystore = null; Keystore signedKeystore = null;
if(i < signedKeystores.size()) { if(i < newSignedKeystores.size()) {
signedKeystore = signedKeystores.get(i); signedKeystore = newSignedKeystores.get(i);
} }
if(segment != null) { if(segment != null) {

View file

@ -405,7 +405,7 @@ public class HeadersController extends TransactionFormController implements Init
updateSignedKeystores(signingWallet); updateSignedKeystores(signingWallet);
int threshold = signingWallet.getDefaultPolicy().getNumSignaturesRequired(); int threshold = signingWallet.getDefaultPolicy().getNumSignaturesRequired();
signaturesProgressBar.initialize(headersForm.getSignedKeystores(), threshold); signaturesProgressBar.initialize(headersForm.getSignatureKeystoreMap(), threshold);
}); });
EventManager.get().post(new RequestOpenWalletsEvent()); EventManager.get().post(new RequestOpenWalletsEvent());
@ -705,12 +705,11 @@ public class HeadersController extends TransactionFormController implements Init
} }
private void updateSignedKeystores(Wallet signingWallet) { private void updateSignedKeystores(Wallet signingWallet) {
Map<PSBTInput, List<Keystore>> signedKeystoresMap = signingWallet.getSignedKeystores(headersForm.getPsbt()); Map<PSBTInput, Map<TransactionSignature, Keystore>> signedKeystoresMap = signingWallet.getSignedKeystores(headersForm.getPsbt());
Optional<List<Keystore>> optSignedKeystores = signedKeystoresMap.values().stream().filter(list -> !list.isEmpty()).min(Comparator.comparingInt(List::size)); Optional<Map<TransactionSignature, Keystore>> optSignedKeystores = signedKeystoresMap.values().stream().filter(map -> !map.isEmpty()).min(Comparator.comparingInt(Map::size));
optSignedKeystores.ifPresent(signedKeystores -> { optSignedKeystores.ifPresent(signedKeystores -> {
List<Keystore> newSignedKeystores = new ArrayList<>(signedKeystores); headersForm.getSignatureKeystoreMap().keySet().retainAll(signedKeystores.keySet());
newSignedKeystores.removeAll(headersForm.getSignedKeystores()); headersForm.getSignatureKeystoreMap().putAll(signedKeystores);
headersForm.getSignedKeystores().addAll(newSignedKeystores);
}); });
} }
@ -922,6 +921,7 @@ public class HeadersController extends TransactionFormController implements Init
@Subscribe @Subscribe
public void keystoreSigned(KeystoreSignedEvent event) { public void keystoreSigned(KeystoreSignedEvent event) {
if(headersForm.getSignedKeystores().contains(event.getKeystore()) && headersForm.getPsbt() != null) { if(headersForm.getSignedKeystores().contains(event.getKeystore()) && headersForm.getPsbt() != null) {
//Attempt to finalize PSBT - will do nothing if all inputs are not signed
finalizePSBT(); finalizePSBT();
} }
} }

View file

@ -11,7 +11,7 @@ import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.*; import com.sparrowwallet.sparrow.control.*;
import com.sparrowwallet.sparrow.event.*; import com.sparrowwallet.sparrow.event.*;
import javafx.collections.ListChangeListener; import javafx.collections.MapChangeListener;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.*; import javafx.scene.control.*;
@ -126,7 +126,7 @@ public class InputController extends TransactionFormController implements Initia
initializeLocktimeFields(txInput); initializeLocktimeFields(txInput);
if(psbtInput != null) { if(psbtInput != null) {
inputForm.getSignedKeystores().addListener((ListChangeListener<Keystore>) c -> { inputForm.getSignatureKeystoreMap().addListener((MapChangeListener<TransactionSignature, Keystore>) c -> {
updateSignatures(inputForm.getPsbtInput()); updateSignatures(inputForm.getPsbtInput());
}); });
} }

View file

@ -13,7 +13,7 @@ import com.sparrowwallet.sparrow.event.BitcoinUnitChangedEvent;
import com.sparrowwallet.sparrow.event.BlockTransactionFetchedEvent; import com.sparrowwallet.sparrow.event.BlockTransactionFetchedEvent;
import com.sparrowwallet.sparrow.event.PSBTCombinedEvent; import com.sparrowwallet.sparrow.event.PSBTCombinedEvent;
import com.sparrowwallet.sparrow.event.PSBTFinalizedEvent; import com.sparrowwallet.sparrow.event.PSBTFinalizedEvent;
import javafx.collections.ListChangeListener; import javafx.collections.MapChangeListener;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.chart.PieChart; import javafx.scene.chart.PieChart;
@ -62,7 +62,7 @@ public class InputsController extends TransactionFormController implements Initi
if(inputsForm.getPsbt() != null) { if(inputsForm.getPsbt() != null) {
updatePSBTInputs(inputsForm.getPsbt()); updatePSBTInputs(inputsForm.getPsbt());
inputsForm.getSignedKeystores().addListener((ListChangeListener<Keystore>) c -> { inputsForm.getSignatureKeystoreMap().addListener((MapChangeListener<TransactionSignature, Keystore>) c -> {
updatePSBTInputs(inputsForm.getPsbt()); updatePSBTInputs(inputsForm.getPsbt());
}); });
} else if(inputsForm.getInputTransactions() != null) { } else if(inputsForm.getInputTransactions() != null) {

View file

@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.transaction;
import com.sparrowwallet.drongo.protocol.Sha256Hash; import com.sparrowwallet.drongo.protocol.Sha256Hash;
import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.protocol.TransactionSignature;
import com.sparrowwallet.drongo.psbt.PSBT; import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.wallet.BlockTransaction; import com.sparrowwallet.drongo.wallet.BlockTransaction;
import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.Keystore;
@ -9,9 +10,10 @@ import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.io.Storage; import com.sparrowwallet.sparrow.io.Storage;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap; import javafx.collections.ObservableMap;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -30,7 +32,7 @@ public class TransactionData {
private final ObservableMap<Wallet, Storage> availableWallets = FXCollections.observableHashMap(); private final ObservableMap<Wallet, Storage> availableWallets = FXCollections.observableHashMap();
private final SimpleObjectProperty<Wallet> signingWallet = new SimpleObjectProperty<>(this, "signingWallet", null); private final SimpleObjectProperty<Wallet> signingWallet = new SimpleObjectProperty<>(this, "signingWallet", null);
private final ObservableList<Keystore> signedKeystores = FXCollections.observableArrayList(); private final ObservableMap<TransactionSignature, Keystore> signatureKeystoreMap = FXCollections.observableMap(new LinkedHashMap<>());
public TransactionData(String name, PSBT psbt) { public TransactionData(String name, PSBT psbt) {
this(name, psbt.getTransaction()); this(name, psbt.getTransaction());
@ -147,7 +149,11 @@ public class TransactionData {
this.signingWallet.set(wallet); this.signingWallet.set(wallet);
} }
public ObservableList<Keystore> getSignedKeystores() { public ObservableMap<TransactionSignature, Keystore> getSignatureKeystoreMap() {
return signedKeystores; return signatureKeystoreMap;
}
public Collection<Keystore> getSignedKeystores() {
return signatureKeystoreMap.values();
} }
} }

View file

@ -2,17 +2,18 @@ package com.sparrowwallet.sparrow.transaction;
import com.sparrowwallet.drongo.protocol.Sha256Hash; import com.sparrowwallet.drongo.protocol.Sha256Hash;
import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.protocol.TransactionSignature;
import com.sparrowwallet.drongo.psbt.PSBT; import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.wallet.BlockTransaction; import com.sparrowwallet.drongo.wallet.BlockTransaction;
import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.io.Storage; import com.sparrowwallet.sparrow.io.Storage;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap; import javafx.collections.ObservableMap;
import javafx.scene.Node; import javafx.scene.Node;
import java.io.IOException; import java.io.IOException;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -83,7 +84,11 @@ public abstract class TransactionForm {
txdata.setSigningWallet(signingWallet); txdata.setSigningWallet(signingWallet);
} }
public ObservableList<Keystore> getSignedKeystores() { public ObservableMap<TransactionSignature, Keystore> getSignatureKeystoreMap() {
return txdata.getSignatureKeystoreMap();
}
public Collection<Keystore> getSignedKeystores() {
return txdata.getSignedKeystores(); return txdata.getSignedKeystores();
} }