mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 15:10:44 +01:00
Merge pull request #4957 from sqrrm/sign-single-account
Sign single account
This commit is contained in:
commit
0789180bb0
5 changed files with 89 additions and 75 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue