Skip to content

Commit

Permalink
Merge pull request #4957 from sqrrm/sign-single-account
Browse files Browse the repository at this point in the history
Sign single account
  • Loading branch information
ripcurlx authored Dec 16, 2020
2 parents 4cdd94a + 26024bd commit 0789180
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import bisq.common.crypto.Sig;
import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.util.MathUtils;
import bisq.common.util.Tuple2;
import bisq.common.util.Utilities;

import org.bitcoinj.core.Coin;
Expand Down Expand Up @@ -880,4 +881,29 @@ public boolean isSignWitnessTrade(Trade trade) {
!peerHasSignedWitness(trade) &&
tradeAmountIsSufficient(trade.getTradeAmount());
}

public String getSignInfoFromAccount(PaymentAccount paymentAccount) {
var pubKey = keyRing.getSignatureKeyPair().getPublic();
var witness = getMyWitness(paymentAccount.getPaymentAccountPayload());
return Utilities.bytesAsHexString(witness.getHash()) + "," + Utilities.bytesAsHexString(pubKey.getEncoded());
}

public Tuple2<AccountAgeWitness, byte[]> getSignInfoFromString(String signInfo) {
var parts = signInfo.split(",");
if (parts.length != 2) {
return null;
}
byte[] pubKeyHash;
Optional<AccountAgeWitness> accountAgeWitness;
try {
var accountAgeWitnessHash = Utilities.decodeFromHex(parts[0]);
pubKeyHash = Utilities.decodeFromHex(parts[1]);
accountAgeWitness = getWitnessByHash(accountAgeWitnessHash);
return accountAgeWitness
.map(ageWitness -> new Tuple2<>(ageWitness, pubKeyHash))
.orElse(null);
} catch (Exception e) {
return null;
}
}
}
5 changes: 1 addition & 4 deletions core/src/main/resources/i18n/displayStrings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2892,15 +2892,12 @@ popup.accountSigning.peerLimitLifted=The initial limit for one of your accounts
popup.accountSigning.peerSigner=One of your accounts is mature enough to sign other payment accounts \
and the initial limit for one of your accounts has been lifted.\n\n{0}

popup.accountSigning.singleAccountSelect.headline=Select account age witness
popup.accountSigning.singleAccountSelect.description=Search for account age witness.
popup.accountSigning.singleAccountSelect.datePicker=Select point of time for signing
popup.accountSigning.singleAccountSelect.headline=Import unsigned account age witness
popup.accountSigning.confirmSingleAccount.headline=Confirm selected account age witness
popup.accountSigning.confirmSingleAccount.selectedHash=Selected witness hash
popup.accountSigning.confirmSingleAccount.button=Sign account age witness
popup.accountSigning.successSingleAccount.description=Witness {0} was signed
popup.accountSigning.successSingleAccount.success.headline=Success
popup.accountSigning.successSingleAccount.signError=Failed to sign witness, {0}

popup.accountSigning.unsignedPubKeys.headline=Unsigned Pubkeys
popup.accountSigning.unsignedPubKeys.sign=Sign Pubkeys
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public void initialize() {
accountAgeWitnessService.getAccountAgeWitnessUtils().logSigners();
} else if (Utilities.isCtrlShiftPressed(KeyCode.U, event)) {
accountAgeWitnessService.getAccountAgeWitnessUtils().logUnsignedSignerPubKeys();
} else if (Utilities.isAltOrCtrlPressed(KeyCode.C, event)) {
copyAccount();
}
};

Expand Down Expand Up @@ -174,4 +176,7 @@ public void updateItem(final PaymentAccount item, boolean empty) {
protected abstract void buildForm();

protected abstract void onSelectAccount(PaymentAccount paymentAccount);

protected void copyAccount() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
import bisq.common.config.Config;
import bisq.common.util.Tuple2;
import bisq.common.util.Tuple3;
import bisq.common.util.Utilities;

import org.bitcoinj.core.Coin;

Expand Down Expand Up @@ -541,5 +542,14 @@ private void removeAccountRows() {
gridRow = 1;
}

@Override
protected void copyAccount() {
var selectedAccount = paymentAccountsListView.getSelectionModel().getSelectedItem();
if (selectedAccount == null) {
return;
}
Utilities.copyToClipboard(accountAgeWitnessService.getSignInfoFromAccount(selectedAccount));
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package bisq.desktop.main.overlays.windows;

import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.BisqTextArea;
import bisq.desktop.components.InputTextField;
import bisq.desktop.main.overlays.Overlay;
import bisq.desktop.main.overlays.popups.Popup;
Expand All @@ -27,24 +28,21 @@
import bisq.core.locale.Res;
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;

import bisq.common.util.Tuple2;
import bisq.common.util.Utilities;

import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Utils;

import javax.inject.Inject;

import com.jfoenix.controls.JFXAutoCompletePopup;

import javafx.scene.control.DatePicker;
import javafx.scene.control.ListCell;
import javafx.scene.control.TextArea;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;

import javafx.geometry.VPos;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Date;

import lombok.extern.slf4j.Slf4j;

Expand All @@ -53,10 +51,7 @@
@Slf4j
public class SignSpecificWitnessWindow extends Overlay<SignSpecificWitnessWindow> {

private InputTextField searchTextField;
private JFXAutoCompletePopup<AccountAgeWitness> searchAutoComplete;
private AccountAgeWitness selectedWitness;
private DatePicker datePicker;
private Tuple2<AccountAgeWitness, byte[]> signInfo;
private InputTextField privateKey;
private final AccountAgeWitnessService accountAgeWitnessService;
private final ArbitratorManager arbitratorManager;
Expand Down Expand Up @@ -89,103 +84,75 @@ public void show() {
}

private void addSelectWitnessContent() {
searchTextField = addInputTextField(gridPane, ++rowIndex,
Res.get("popup.accountSigning.singleAccountSelect.description"));

searchAutoComplete = new JFXAutoCompletePopup<>();
searchAutoComplete.setPrefWidth(400);
searchAutoComplete.getSuggestions().addAll(accountAgeWitnessService.getOrphanSignedWitnesses());
searchAutoComplete.setSuggestionsCellFactory(param -> new ListCell<>() {
@Override
protected void updateItem(AccountAgeWitness item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(Utilities.bytesAsHexString(item.getHash()));
} else {
setText("");
}
TextArea accountInfoText = new BisqTextArea();
accountInfoText.setPrefHeight(270);
accountInfoText.setWrapText(true);
GridPane.setRowIndex(accountInfoText, ++rowIndex);
gridPane.getChildren().add(accountInfoText);

accountInfoText.textProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null || newValue.isEmpty()) {
return;
}
});
searchAutoComplete.setSelectionHandler(event -> {
searchTextField.setText(Utilities.bytesAsHexString(event.getObject().getHash()));
selectedWitness = event.getObject();
if (selectedWitness != null) {
datePicker.setValue(Instant.ofEpochMilli(selectedWitness.getDate()).atZone(
ZoneId.systemDefault()).toLocalDate());
signInfo = accountAgeWitnessService.getSignInfoFromString(newValue);
if (signInfo == null) {
actionButton.setDisable(true);
return;
}
actionButton.setDisable(false);
});

searchTextField.textProperty().addListener(observable -> {
searchAutoComplete.filter(witness -> Utilities.bytesAsHexString(witness.getHash()).startsWith(
searchTextField.getText().toLowerCase()));
if (searchAutoComplete.getFilteredSuggestions().isEmpty()) {
searchAutoComplete.hide();
} else {
searchAutoComplete.show(searchTextField);
}
});

datePicker = addTopLabelDatePicker(gridPane, ++rowIndex,
Res.get("popup.accountSigning.singleAccountSelect.datePicker"),
0).second;
datePicker.setOnAction(e -> updateWitnessSelectionState());
}

private void addECKeyField() {
privateKey = addInputTextField(gridPane, ++rowIndex, Res.get("popup.accountSigning.signAccounts.ECKey"));
actionButton.setDisable(true);
GridPane.setVgrow(privateKey, Priority.ALWAYS);
GridPane.setValignment(privateKey, VPos.TOP);
}

private void updateWitnessSelectionState() {
actionButton.setDisable(selectedWitness == null || datePicker.getValue() == null);
privateKey.textProperty().addListener((observable, oldValue, newValue) -> {
if (checkedArbitratorKey() == null) {
actionButton.setDisable(true);
return;
}
actionButton.setDisable(false);
});
}

private void removeContent() {
removeRowsFromGridPane(gridPane, 1, 3);
rowIndex = 1;
}

private void selectAccountAgeWitness() {
private void importAccountAgeWitness() {
removeContent();
headLineLabel.setText(Res.get("popup.accountSigning.confirmSingleAccount.headline"));
var selectedWitnessTextField = addTopLabelTextField(gridPane, ++rowIndex,
Res.get("popup.accountSigning.confirmSingleAccount.selectedHash")).second;
selectedWitnessTextField.setText(Utilities.bytesAsHexString(selectedWitness.getHash()));
selectedWitnessTextField.setText(Utilities.bytesAsHexString(signInfo.first.getHash()));
addECKeyField();
((AutoTooltipButton) actionButton).updateText(Res.get("popup.accountSigning.confirmSingleAccount.button"));
actionButton.setOnAction(a -> {
var arbitratorKey = arbitratorManager.getRegistrationKey(privateKey.getText());
var arbitratorKey = checkedArbitratorKey();
if (arbitratorKey != null) {
var arbitratorPubKeyAsHex = Utils.HEX.encode(arbitratorKey.getPubKey());
var isKeyValid = arbitratorManager.isPublicKeyInList(arbitratorPubKeyAsHex);
if (isKeyValid) {
var result = accountAgeWitnessService.arbitratorSignOrphanWitness(selectedWitness,
arbitratorKey,
datePicker.getValue().atStartOfDay().toEpochSecond(ZoneOffset.UTC) * 1000);
if (result.isEmpty()) {
addSuccessContent();
} else {
new Popup().error(Res.get("popup.accountSigning.successSingleAccount.signError", result))
.onClose(this::hide).show();
}
}
accountAgeWitnessService.arbitratorSignAccountAgeWitness(signInfo.first,
arbitratorKey,
signInfo.second,
new Date().getTime());
addSuccessContent();
} else {
new Popup().error(Res.get("popup.accountSigning.signAccounts.ECKey.error")).onClose(this::hide).show();
}

});
}


private void addSuccessContent() {
removeContent();
closeButton.setVisible(false);
closeButton.setManaged(false);
headLineLabel.setText(Res.get("popup.accountSigning.successSingleAccount.success.headline"));
var descriptionLabel = addMultilineLabel(gridPane, ++rowIndex,
Res.get("popup.accountSigning.successSingleAccount.description",
Utilities.bytesAsHexString(selectedWitness.getHash())));
Utilities.bytesAsHexString(signInfo.first.getHash())));
GridPane.setVgrow(descriptionLabel, Priority.ALWAYS);
GridPane.setValignment(descriptionLabel, VPos.TOP);
((AutoTooltipButton) actionButton).updateText(Res.get("shared.ok"));
Expand All @@ -194,15 +161,24 @@ private void addSuccessContent() {

@Override
protected void addButtons() {
var buttonTuple = add2ButtonsAfterGroup(gridPane, ++rowIndex + 1,
var buttonTuple = add2ButtonsAfterGroup(gridPane, ++rowIndex + 2,
Res.get("popup.accountSigning.singleAccountSelect.headline"), Res.get("shared.cancel"));

actionButton = buttonTuple.first;
actionButton.setDisable(true);
actionButton.setOnAction(e -> selectAccountAgeWitness());
actionButton.setOnAction(e -> importAccountAgeWitness());

closeButton = (AutoTooltipButton) buttonTuple.second;
closeButton.setOnAction(e -> hide());
}

private ECKey checkedArbitratorKey() {
var arbitratorKey = arbitratorManager.getRegistrationKey(privateKey.getText());
if (arbitratorKey == null) {
return null;
}
var arbitratorPubKeyAsHex = Utils.HEX.encode(arbitratorKey.getPubKey());
var isKeyValid = arbitratorManager.isPublicKeyInList(arbitratorPubKeyAsHex);
return isKeyValid ? arbitratorKey : null;
}
}

0 comments on commit 0789180

Please sign in to comment.