Merge pull request #4957 from sqrrm/sign-single-account

Sign single account
This commit is contained in:
Christoph Atteneder 2020-12-16 14:07:34 +01:00 committed by GitHub
commit 0789180bb0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 89 additions and 75 deletions

View file

@ -51,6 +51,7 @@ import bisq.common.crypto.PubKeyRing;
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;
@ -880,4 +881,29 @@ public class AccountAgeWitnessService {
!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;
}
}
}

View file

@ -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

View file

@ -62,6 +62,8 @@ public abstract class PaymentAccountsView<R extends Node, M extends ActivatableW
accountAgeWitnessService.getAccountAgeWitnessUtils().logSigners();
} else if (Utilities.isCtrlShiftPressed(KeyCode.U, event)) {
accountAgeWitnessService.getAccountAgeWitnessUtils().logUnsignedSignerPubKeys();
} else if (Utilities.isAltOrCtrlPressed(KeyCode.C, event)) {
copyAccount();
}
};
@ -174,4 +176,7 @@ public abstract class PaymentAccountsView<R extends Node, M extends ActivatableW
protected abstract void buildForm();
protected abstract void onSelectAccount(PaymentAccount paymentAccount);
protected void copyAccount() {
}
}

View file

@ -99,6 +99,7 @@ import bisq.core.util.validation.InputValidator;
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;
@ -541,5 +542,14 @@ public class FiatAccountsView extends PaymentAccountsView<GridPane, FiatAccounts
gridRow = 1;
}
@Override
protected void copyAccount() {
var selectedAccount = paymentAccountsListView.getSelectionModel().getSelectedItem();
if (selectedAccount == null) {
return;
}
Utilities.copyToClipboard(accountAgeWitnessService.getSignInfoFromAccount(selectedAccount));
}
}

View file

@ -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;
@ -27,24 +28,21 @@ import bisq.core.account.witness.AccountAgeWitnessService;
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;
@ -53,10 +51,7 @@ import static bisq.desktop.util.FormBuilder.*;
@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;
@ -89,56 +84,37 @@ public class SignSpecificWitnessWindow extends Overlay<SignSpecificWitnessWindow
}
private void addSelectWitnessContent() {
searchTextField = addInputTextField(gridPane, ++rowIndex,
Res.get("popup.accountSigning.singleAccountSelect.description"));
TextArea accountInfoText = new BisqTextArea();
accountInfoText.setPrefHeight(270);
accountInfoText.setWrapText(true);
GridPane.setRowIndex(accountInfoText, ++rowIndex);
gridPane.getChildren().add(accountInfoText);
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("");
}
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() {
@ -146,30 +122,22 @@ public class SignSpecificWitnessWindow extends Overlay<SignSpecificWitnessWindow
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();
}
@ -177,7 +145,6 @@ public class SignSpecificWitnessWindow extends Overlay<SignSpecificWitnessWindow
});
}
private void addSuccessContent() {
removeContent();
closeButton.setVisible(false);
@ -185,7 +152,7 @@ public class SignSpecificWitnessWindow extends Overlay<SignSpecificWitnessWindow
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"));
@ -194,15 +161,24 @@ public class SignSpecificWitnessWindow extends Overlay<SignSpecificWitnessWindow
@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;
}
}