From fba329de9ac73e64960f1670626c6f55466434e4 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Sun, 19 Apr 2020 18:41:22 +0200 Subject: [PATCH 1/7] Arbitrator sign single account age witness --- .../account/sign/SignedWitnessService.java | 40 +++- .../witness/AccountAgeWitnessService.java | 14 +- .../resources/i18n/displayStrings.properties | 11 + .../windows/SignSpecificWitnessWindow.java | 207 ++++++++++++++++++ .../agent/arbitration/ArbitratorView.java | 8 +- 5 files changed, 271 insertions(+), 9 deletions(-) create mode 100644 desktop/src/main/java/bisq/desktop/main/overlays/windows/SignSpecificWitnessWindow.java diff --git a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java index 6552ab1b054..521caee50e9 100644 --- a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java +++ b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java @@ -176,7 +176,14 @@ public boolean isFilteredWitness(AccountAgeWitness accountAgeWitness) { .anyMatch(ownerPubKey -> filterManager.isSignerPubKeyBanned(Utils.HEX.encode(ownerPubKey))); } - public String ownerPubKey(AccountAgeWitness accountAgeWitness) { + private byte[] ownerPubKey(AccountAgeWitness accountAgeWitness) { + return getSignedWitnessSet(accountAgeWitness).stream() + .map(SignedWitness::getWitnessOwnerPubKey) + .findFirst() + .orElse(null); + } + + public String ownerPubKeyAsString(AccountAgeWitness accountAgeWitness) { return getSignedWitnessSet(accountAgeWitness).stream() .map(signedWitness -> Utils.HEX.encode(signedWitness.getWitnessOwnerPubKey())) .findFirst() @@ -195,9 +202,31 @@ public void signAccountAgeWitness(Coin tradeAmount, AccountAgeWitness accountAgeWitness, ECKey key, PublicKey peersPubKey) { + signAccountAgeWitness(tradeAmount, accountAgeWitness, key, peersPubKey.getEncoded(), new Date().getTime()); + } + + public String signAccountAgeWitness(AccountAgeWitness accountAgeWitness, + ECKey key, + long time) { + return signAccountAgeWitness(MINIMUM_TRADE_AMOUNT_FOR_SIGNING, accountAgeWitness, key, + ownerPubKey(accountAgeWitness), time); + } + + // Arbitrators sign with EC key + private String signAccountAgeWitness(Coin tradeAmount, + AccountAgeWitness accountAgeWitness, + ECKey key, + byte[] peersPubKey, + long time) { if (isSignedAccountAgeWitness(accountAgeWitness)) { - log.warn("Arbitrator trying to sign already signed accountagewitness {}", accountAgeWitness.toString()); - return; + var err = "Arbitrator trying to sign already signed accountagewitness " + accountAgeWitness.toString(); + log.warn(err); + return err; + } + if (peersPubKey == null) { + var err = "Trying to sign accountAgeWitness " + accountAgeWitness.toString() + "\nwith owner pubkey=null"; + log.warn(err); + return err; } String accountAgeWitnessHashAsHex = Utilities.encodeToHex(accountAgeWitness.getHash()); @@ -206,11 +235,12 @@ public void signAccountAgeWitness(Coin tradeAmount, accountAgeWitness.getHash(), signatureBase64.getBytes(Charsets.UTF_8), key.getPubKey(), - peersPubKey.getEncoded(), - new Date().getTime(), + peersPubKey, + time, tradeAmount.value); publishSignedWitness(signedWitness); log.info("Arbitrator signed witness {}", signedWitness.toString()); + return ""; } // Any peer can sign with DSA key diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index f56b27669c0..fb7b352fb7a 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -33,7 +33,6 @@ import bisq.core.support.dispute.Dispute; import bisq.core.support.dispute.DisputeResult; import bisq.core.support.dispute.arbitration.TraderDataItem; -import bisq.core.trade.Contract; import bisq.core.trade.Trade; import bisq.core.trade.protocol.TradingPeer; import bisq.core.user.User; @@ -76,6 +75,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; @@ -131,6 +131,7 @@ public String getPresentation() { private final ChargeBackRisk chargeBackRisk; private final FilterManager filterManager; + @Getter private final Map accountAgeWitnessMap = new HashMap<>(); @@ -219,7 +220,7 @@ public void publishMyAccountAgeWitness(PaymentAccountPayload paymentAccountPaylo public byte[] getPeerAccountAgeWitnessHash(Trade trade) { return findTradePeerWitness(trade) - .map(accountAgeWitness -> accountAgeWitness.getHash()) + .map(AccountAgeWitness::getHash) .orElse(null); } @@ -628,6 +629,12 @@ public void arbitratorSignAccountAgeWitness(Coin tradeAmount, signedWitnessService.signAccountAgeWitness(tradeAmount, accountAgeWitness, key, peersPubKey); } + public String arbitratorSignAccountAgeWitness(AccountAgeWitness accountAgeWitness, + ECKey key, + long time) { + return signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, time); + } + public void traderSignPeersAccountAgeWitness(Trade trade) { AccountAgeWitness peersWitness = findTradePeerWitness(trade).orElse(null); Coin tradeAmount = trade.getTradeAmount(); @@ -752,7 +759,7 @@ public SignState getSignState(Trade trade) { public SignState getSignState(AccountAgeWitness accountAgeWitness) { // Add hash to sign state info when running in debug mode String hash = log.isDebugEnabled() ? Utilities.bytesAsHexString(accountAgeWitness.getHash()) + "\n" + - signedWitnessService.ownerPubKey(accountAgeWitness) : ""; + signedWitnessService.ownerPubKeyAsString(accountAgeWitness) : ""; if (signedWitnessService.isFilteredWitness(accountAgeWitness)) { return SignState.BANNED.addHash(hash); } @@ -776,6 +783,7 @@ public SignState getSignState(AccountAgeWitness accountAgeWitness) { /////////////////////////////////////////////////////////////////////////////////////////// // Debug logs /////////////////////////////////////////////////////////////////////////////////////////// + private String getWitnessDebugLog(PaymentAccountPayload paymentAccountPayload, PubKeyRing pubKeyRing) { Optional accountAgeWitness = findWitness(paymentAccountPayload, pubKeyRing); diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index bd5fd605eed..fa38884e6b7 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2729,6 +2729,17 @@ 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.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} + + #################################################################### # Notifications #################################################################### diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignSpecificWitnessWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignSpecificWitnessWindow.java new file mode 100644 index 00000000000..bf1d89a88aa --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignSpecificWitnessWindow.java @@ -0,0 +1,207 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.main.overlays.windows; + +import bisq.desktop.components.AutoTooltipButton; +import bisq.desktop.components.InputTextField; +import bisq.desktop.main.overlays.Overlay; +import bisq.desktop.main.overlays.popups.Popup; + +import bisq.core.account.witness.AccountAgeWitness; +import bisq.core.account.witness.AccountAgeWitnessService; +import bisq.core.locale.Res; +import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; + +import bisq.common.util.Utilities; + +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.layout.GridPane; +import javafx.scene.layout.Priority; + +import javafx.geometry.VPos; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; + +import lombok.extern.slf4j.Slf4j; + +import static bisq.desktop.util.FormBuilder.*; + +@Slf4j +public class SignSpecificWitnessWindow extends Overlay { + + private InputTextField searchTextField; + private JFXAutoCompletePopup searchAutoComplete; + private AccountAgeWitness selectedWitness; + private DatePicker datePicker; + private InputTextField privateKey; + private final AccountAgeWitnessService accountAgeWitnessService; + private final ArbitratorManager arbitratorManager; + + + @Inject + public SignSpecificWitnessWindow(AccountAgeWitnessService accountAgeWitnessService, + ArbitratorManager arbitratorManager) { + this.accountAgeWitnessService = accountAgeWitnessService; + this.arbitratorManager = arbitratorManager; + } + + @Override + public void show() { + width = 1000; + rowIndex = -1; + createGridPane(); + + gridPane.setPrefHeight(600); + gridPane.getColumnConstraints().get(1).setHgrow(Priority.NEVER); + headLine(Res.get("popup.accountSigning.singleAccountSelect.headline")); + type = Type.Attention; + + addHeadLine(); + addSelectWitnessContent(); + addButtons(); + applyStyles(); + + display(); + } + + private void addSelectWitnessContent() { + searchTextField = addInputTextField(gridPane, ++rowIndex, + Res.get("popup.accountSigning.singleAccountSelect.description")); + + searchAutoComplete = new JFXAutoCompletePopup<>(); + searchAutoComplete.setPrefWidth(400); + searchAutoComplete.getSuggestions().addAll(accountAgeWitnessService.getAccountAgeWitnessMap().values()); + 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(""); + } + } + }); + 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()); + } + }); + + 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")); + GridPane.setVgrow(privateKey, Priority.ALWAYS); + GridPane.setValignment(privateKey, VPos.TOP); + } + + private void updateWitnessSelectionState() { + actionButton.setDisable(selectedWitness == null || datePicker.getValue() == null); + } + + private void removeContent() { + removeRowsFromGridPane(gridPane, 1, 3); + rowIndex = 1; + } + + private void selectAccountAgeWitness() { + 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())); + addECKeyField(); + ((AutoTooltipButton) actionButton).updateText(Res.get("popup.accountSigning.confirmSingleAccount.button")); + actionButton.setOnAction(a -> { + var arbitratorKey = arbitratorManager.getRegistrationKey(privateKey.getText()); + if (arbitratorKey != null) { + var arbitratorPubKeyAsHex = Utils.HEX.encode(arbitratorKey.getPubKey()); + var isKeyValid = arbitratorManager.isPublicKeyInList(arbitratorPubKeyAsHex); + if (isKeyValid) { + var result = accountAgeWitnessService.arbitratorSignAccountAgeWitness(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(); + } + } + } 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()))); + GridPane.setVgrow(descriptionLabel, Priority.ALWAYS); + GridPane.setValignment(descriptionLabel, VPos.TOP); + ((AutoTooltipButton) actionButton).updateText(Res.get("shared.ok")); + actionButton.setOnAction(a -> hide()); + } + + @Override + protected void addButtons() { + var buttonTuple = add2ButtonsAfterGroup(gridPane, ++rowIndex + 1, + Res.get("popup.accountSigning.singleAccountSelect.headline"), Res.get("shared.cancel")); + + actionButton = buttonTuple.first; + actionButton.setDisable(true); + actionButton.setOnAction(e -> selectAccountAgeWitness()); + + closeButton = (AutoTooltipButton) buttonTuple.second; + closeButton.setOnAction(e -> hide()); + + } +} diff --git a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/arbitration/ArbitratorView.java b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/arbitration/ArbitratorView.java index 05d2852c353..d8d87c73844 100644 --- a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/arbitration/ArbitratorView.java +++ b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/arbitration/ArbitratorView.java @@ -23,6 +23,7 @@ import bisq.desktop.main.overlays.windows.ContractWindow; import bisq.desktop.main.overlays.windows.DisputeSummaryWindow; import bisq.desktop.main.overlays.windows.SignPaymentAccountsWindow; +import bisq.desktop.main.overlays.windows.SignSpecificWitnessWindow; import bisq.desktop.main.overlays.windows.TradeDetailsWindow; import bisq.desktop.main.support.dispute.agent.DisputeAgentView; @@ -49,6 +50,7 @@ public class ArbitratorView extends DisputeAgentView { private final SignPaymentAccountsWindow signPaymentAccountsWindow; + private final SignSpecificWitnessWindow signSpecificWitnessWindow; @Inject public ArbitratorView(ArbitrationManager arbitrationManager, @@ -61,7 +63,8 @@ public ArbitratorView(ArbitrationManager arbitrationManager, TradeDetailsWindow tradeDetailsWindow, AccountAgeWitnessService accountAgeWitnessService, @Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys, - SignPaymentAccountsWindow signPaymentAccountsWindow) { + SignPaymentAccountsWindow signPaymentAccountsWindow, + SignSpecificWitnessWindow signSpecificWitnessWindow) { super(arbitrationManager, keyRing, tradeManager, @@ -73,6 +76,7 @@ public ArbitratorView(ArbitrationManager arbitrationManager, accountAgeWitnessService, useDevPrivilegeKeys); this.signPaymentAccountsWindow = signPaymentAccountsWindow; + this.signSpecificWitnessWindow = signSpecificWitnessWindow; } @Override @@ -89,6 +93,8 @@ protected DisputeSession getConcreteDisputeChatSession(Dispute dispute) { protected void handleKeyPressed(KeyEvent event) { if (Utilities.isAltOrCtrlPressed(KeyCode.S, event)) { signPaymentAccountsWindow.show(); + } else if (Utilities.isAltOrCtrlPressed(KeyCode.P, event)) { + signSpecificWitnessWindow.show(); } } } From 8bf0f3350c54ccfd85f8f984b2eef9668b7dcde6 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Fri, 24 Apr 2020 20:25:23 +0200 Subject: [PATCH 2/7] Test dummy account age witness signing Add orphaned witness log --- .../main/java/bisq/common/util/Utilities.java | 11 ++ .../bisq/core/account/sign/SignedWitness.java | 2 +- .../account/sign/SignedWitnessService.java | 17 +- .../witness/AccountAgeWitnessService.java | 49 +++++- .../witness/AccountAgeWitnessServiceTest.java | 150 +++++++++++++++--- 5 files changed, 202 insertions(+), 27 deletions(-) diff --git a/common/src/main/java/bisq/common/util/Utilities.java b/common/src/main/java/bisq/common/util/Utilities.java index 7947157bc7d..f958bdd9b99 100644 --- a/common/src/main/java/bisq/common/util/Utilities.java +++ b/common/src/main/java/bisq/common/util/Utilities.java @@ -56,15 +56,19 @@ import java.util.GregorianCalendar; import java.util.HashSet; import java.util.Locale; +import java.util.Map; import java.util.Random; import java.util.Set; import java.util.TimeZone; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @@ -452,4 +456,11 @@ public static int byteArrayToInteger(byte[] bytes) { } return result; } + + // Helper to filter unique elements by key + public static Predicate distinctByKey(Function keyExtractor) + { + Map map = new ConcurrentHashMap<>(); + return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; + } } diff --git a/core/src/main/java/bisq/core/account/sign/SignedWitness.java b/core/src/main/java/bisq/core/account/sign/SignedWitness.java index 975563623a5..94c1cb11a3a 100644 --- a/core/src/main/java/bisq/core/account/sign/SignedWitness.java +++ b/core/src/main/java/bisq/core/account/sign/SignedWitness.java @@ -168,7 +168,7 @@ public boolean isSignedByArbitrator() { // Getters /////////////////////////////////////////////////////////////////////////////////////////// - P2PDataStorage.ByteArray getHashAsByteArray() { + public P2PDataStorage.ByteArray getHashAsByteArray() { return new P2PDataStorage.ByteArray(hash); } diff --git a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java index 521caee50e9..43a11a7f121 100644 --- a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java +++ b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java @@ -58,19 +58,21 @@ import java.util.Stack; import java.util.stream.Collectors; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; @Slf4j public class SignedWitnessService { public static final long SIGNER_AGE_DAYS = 30; private static final long SIGNER_AGE = SIGNER_AGE_DAYS * ChronoUnit.DAYS.getDuration().toMillis(); - static final Coin MINIMUM_TRADE_AMOUNT_FOR_SIGNING = Coin.parseCoin("0.0025"); + public static final Coin MINIMUM_TRADE_AMOUNT_FOR_SIGNING = Coin.parseCoin("0.0025"); private final KeyRing keyRing; private final P2PService p2PService; private final ArbitratorManager arbitratorManager; private final User user; + @Getter private final Map signedWitnessMap = new HashMap<>(); private final FilterManager filterManager; @@ -205,11 +207,13 @@ public void signAccountAgeWitness(Coin tradeAmount, signAccountAgeWitness(tradeAmount, accountAgeWitness, key, peersPubKey.getEncoded(), new Date().getTime()); } + // Arbitrators sign with EC key public String signAccountAgeWitness(AccountAgeWitness accountAgeWitness, ECKey key, + byte[] peersPubKey, long time) { - return signAccountAgeWitness(MINIMUM_TRADE_AMOUNT_FOR_SIGNING, accountAgeWitness, key, - ownerPubKey(accountAgeWitness), time); + var witnessPubKey = peersPubKey == null ? ownerPubKey(accountAgeWitness) : peersPubKey; + return signAccountAgeWitness(MINIMUM_TRADE_AMOUNT_FOR_SIGNING, accountAgeWitness, key, witnessPubKey, time); } // Arbitrators sign with EC key @@ -330,6 +334,13 @@ public Set getTrustedPeerSignedWitnessSet(AccountAgeWitness accou .collect(Collectors.toSet()); } + public Set getOrphanSignedWitnessSet() { + return signedWitnessMap.values().stream() + .filter(witness -> getSignedWitnessSetByOwnerPubKey(witness.getSignerPubKey(), new Stack<>()).isEmpty()) + .filter(witness -> witness.getVerificationMethod() != SignedWitness.VerificationMethod.ARBITRATOR) + .collect(Collectors.toSet()); + } + // We go one level up by using the signer Key to lookup for SignedWitness objects which contain the signerKey as // witnessOwnerPubKey private Set getSignedWitnessSetByOwnerPubKey(byte[] ownerPubKey, diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index fb7b352fb7a..10ef74ab60d 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -17,6 +17,7 @@ package bisq.core.account.witness; +import bisq.core.account.sign.SignedWitness; import bisq.core.account.sign.SignedWitnessService; import bisq.core.filter.FilterManager; import bisq.core.filter.PaymentAccountFilter; @@ -66,12 +67,18 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Random; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -630,9 +637,16 @@ public void arbitratorSignAccountAgeWitness(Coin tradeAmount, } public String arbitratorSignAccountAgeWitness(AccountAgeWitness accountAgeWitness, - ECKey key, - long time) { - return signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, time); + ECKey key, + long time) { + return signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, null, time); + } + + public String arbitratorSignAccountAgeWitness(AccountAgeWitness accountAgeWitness, + ECKey key, + byte[] tradersPubKey, + long time) { + return signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, tradersPubKey,time); } public void traderSignPeersAccountAgeWitness(Trade trade) { @@ -780,6 +794,35 @@ public SignState getSignState(AccountAgeWitness accountAgeWitness) { } } + public Set getOrphanSignedWitnesses() { + var orphans = signedWitnessService.getOrphanSignedWitnessSet().stream() + .map(signedWitness -> getWitnessByHash(signedWitness.getAccountAgeWitnessHash()).orElse(null)) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + var orphanSigners = signedWitnessService.getOrphanSignedWitnessSet().stream() + .filter(Utilities.distinctByKey(w -> Utilities.bytesAsHexString(w.getSignerPubKey()))) + .collect(Collectors.toSet()); + var orphanAEW = signedWitnessService.getOrphanSignedWitnessSet(); + log.debug("Orphaned signed account age witnesses:"); + orphanSigners.forEach(w -> { + log.debug("{}: {}", w.getVerificationMethod().toString(), + Utilities.bytesAsHexString(Hash.getRipemd160hash(w.getSignerPubKey()))); + logChild(w, " ", new HashSet<>()); + }); + return orphans; + } + + private void logChild(SignedWitness sigWit, String initString, Set excluded) { + var allSig = signedWitnessService.getSignedWitnessMap(); + log.debug("{}{}", initString, Utilities.bytesAsHexString(sigWit.getAccountAgeWitnessHash())); + allSig.values().forEach(w -> { + if (Arrays.equals(w.getSignerPubKey(), sigWit.getWitnessOwnerPubKey()) && !excluded.contains(w)) { + excluded.add(sigWit); + logChild(w, initString + " ", excluded); + } + }); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Debug logs /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java b/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java index e0baf486644..a0cdd3798b1 100644 --- a/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java +++ b/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java @@ -37,6 +37,7 @@ import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService; import bisq.common.crypto.CryptoException; +import bisq.common.crypto.Hash; import bisq.common.crypto.KeyRing; import bisq.common.crypto.KeyStorage; import bisq.common.crypto.PubKeyRing; @@ -53,9 +54,11 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; +import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Before; @@ -81,12 +84,25 @@ public class AccountAgeWitnessServiceTest { private AccountAgeWitnessService service; private ChargeBackRisk chargeBackRisk; private FilterManager filterManager; + private File dir1; + private File dir2; + private File dir3; @Before - public void setup() { + public void setup() throws IOException { + KeyRing keyRing = mock(KeyRing.class); + setupService(keyRing); + keypair = Sig.generateKeyPair(); + publicKey = keypair.getPublic(); + // Setup temp storage dir + dir1 = makeDir("temp_tests1"); + dir2 = makeDir("temp_tests1"); + dir3 = makeDir("temp_tests1"); + } + + private void setupService(KeyRing keyRing) { chargeBackRisk = mock(ChargeBackRisk.class); AppendOnlyDataStoreService dataStoreService = mock(AppendOnlyDataStoreService.class); - KeyRing keyRing = mock(KeyRing.class); P2PService p2pService = mock(P2PService.class); ArbitratorManager arbitratorManager = mock(ArbitratorManager.class); when(arbitratorManager.isPublicKeyInList(any())).thenReturn(true); @@ -94,8 +110,13 @@ public void setup() { filterManager = mock(FilterManager.class); signedWitnessService = new SignedWitnessService(keyRing, p2pService, arbitratorManager, null, appendOnlyDataStoreService, null, filterManager); service = new AccountAgeWitnessService(null, null, null, signedWitnessService, chargeBackRisk, null, dataStoreService, filterManager); - keypair = Sig.generateKeyPair(); - publicKey = keypair.getPublic(); + } + + private File makeDir(String name) throws IOException { + var dir = File.createTempFile(name, ""); + dir.delete(); + dir.mkdir(); + return dir; } @After @@ -105,21 +126,21 @@ public void tearDown() { @Ignore @Test - public void testIsTradeDateAfterReleaseDate() throws CryptoException { - Date ageWitnessReleaseDate = new GregorianCalendar(2017, 9, 23).getTime(); - Date tradeDate = new GregorianCalendar(2017, 10, 1).getTime(); + public void testIsTradeDateAfterReleaseDate() { + Date ageWitnessReleaseDate = new GregorianCalendar(2017, Calendar.OCTOBER, 23).getTime(); + Date tradeDate = new GregorianCalendar(2017, Calendar.NOVEMBER, 1).getTime(); assertTrue(service.isDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate, errorMessage -> { })); - tradeDate = new GregorianCalendar(2017, 9, 23).getTime(); + tradeDate = new GregorianCalendar(2017, Calendar.OCTOBER, 23).getTime(); assertTrue(service.isDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate, errorMessage -> { })); - tradeDate = new GregorianCalendar(2017, 9, 22, 0, 0, 1).getTime(); + tradeDate = new GregorianCalendar(2017, Calendar.OCTOBER, 22, 0, 0, 1).getTime(); assertTrue(service.isDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate, errorMessage -> { })); - tradeDate = new GregorianCalendar(2017, 9, 22).getTime(); + tradeDate = new GregorianCalendar(2017, Calendar.OCTOBER, 22).getTime(); assertFalse(service.isDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate, errorMessage -> { })); - tradeDate = new GregorianCalendar(2017, 9, 21).getTime(); + tradeDate = new GregorianCalendar(2017, Calendar.OCTOBER, 21).getTime(); assertFalse(service.isDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate, errorMessage -> { })); } @@ -140,15 +161,7 @@ public void testVerifySignatureOfNonce() throws CryptoException { } @Test - public void testArbitratorSignWitness() throws IOException { - // Setup temp storage dir - File dir1 = File.createTempFile("temp_tests1", ""); - dir1.delete(); - dir1.mkdir(); - File dir2 = File.createTempFile("temp_tests1", ""); - dir2.delete(); - dir2.mkdir(); - + public void testArbitratorSignWitness() { KeyRing buyerKeyRing = new KeyRing(new KeyStorage(dir1)); KeyRing sellerKeyRing = new KeyRing(new KeyStorage(dir2)); @@ -238,13 +251,110 @@ public void testArbitratorSignWitness() throws IOException { buyerPubKeyRing.getSignaturePubKeyBytes()).stream() .findFirst() .orElse(null); + assert foundBuyerSignedWitness != null; assertEquals(Utilities.bytesAsHexString(foundBuyerSignedWitness.getAccountAgeWitnessHash()), Utilities.bytesAsHexString(buyerAccountAgeWitness.getHash())); SignedWitness foundSellerSignedWitness = signedWitnessService.getSignedWitnessSetByOwnerPubKey( sellerPubKeyRing.getSignaturePubKeyBytes()).stream() .findFirst() .orElse(null); + assert foundSellerSignedWitness != null; assertEquals(Utilities.bytesAsHexString(foundSellerSignedWitness.getAccountAgeWitnessHash()), Utilities.bytesAsHexString(sellerAccountAgeWitness.getHash())); } + + // Create a tree of signed witnesses Arb -(SWA)-> aew1 -(SW1)-> aew2 -(SW2)-> aew3 + // Delete SWA signature, none of the account age witnesses are considered signed + // Sign a dummy AccountAgeWitness using the signerPubkey from SW1; aew2 and aew3 are not considered signed. The + // lost SignedWitness isn't possible to recover so aew1 is still not signed, but it's pubkey is a signer. + @Test + public void testArbitratorSignDummyWitness() throws CryptoException { + ECKey arbitratorKey = new ECKey(); + // Init 2 user accounts + var user1KeyRing = new KeyRing(new KeyStorage(dir1)); + var user2KeyRing = new KeyRing(new KeyStorage(dir2)); + var user3KeyRing = new KeyRing(new KeyStorage(dir3)); + var pubKeyRing1 = user1KeyRing.getPubKeyRing(); + var pubKeyRing2 = user2KeyRing.getPubKeyRing(); + var pubKeyRing3 = user3KeyRing.getPubKeyRing(); + var account1 = new SepaAccountPayload(PaymentMethod.SEPA_ID, "1", CountryUtil.getAllSepaCountries()); + var account2 = new SepaAccountPayload(PaymentMethod.SEPA_ID, "2", CountryUtil.getAllSepaCountries()); + var account3 = new SepaAccountPayload(PaymentMethod.SEPA_ID, "3", CountryUtil.getAllSepaCountries()); + var aew1 = service.getNewWitness(account1, pubKeyRing1); + var aew2 = service.getNewWitness(account2, pubKeyRing2); + var aew3 = service.getNewWitness(account3, pubKeyRing3); + // Backdate witness1 70 days + aew1 = new AccountAgeWitness(aew1.getHash(), new Date().getTime() - TimeUnit.DAYS.toMillis(70)); + aew2 = new AccountAgeWitness(aew2.getHash(), new Date().getTime() - TimeUnit.DAYS.toMillis(35)); + aew3 = new AccountAgeWitness(aew3.getHash(), new Date().getTime() - TimeUnit.DAYS.toMillis(1)); + service.addToMap(aew1); + service.addToMap(aew2); + service.addToMap(aew3); + + // Test as user1. It's still possible to sign as arbitrator since the ECKey is passed as an argument. + setupService(user1KeyRing); + + // Arbitrator signs user1 + service.arbitratorSignAccountAgeWitness(aew1, arbitratorKey, pubKeyRing1.getSignaturePubKeyBytes(), + aew1.getDate()); + // user1 signs user2 + signAccountAgeWitness(aew2, pubKeyRing2.getSignaturePubKey(), aew2.getDate(), user1KeyRing); + // user2 signs user3 + signAccountAgeWitness(aew3, pubKeyRing3.getSignaturePubKey(), aew3.getDate(), user2KeyRing); + signedWitnessService.signAccountAgeWitness(SignedWitnessService.MINIMUM_TRADE_AMOUNT_FOR_SIGNING, aew2, + pubKeyRing2.getSignaturePubKey()); + assertTrue(service.accountIsSigner(aew1)); + assertTrue(service.accountIsSigner(aew2)); + assertFalse(service.accountIsSigner(aew3)); + assertTrue(signedWitnessService.isSignedAccountAgeWitness(aew3)); + + // Remove SignedWitness signed by arbitrator + @SuppressWarnings("OptionalGetWithoutIsPresent") + var signedWitnessArb = signedWitnessService.getSignedWitnessMap().values().stream() + .filter(sw -> sw.getVerificationMethod() == SignedWitness.VerificationMethod.ARBITRATOR) + .findAny() + .get(); + signedWitnessService.getSignedWitnessMap().remove(signedWitnessArb.getHashAsByteArray()); + assertEquals(signedWitnessService.getSignedWitnessMap().size(), 2); + + // Check that no account age witness is a signer + assertFalse(service.accountIsSigner(aew1)); + assertFalse(service.accountIsSigner(aew2)); + assertFalse(service.accountIsSigner(aew3)); + assertFalse(signedWitnessService.isSignedAccountAgeWitness(aew2)); + + // Sign dummy AccountAgeWitness using signer key from SW_1 + assertEquals(signedWitnessService.getOrphanSignedWitnessSet().size(), 1); + + // TODO: move this to accountagewitnessservice + @SuppressWarnings("OptionalGetWithoutIsPresent") + var orphanedSignedWitness = signedWitnessService.getOrphanSignedWitnessSet().stream().findAny().get(); + var dummyAccountAgeWitnessHash = Hash.getRipemd160hash(orphanedSignedWitness.getSignerPubKey()); + var dummyAEW = new AccountAgeWitness(dummyAccountAgeWitnessHash, + orphanedSignedWitness.getDate() - + (TimeUnit.DAYS.toMillis(SignedWitnessService.SIGNER_AGE_DAYS + 1))); + service.arbitratorSignAccountAgeWitness( + dummyAEW, arbitratorKey, orphanedSignedWitness.getSignerPubKey(), dummyAEW.getDate()); + + assertFalse(service.accountIsSigner(aew1)); + assertTrue(service.accountIsSigner(aew2)); + assertFalse(service.accountIsSigner(aew3)); + assertTrue(signedWitnessService.isSignedAccountAgeWitness(aew2)); + } + + private void signAccountAgeWitness(AccountAgeWitness accountAgeWitness, + PublicKey witnessOwnerPubKey, + long time, + KeyRing signerKeyRing) throws CryptoException { + byte[] signature = Sig.sign(signerKeyRing.getSignatureKeyPair().getPrivate(), accountAgeWitness.getHash()); + SignedWitness signedWitness = new SignedWitness(SignedWitness.VerificationMethod.TRADE, + accountAgeWitness.getHash(), + signature, + signerKeyRing.getSignatureKeyPair().getPublic().getEncoded(), + witnessOwnerPubKey.getEncoded(), + time, + SignedWitnessService.MINIMUM_TRADE_AMOUNT_FOR_SIGNING.value); + signedWitnessService.getSignedWitnessMap().putIfAbsent(signedWitness.getHashAsByteArray(), signedWitness); + } + } From 7bd5969dcf1cfbf903e3404ead1a5b755f9f81bd Mon Sep 17 00:00:00 2001 From: sqrrm Date: Tue, 28 Apr 2020 11:17:09 +0200 Subject: [PATCH 3/7] Refactor accountagewitnessservice log helpers Better filter for root signedwitnesses Add debug keyboard shortcuts: - ctrl+shift+L -> logSignedWitnesses - ctrl+shift+S -> logSigners --- .../main/java/bisq/common/util/Utilities.java | 4 + .../account/sign/SignedWitnessService.java | 5 +- .../witness/AccountAgeWitnessService.java | 103 ++---------- .../witness/AccountAgeWitnessUtils.java | 158 ++++++++++++++++++ .../core/trade/protocol/ProcessModel.java | 2 +- .../witness/AccountAgeWitnessServiceTest.java | 4 +- .../account/content/PaymentAccountsView.java | 20 ++- .../pendingtrades/PendingTradesViewModel.java | 2 +- 8 files changed, 200 insertions(+), 98 deletions(-) create mode 100644 core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java diff --git a/common/src/main/java/bisq/common/util/Utilities.java b/common/src/main/java/bisq/common/util/Utilities.java index f958bdd9b99..bfa0d7df54a 100644 --- a/common/src/main/java/bisq/common/util/Utilities.java +++ b/common/src/main/java/bisq/common/util/Utilities.java @@ -334,6 +334,10 @@ public static boolean isAltPressed(KeyCode keyCode, KeyEvent keyEvent) { return new KeyCodeCombination(keyCode, KeyCombination.ALT_DOWN).match(keyEvent); } + public static boolean isCtrlShiftPressed(KeyCode keyCode, KeyEvent keyEvent) { + return new KeyCodeCombination(keyCode, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN).match(keyEvent); + } + public static byte[] concatenateByteArrays(byte[] array1, byte[] array2) { return ArrayUtils.addAll(array1, array2); } diff --git a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java index 43a11a7f121..79beae2e588 100644 --- a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java +++ b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java @@ -334,10 +334,11 @@ public Set getTrustedPeerSignedWitnessSet(AccountAgeWitness accou .collect(Collectors.toSet()); } - public Set getOrphanSignedWitnessSet() { + public Set getRootSignedWitnessSet(boolean includeSignedByArbitrator) { return signedWitnessMap.values().stream() .filter(witness -> getSignedWitnessSetByOwnerPubKey(witness.getSignerPubKey(), new Stack<>()).isEmpty()) - .filter(witness -> witness.getVerificationMethod() != SignedWitness.VerificationMethod.ARBITRATOR) + .filter(witness -> includeSignedByArbitrator || + witness.getVerificationMethod() != SignedWitness.VerificationMethod.ARBITRATOR) .collect(Collectors.toSet()); } diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index 10ef74ab60d..46e6e821a8b 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -74,11 +74,8 @@ import java.util.Optional; import java.util.Random; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Stack; import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -137,6 +134,8 @@ public String getPresentation() { private final SignedWitnessService signedWitnessService; private final ChargeBackRisk chargeBackRisk; private final FilterManager filterManager; + @Getter + private final AccountAgeWitnessUtils accountAgeWitnessUtils; @Getter private final Map accountAgeWitnessMap = new HashMap<>(); @@ -163,6 +162,11 @@ public AccountAgeWitnessService(KeyRing keyRing, this.chargeBackRisk = chargeBackRisk; this.filterManager = filterManager; + accountAgeWitnessUtils = new AccountAgeWitnessUtils( + this, + signedWitnessService, + keyRing); + // We need to add that early (before onAllServicesInitialized) as it will be used at startup. appendOnlyDataStoreService.addService(accountAgeWitnessStorageService); } @@ -231,7 +235,7 @@ public byte[] getPeerAccountAgeWitnessHash(Trade trade) { .orElse(null); } - private byte[] getAccountInputDataWithSalt(PaymentAccountPayload paymentAccountPayload) { + byte[] getAccountInputDataWithSalt(PaymentAccountPayload paymentAccountPayload) { return Utilities.concatenateByteArrays(paymentAccountPayload.getAgeWitnessInputData(), paymentAccountPayload.getSalt()); } @@ -243,7 +247,7 @@ public AccountAgeWitness getNewWitness(PaymentAccountPayload paymentAccountPaylo return new AccountAgeWitness(hash, new Date().getTime()); } - private Optional findWitness(PaymentAccountPayload paymentAccountPayload, + Optional findWitness(PaymentAccountPayload paymentAccountPayload, PubKeyRing pubKeyRing) { byte[] accountInputDataWithSalt = getAccountInputDataWithSalt(paymentAccountPayload); byte[] hash = Hash.getSha256Ripemd160hash(Utilities.concatenateByteArrays(accountInputDataWithSalt, @@ -646,7 +650,7 @@ public String arbitratorSignAccountAgeWitness(AccountAgeWitness accountAgeWitnes ECKey key, byte[] tradersPubKey, long time) { - return signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, tradersPubKey,time); + return signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, tradersPubKey, time); } public void traderSignPeersAccountAgeWitness(Trade trade) { @@ -795,92 +799,9 @@ public SignState getSignState(AccountAgeWitness accountAgeWitness) { } public Set getOrphanSignedWitnesses() { - var orphans = signedWitnessService.getOrphanSignedWitnessSet().stream() + return signedWitnessService.getRootSignedWitnessSet(false).stream() .map(signedWitness -> getWitnessByHash(signedWitness.getAccountAgeWitnessHash()).orElse(null)) .filter(Objects::nonNull) .collect(Collectors.toSet()); - var orphanSigners = signedWitnessService.getOrphanSignedWitnessSet().stream() - .filter(Utilities.distinctByKey(w -> Utilities.bytesAsHexString(w.getSignerPubKey()))) - .collect(Collectors.toSet()); - var orphanAEW = signedWitnessService.getOrphanSignedWitnessSet(); - log.debug("Orphaned signed account age witnesses:"); - orphanSigners.forEach(w -> { - log.debug("{}: {}", w.getVerificationMethod().toString(), - Utilities.bytesAsHexString(Hash.getRipemd160hash(w.getSignerPubKey()))); - logChild(w, " ", new HashSet<>()); - }); - return orphans; - } - - private void logChild(SignedWitness sigWit, String initString, Set excluded) { - var allSig = signedWitnessService.getSignedWitnessMap(); - log.debug("{}{}", initString, Utilities.bytesAsHexString(sigWit.getAccountAgeWitnessHash())); - allSig.values().forEach(w -> { - if (Arrays.equals(w.getSignerPubKey(), sigWit.getWitnessOwnerPubKey()) && !excluded.contains(w)) { - excluded.add(sigWit); - logChild(w, initString + " ", excluded); - } - }); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Debug logs - /////////////////////////////////////////////////////////////////////////////////////////// - - private String getWitnessDebugLog(PaymentAccountPayload paymentAccountPayload, - PubKeyRing pubKeyRing) { - Optional accountAgeWitness = findWitness(paymentAccountPayload, pubKeyRing); - if (!accountAgeWitness.isPresent()) { - byte[] accountInputDataWithSalt = getAccountInputDataWithSalt(paymentAccountPayload); - byte[] hash = Hash.getSha256Ripemd160hash(Utilities.concatenateByteArrays(accountInputDataWithSalt, - pubKeyRing.getSignaturePubKeyBytes())); - return "No accountAgeWitness found for paymentAccountPayload with hash " + Utilities.bytesAsHexString(hash); - } - - SignState signState = getSignState(accountAgeWitness.get()); - return signState.name() + " " + signState.getPresentation() + - "\n" + accountAgeWitness.toString(); - } - - public void witnessDebugLog(Trade trade, @Nullable AccountAgeWitness myWitness) { - // Log to find why accounts sometimes don't get signed as expected - // TODO: Demote to debug or remove once account signing is working ok - checkNotNull(trade.getContract()); - checkNotNull(trade.getContract().getBuyerPaymentAccountPayload()); - boolean checkingSignTrade = true; - boolean isBuyer = trade.getContract().isMyRoleBuyer(keyRing.getPubKeyRing()); - AccountAgeWitness witness = myWitness; - if (witness == null) { - witness = isBuyer ? - getMyWitness(trade.getContract().getBuyerPaymentAccountPayload()) : - getMyWitness(trade.getContract().getSellerPaymentAccountPayload()); - checkingSignTrade = false; - } - boolean isSignWitnessTrade = accountIsSigner(witness) && - !peerHasSignedWitness(trade) && - tradeAmountIsSufficient(trade.getTradeAmount()); - log.info("AccountSigning: " + - "\ntradeId: {}" + - "\nis buyer: {}" + - "\nbuyer account age witness info: {}" + - "\nseller account age witness info: {}" + - "\nchecking for sign trade: {}" + - "\nis myWitness signer: {}" + - "\npeer has signed witness: {}" + - "\ntrade amount: {}" + - "\ntrade amount is sufficient: {}" + - "\nisSignWitnessTrade: {}", - trade.getId(), - isBuyer, - getWitnessDebugLog(trade.getContract().getBuyerPaymentAccountPayload(), - trade.getContract().getBuyerPubKeyRing()), - getWitnessDebugLog(trade.getContract().getSellerPaymentAccountPayload(), - trade.getContract().getSellerPubKeyRing()), - checkingSignTrade, // Following cases added to use same logic as in seller signing check - accountIsSigner(witness), - peerHasSignedWitness(trade), - trade.getTradeAmount(), - tradeAmountIsSufficient(trade.getTradeAmount()), - isSignWitnessTrade); } } diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java new file mode 100644 index 00000000000..5fc14b579ed --- /dev/null +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java @@ -0,0 +1,158 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.core.account.witness; + +import bisq.core.account.sign.SignedWitness; +import bisq.core.account.sign.SignedWitnessService; +import bisq.core.payment.payload.PaymentAccountPayload; +import bisq.core.trade.Trade; + +import bisq.network.p2p.storage.P2PDataStorage; + +import bisq.common.crypto.Hash; +import bisq.common.crypto.KeyRing; +import bisq.common.crypto.PubKeyRing; +import bisq.common.util.Utilities; + +import java.util.Arrays; +import java.util.Optional; +import java.util.Stack; + +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +@Slf4j +public class AccountAgeWitnessUtils { + private final AccountAgeWitnessService accountAgeWitnessService; + private final SignedWitnessService signedWitnessService; + private final KeyRing keyRing; + + AccountAgeWitnessUtils(AccountAgeWitnessService accountAgeWitnessService, + SignedWitnessService signedWitnessService, + KeyRing keyRing) { + this.accountAgeWitnessService = accountAgeWitnessService; + this.signedWitnessService = signedWitnessService; + this.keyRing = keyRing; + } + + // Log tree of signed witnesses + public void logSignedWitnesses() { + var orphanSigners = signedWitnessService.getRootSignedWitnessSet(false); + log.info("Orphaned signed account age witnesses:"); + orphanSigners.forEach(w -> { + log.warn("{}: {}", w.getVerificationMethod().toString(), + Utilities.bytesAsHexString(Hash.getRipemd160hash(w.getSignerPubKey()))); + logChild(w, " ", new Stack<>()); + }); + } + + private void logChild(SignedWitness sigWit, String initString, Stack excluded) { + var allSig = signedWitnessService.getSignedWitnessMap(); + log.warn("{}{}", initString, Utilities.bytesAsHexString(sigWit.getAccountAgeWitnessHash())); + allSig.values().forEach(w -> { + if (!excluded.contains(new P2PDataStorage.ByteArray(w.getWitnessOwnerPubKey())) && + Arrays.equals(w.getSignerPubKey(), sigWit.getWitnessOwnerPubKey())) { + excluded.push(new P2PDataStorage.ByteArray(w.getWitnessOwnerPubKey())); + logChild(w, initString + " ", excluded); + excluded.pop(); + } + }); + } + + // Log signers per + public void logSigners() { + log.info("signers per AEW"); + var allSig = signedWitnessService.getSignedWitnessMap(); + allSig.values().forEach(w -> { + log.info("AEW {}", Utilities.bytesAsHexString(w.getAccountAgeWitnessHash())); + allSig.values().forEach(ww -> { + if (Arrays.equals(w.getSignerPubKey(), ww.getWitnessOwnerPubKey())) { + log.info(" {}", Utilities.bytesAsHexString(ww.getAccountAgeWitnessHash())); + } + }); + } + ); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Debug logs + /////////////////////////////////////////////////////////////////////////////////////////// + + private String getWitnessDebugLog(PaymentAccountPayload paymentAccountPayload, + PubKeyRing pubKeyRing) { + Optional accountAgeWitness = + accountAgeWitnessService.findWitness(paymentAccountPayload, pubKeyRing); + if (!accountAgeWitness.isPresent()) { + byte[] accountInputDataWithSalt = + accountAgeWitnessService.getAccountInputDataWithSalt(paymentAccountPayload); + byte[] hash = Hash.getSha256Ripemd160hash(Utilities.concatenateByteArrays(accountInputDataWithSalt, + pubKeyRing.getSignaturePubKeyBytes())); + return "No accountAgeWitness found for paymentAccountPayload with hash " + Utilities.bytesAsHexString(hash); + } + + AccountAgeWitnessService.SignState signState = + accountAgeWitnessService.getSignState(accountAgeWitness.get()); + return signState.name() + " " + signState.getPresentation() + + "\n" + accountAgeWitness.toString(); + } + + public void witnessDebugLog(Trade trade, @Nullable AccountAgeWitness myWitness) { + // Log to find why accounts sometimes don't get signed as expected + // TODO: Demote to debug or remove once account signing is working ok + checkNotNull(trade.getContract()); + checkNotNull(trade.getContract().getBuyerPaymentAccountPayload()); + boolean checkingSignTrade = true; + boolean isBuyer = trade.getContract().isMyRoleBuyer(keyRing.getPubKeyRing()); + AccountAgeWitness witness = myWitness; + if (witness == null) { + witness = isBuyer ? + accountAgeWitnessService.getMyWitness(trade.getContract().getBuyerPaymentAccountPayload()) : + accountAgeWitnessService.getMyWitness(trade.getContract().getSellerPaymentAccountPayload()); + checkingSignTrade = false; + } + boolean isSignWitnessTrade = accountAgeWitnessService.accountIsSigner(witness) && + !accountAgeWitnessService.peerHasSignedWitness(trade) && + accountAgeWitnessService.tradeAmountIsSufficient(trade.getTradeAmount()); + log.info("AccountSigning: " + + "\ntradeId: {}" + + "\nis buyer: {}" + + "\nbuyer account age witness info: {}" + + "\nseller account age witness info: {}" + + "\nchecking for sign trade: {}" + + "\nis myWitness signer: {}" + + "\npeer has signed witness: {}" + + "\ntrade amount: {}" + + "\ntrade amount is sufficient: {}" + + "\nisSignWitnessTrade: {}", + trade.getId(), + isBuyer, + getWitnessDebugLog(trade.getContract().getBuyerPaymentAccountPayload(), + trade.getContract().getBuyerPubKeyRing()), + getWitnessDebugLog(trade.getContract().getSellerPaymentAccountPayload(), + trade.getContract().getSellerPubKeyRing()), + checkingSignTrade, // Following cases added to use same logic as in seller signing check + accountAgeWitnessService.accountIsSigner(witness), + accountAgeWitnessService.peerHasSignedWitness(trade), + trade.getTradeAmount(), + accountAgeWitnessService.tradeAmountIsSufficient(trade.getTradeAmount()), + isSignWitnessTrade); + } +} diff --git a/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java b/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java index 6f575be6843..27317d7569c 100644 --- a/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java +++ b/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java @@ -358,6 +358,6 @@ private void setPubKeyRing(PubKeyRing pubKeyRing) { } void logTrade(Trade trade) { - accountAgeWitnessService.witnessDebugLog(trade, null); + accountAgeWitnessService.getAccountAgeWitnessUtils().witnessDebugLog(trade, null); } } diff --git a/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java b/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java index a0cdd3798b1..c8e1cc6b59e 100644 --- a/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java +++ b/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java @@ -324,11 +324,11 @@ public void testArbitratorSignDummyWitness() throws CryptoException { assertFalse(signedWitnessService.isSignedAccountAgeWitness(aew2)); // Sign dummy AccountAgeWitness using signer key from SW_1 - assertEquals(signedWitnessService.getOrphanSignedWitnessSet().size(), 1); + assertEquals(signedWitnessService.getRootSignedWitnessSet().size(), 1); // TODO: move this to accountagewitnessservice @SuppressWarnings("OptionalGetWithoutIsPresent") - var orphanedSignedWitness = signedWitnessService.getOrphanSignedWitnessSet().stream().findAny().get(); + var orphanedSignedWitness = signedWitnessService.getRootSignedWitnessSet().stream().findAny().get(); var dummyAccountAgeWitnessHash = Hash.getRipemd160hash(orphanedSignedWitness.getSignerPubKey()); var dummyAEW = new AccountAgeWitness(dummyAccountAgeWitnessHash, orphanedSignedWitness.getDate() - diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/PaymentAccountsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/PaymentAccountsView.java index e71b7a4e615..c939a1bf83e 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/PaymentAccountsView.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/PaymentAccountsView.java @@ -16,6 +16,8 @@ import bisq.core.payment.payload.PaymentMethod; import bisq.common.UserThread; +import bisq.common.app.DevEnv; +import bisq.common.util.Utilities; import org.apache.commons.lang3.StringUtils; @@ -26,10 +28,14 @@ import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.AnchorPane; import javafx.beans.value.ChangeListener; +import javafx.event.EventHandler; + import javafx.collections.ObservableList; import javafx.util.Callback; @@ -41,8 +47,8 @@ public abstract class PaymentAccountsView paymentAccountsListView; private ChangeListener paymentAccountChangeListener; protected Button addAccountButton, exportButton, importButton; - SignedWitnessService signedWitnessService; protected AccountAgeWitnessService accountAgeWitnessService; + private EventHandler keyEventEventHandler; public PaymentAccountsView(M model, AccountAgeWitnessService accountAgeWitnessService) { super(model); @@ -51,6 +57,14 @@ public PaymentAccountsView(M model, AccountAgeWitnessService accountAgeWitnessSe @Override public void initialize() { + keyEventEventHandler = event -> { + if (Utilities.isCtrlShiftPressed(KeyCode.L, event)) { + accountAgeWitnessService.getAccountAgeWitnessUtils().logSignedWitnesses(); + } else if (Utilities.isCtrlShiftPressed(KeyCode.S, event)) { + accountAgeWitnessService.getAccountAgeWitnessUtils().logSigners(); + } + }; + buildForm(); paymentAccountChangeListener = (observable, oldValue, newValue) -> { if (newValue != null) @@ -68,6 +82,8 @@ protected void activate() { addAccountButton.setOnAction(event -> addNewAccount()); exportButton.setOnAction(event -> exportAccounts()); importButton.setOnAction(event -> importAccounts()); + if (root.getScene() != null) + root.getScene().addEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler); } @Override @@ -76,6 +92,8 @@ protected void deactivate() { addAccountButton.setOnAction(null); exportButton.setOnAction(null); importButton.setOnAction(null); + if (root.getScene() != null) + root.getScene().removeEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler); } protected void onDeleteAccount(PaymentAccount paymentAccount) { diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java index cefeb458b66..7c15525dce3 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java @@ -380,7 +380,7 @@ public boolean isSignWitnessTrade() { checkNotNull(trade.getOffer(), "offer must not be null"); AccountAgeWitness myWitness = accountAgeWitnessService.getMyWitness(dataModel.getSellersPaymentAccountPayload()); - accountAgeWitnessService.witnessDebugLog(trade, myWitness); + accountAgeWitnessService.getAccountAgeWitnessUtils().witnessDebugLog(trade, myWitness); return accountAgeWitnessService.accountIsSigner(myWitness) && !accountAgeWitnessService.peerHasSignedWitness(trade) && From 0dc769bf9bc1f995a3e543f7a4e06eff5f436aee Mon Sep 17 00:00:00 2001 From: sqrrm Date: Sun, 31 May 2020 15:08:00 +0200 Subject: [PATCH 4/7] Arbitrator signing of AccountAgeWitness with known pubkey Improve debug logging. Lower level from warning to info Fix broken test Add example code to clean out unnecessary SignedWitnesses --- .../account/sign/SignedWitnessService.java | 21 +++++++++++- .../witness/AccountAgeWitnessService.java | 32 ++++++++++--------- .../witness/AccountAgeWitnessUtils.java | 13 +++++--- .../witness/AccountAgeWitnessServiceTest.java | 4 +-- .../windows/SignSpecificWitnessWindow.java | 5 +-- 5 files changed, 51 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java index 79beae2e588..a2eaf8c1dc2 100644 --- a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java +++ b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java @@ -50,12 +50,15 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; +import java.util.stream.Collector; import java.util.stream.Collectors; import lombok.Getter; @@ -125,6 +128,8 @@ public void onUpdatedDataReceived() { } }); } + // TODO: Enable cleaning of signed witness list when necessary + // cleanSignedWitnesses(); } private void onBootstrapComplete() { @@ -441,7 +446,6 @@ private boolean verifyDate(SignedWitness signedWitness, long childSignedWitnessD @VisibleForTesting void addToMap(SignedWitness signedWitness) { - // TODO: Perhaps filter out all but one signedwitness per accountagewitness signedWitnessMap.putIfAbsent(signedWitness.getHashAsByteArray(), signedWitness); } @@ -456,4 +460,19 @@ private void publishSignedWitness(SignedWitness signedWitness) { private void doRepublishAllSignedWitnesses() { signedWitnessMap.forEach((e, signedWitness) -> p2PService.addPersistableNetworkPayload(signedWitness, true)); } + + // Remove SignedWitnesses that are signed by TRADE that also have an ARBITRATOR signature + // for the same ownerPubKey and AccountAgeWitnessHash + private void cleanSignedWitnesses() { + var orphans = getRootSignedWitnessSet(false); + var signedWitnessesCopy = new HashSet<>(signedWitnessMap.values()); + signedWitnessesCopy.forEach(sw -> orphans.forEach(orphan -> { + if (sw.getVerificationMethod() == SignedWitness.VerificationMethod.ARBITRATOR && + Arrays.equals(sw.getWitnessOwnerPubKey(), orphan.getWitnessOwnerPubKey()) && + Arrays.equals(sw.getAccountAgeWitnessHash(), orphan.getAccountAgeWitnessHash())) { + signedWitnessMap.remove(orphan.getHashAsByteArray()); + log.info("Remove duplicate SignedWitness: {}", orphan.toString()); + } + })); + } } diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index 46e6e821a8b..dde667a3733 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -17,7 +17,6 @@ package bisq.core.account.witness; -import bisq.core.account.sign.SignedWitness; import bisq.core.account.sign.SignedWitnessService; import bisq.core.filter.FilterManager; import bisq.core.filter.PaymentAccountFilter; @@ -67,14 +66,12 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Random; import java.util.Set; -import java.util.Stack; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -82,8 +79,6 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import javax.annotation.Nullable; - import static com.google.common.base.Preconditions.checkNotNull; @Slf4j @@ -248,7 +243,7 @@ public AccountAgeWitness getNewWitness(PaymentAccountPayload paymentAccountPaylo } Optional findWitness(PaymentAccountPayload paymentAccountPayload, - PubKeyRing pubKeyRing) { + PubKeyRing pubKeyRing) { byte[] accountInputDataWithSalt = getAccountInputDataWithSalt(paymentAccountPayload); byte[] hash = Hash.getSha256Ripemd160hash(Utilities.concatenateByteArrays(accountInputDataWithSalt, pubKeyRing.getSignaturePubKeyBytes())); @@ -640,17 +635,24 @@ public void arbitratorSignAccountAgeWitness(Coin tradeAmount, signedWitnessService.signAccountAgeWitness(tradeAmount, accountAgeWitness, key, peersPubKey); } - public String arbitratorSignAccountAgeWitness(AccountAgeWitness accountAgeWitness, - ECKey key, - long time) { - return signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, null, time); + public String arbitratorSignOrphanWitness(AccountAgeWitness accountAgeWitness, + ECKey key, + long time) { + // Find AccountAgeWitness as signedwitness + var signedWitness = signedWitnessService.getSignedWitnessMap().values().stream() + .filter(sw -> Arrays.equals(sw.getAccountAgeWitnessHash(), accountAgeWitness.getHash())) + .findAny() + .orElse(null); + checkNotNull(signedWitness); + return signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, signedWitness.getWitnessOwnerPubKey(), + time); } - public String arbitratorSignAccountAgeWitness(AccountAgeWitness accountAgeWitness, - ECKey key, - byte[] tradersPubKey, - long time) { - return signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, tradersPubKey, time); + public void arbitratorSignAccountAgeWitness(AccountAgeWitness accountAgeWitness, + ECKey key, + byte[] tradersPubKey, + long time) { + signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, tradersPubKey, time); } public void traderSignPeersAccountAgeWitness(Trade trade) { diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java index 5fc14b579ed..c1563486047 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java @@ -55,18 +55,23 @@ public class AccountAgeWitnessUtils { // Log tree of signed witnesses public void logSignedWitnesses() { - var orphanSigners = signedWitnessService.getRootSignedWitnessSet(false); + var orphanSigners = signedWitnessService.getRootSignedWitnessSet(true); log.info("Orphaned signed account age witnesses:"); orphanSigners.forEach(w -> { - log.warn("{}: {}", w.getVerificationMethod().toString(), - Utilities.bytesAsHexString(Hash.getRipemd160hash(w.getSignerPubKey()))); + log.info("{}: Signer PKH: {} Owner PKH: {} time: {}", w.getVerificationMethod().toString(), + Utilities.bytesAsHexString(Hash.getRipemd160hash(w.getSignerPubKey())).substring(0,7), + Utilities.bytesAsHexString(Hash.getRipemd160hash(w.getWitnessOwnerPubKey())).substring(0,7), + w.getDate()); logChild(w, " ", new Stack<>()); }); } private void logChild(SignedWitness sigWit, String initString, Stack excluded) { var allSig = signedWitnessService.getSignedWitnessMap(); - log.warn("{}{}", initString, Utilities.bytesAsHexString(sigWit.getAccountAgeWitnessHash())); + log.info("{}AEW: {} PKH: {} time: {}", initString, + Utilities.bytesAsHexString(sigWit.getAccountAgeWitnessHash()).substring(0, 7), + Utilities.bytesAsHexString(Hash.getRipemd160hash(sigWit.getWitnessOwnerPubKey())).substring(0, 7), + sigWit.getDate()); allSig.values().forEach(w -> { if (!excluded.contains(new P2PDataStorage.ByteArray(w.getWitnessOwnerPubKey())) && Arrays.equals(w.getSignerPubKey(), sigWit.getWitnessOwnerPubKey())) { diff --git a/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java b/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java index c8e1cc6b59e..36d52df74d3 100644 --- a/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java +++ b/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java @@ -324,11 +324,11 @@ public void testArbitratorSignDummyWitness() throws CryptoException { assertFalse(signedWitnessService.isSignedAccountAgeWitness(aew2)); // Sign dummy AccountAgeWitness using signer key from SW_1 - assertEquals(signedWitnessService.getRootSignedWitnessSet().size(), 1); + assertEquals(signedWitnessService.getRootSignedWitnessSet(false).size(), 1); // TODO: move this to accountagewitnessservice @SuppressWarnings("OptionalGetWithoutIsPresent") - var orphanedSignedWitness = signedWitnessService.getRootSignedWitnessSet().stream().findAny().get(); + var orphanedSignedWitness = signedWitnessService.getRootSignedWitnessSet(false).stream().findAny().get(); var dummyAccountAgeWitnessHash = Hash.getRipemd160hash(orphanedSignedWitness.getSignerPubKey()); var dummyAEW = new AccountAgeWitness(dummyAccountAgeWitnessHash, orphanedSignedWitness.getDate() - diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignSpecificWitnessWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignSpecificWitnessWindow.java index bf1d89a88aa..3f8638878e8 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignSpecificWitnessWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignSpecificWitnessWindow.java @@ -94,7 +94,7 @@ private void addSelectWitnessContent() { searchAutoComplete = new JFXAutoCompletePopup<>(); searchAutoComplete.setPrefWidth(400); - searchAutoComplete.getSuggestions().addAll(accountAgeWitnessService.getAccountAgeWitnessMap().values()); + searchAutoComplete.getSuggestions().addAll(accountAgeWitnessService.getOrphanSignedWitnesses()); searchAutoComplete.setSuggestionsCellFactory(param -> new ListCell<>() { @Override protected void updateItem(AccountAgeWitness item, boolean empty) { @@ -160,7 +160,8 @@ private void selectAccountAgeWitness() { var arbitratorPubKeyAsHex = Utils.HEX.encode(arbitratorKey.getPubKey()); var isKeyValid = arbitratorManager.isPublicKeyInList(arbitratorPubKeyAsHex); if (isKeyValid) { - var result = accountAgeWitnessService.arbitratorSignAccountAgeWitness(selectedWitness, arbitratorKey, + var result = accountAgeWitnessService.arbitratorSignOrphanWitness(selectedWitness, + arbitratorKey, datePicker.getValue().atStartOfDay().toEpochSecond(ZoneOffset.UTC) * 1000); if (result.isEmpty()) { addSuccessContent(); From 752eb49b4a4ce0033aa8acebf29d47a508ee5816 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Mon, 1 Jun 2020 15:45:40 +0200 Subject: [PATCH 5/7] Log unique unsigned signer pubkeys --- .../bisq/core/account/sign/SignedWitnessService.java | 10 ++++++++++ .../core/account/witness/AccountAgeWitnessUtils.java | 9 ++++++++- .../main/account/content/PaymentAccountsView.java | 2 ++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java index a2eaf8c1dc2..26457f38103 100644 --- a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java +++ b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java @@ -29,6 +29,7 @@ import bisq.common.UserThread; import bisq.common.crypto.CryptoException; +import bisq.common.crypto.Hash; import bisq.common.crypto.KeyRing; import bisq.common.crypto.Sig; import bisq.common.util.Utilities; @@ -64,6 +65,8 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import static bisq.common.util.Utilities.distinctByKey; + @Slf4j public class SignedWitnessService { public static final long SIGNER_AGE_DAYS = 30; @@ -347,6 +350,13 @@ public Set getRootSignedWitnessSet(boolean includeSignedByArbitra .collect(Collectors.toSet()); } + public Set getUnsignedSignersPubKeys() { + return getRootSignedWitnessSet(false).stream() + .map(SignedWitness::getSignerPubKey) + .filter(distinctByKey(key -> Utilities.bytesAsHexString(Hash.getRipemd160hash(key)))) + .collect(Collectors.toSet()); + } + // We go one level up by using the signer Key to lookup for SignedWitness objects which contain the signerKey as // witnessOwnerPubKey private Set getSignedWitnessSetByOwnerPubKey(byte[] ownerPubKey, diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java index c1563486047..4a7918f038a 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java @@ -32,6 +32,7 @@ import java.util.Arrays; import java.util.Optional; import java.util.Stack; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @@ -84,7 +85,7 @@ private void logChild(SignedWitness sigWit, String initString, Stack { log.info("AEW {}", Utilities.bytesAsHexString(w.getAccountAgeWitnessHash())); @@ -97,6 +98,12 @@ public void logSigners() { ); } + public void logUnsignedSignerPubKeys() { + log.info("Unsigned signer pubkeys"); + signedWitnessService.getUnsignedSignersPubKeys().forEach(pubKey -> + log.info("PK hash {}",Utilities.bytesAsHexString(Hash.getRipemd160hash(pubKey)))); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Debug logs /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/PaymentAccountsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/PaymentAccountsView.java index c939a1bf83e..deb43641a5d 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/PaymentAccountsView.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/PaymentAccountsView.java @@ -62,6 +62,8 @@ public void initialize() { accountAgeWitnessService.getAccountAgeWitnessUtils().logSignedWitnesses(); } else if (Utilities.isCtrlShiftPressed(KeyCode.S, event)) { accountAgeWitnessService.getAccountAgeWitnessUtils().logSigners(); + } else if (Utilities.isCtrlShiftPressed(KeyCode.U, event)) { + accountAgeWitnessService.getAccountAgeWitnessUtils().logUnsignedSignerPubKeys(); } }; From ca9665fa3c14b5f88350023e15da60880558f5d6 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Wed, 3 Jun 2020 15:41:52 +0200 Subject: [PATCH 6/7] Sign unsigned keys Legacy arbitrator can sign unsigned signed witness signer pubkeys To sign, from legacy arbitrator support, ctrl+O --- .../main/java/bisq/common/util/Utilities.java | 7 +- .../account/sign/SignedWitnessService.java | 24 ++- .../witness/AccountAgeWitnessService.java | 11 + .../witness/AccountAgeWitnessUtils.java | 11 +- .../resources/i18n/displayStrings.properties | 6 + .../windows/SignUnsignedPubKeysWindow.java | 196 ++++++++++++++++++ .../agent/arbitration/ArbitratorView.java | 8 +- 7 files changed, 244 insertions(+), 19 deletions(-) create mode 100644 desktop/src/main/java/bisq/desktop/main/overlays/windows/SignUnsignedPubKeysWindow.java diff --git a/common/src/main/java/bisq/common/util/Utilities.java b/common/src/main/java/bisq/common/util/Utilities.java index bfa0d7df54a..6cdd3bf3427 100644 --- a/common/src/main/java/bisq/common/util/Utilities.java +++ b/common/src/main/java/bisq/common/util/Utilities.java @@ -42,10 +42,6 @@ import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; -import javax.crypto.Cipher; - -import java.security.NoSuchAlgorithmException; - import java.net.URI; import java.net.URISyntaxException; @@ -462,8 +458,7 @@ public static int byteArrayToInteger(byte[] bytes) { } // Helper to filter unique elements by key - public static Predicate distinctByKey(Function keyExtractor) - { + public static Predicate distinctByKey(Function keyExtractor) { Map map = new ConcurrentHashMap<>(); return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; } diff --git a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java index 26457f38103..a397a32f026 100644 --- a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java +++ b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java @@ -65,8 +65,6 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import static bisq.common.util.Utilities.distinctByKey; - @Slf4j public class SignedWitnessService { public static final long SIGNER_AGE_DAYS = 30; @@ -224,6 +222,15 @@ public String signAccountAgeWitness(AccountAgeWitness accountAgeWitness, return signAccountAgeWitness(MINIMUM_TRADE_AMOUNT_FOR_SIGNING, accountAgeWitness, key, witnessPubKey, time); } + // Arbitrators sign with EC key + public String signTraderPubKey(ECKey key, + byte[] peersPubKey, + long childSignTime) { + var time = childSignTime - SIGNER_AGE - 1; + var dummyAccountAgeWitness = new AccountAgeWitness(Hash.getRipemd160hash(peersPubKey), time); + return signAccountAgeWitness(MINIMUM_TRADE_AMOUNT_FOR_SIGNING, dummyAccountAgeWitness, key, peersPubKey, time); + } + // Arbitrators sign with EC key private String signAccountAgeWitness(Coin tradeAmount, AccountAgeWitness accountAgeWitness, @@ -350,11 +357,14 @@ public Set getRootSignedWitnessSet(boolean includeSignedByArbitra .collect(Collectors.toSet()); } - public Set getUnsignedSignersPubKeys() { - return getRootSignedWitnessSet(false).stream() - .map(SignedWitness::getSignerPubKey) - .filter(distinctByKey(key -> Utilities.bytesAsHexString(Hash.getRipemd160hash(key)))) - .collect(Collectors.toSet()); + // Find first (in time) SignedWitness per missing signer + public Set getUnsignedSignerPubKeys() { + var oldestUnsignedSigners = new HashMap(); + getRootSignedWitnessSet(false).forEach(signedWitness -> + oldestUnsignedSigners.compute(new P2PDataStorage.ByteArray(signedWitness.getSignerPubKey()), + (key, oldValue) -> oldValue == null ? signedWitness : + oldValue.getDate() > signedWitness.getDate() ? signedWitness : oldValue)); + return new HashSet<>(oldestUnsignedSigners.values()); } // We go one level up by using the signer Key to lookup for SignedWitness objects which contain the signerKey as diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index dde667a3733..f969a56f087 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -17,6 +17,7 @@ package bisq.core.account.witness; +import bisq.core.account.sign.SignedWitness; import bisq.core.account.sign.SignedWitnessService; import bisq.core.filter.FilterManager; import bisq.core.filter.PaymentAccountFilter; @@ -648,6 +649,12 @@ public String arbitratorSignOrphanWitness(AccountAgeWitness accountAgeWitness, time); } + public String arbitratorSignOrphanPubKey(ECKey key, + byte[] peersPubKey, + long childSignTime) { + return signedWitnessService.signTraderPubKey(key, peersPubKey, childSignTime); + } + public void arbitratorSignAccountAgeWitness(AccountAgeWitness accountAgeWitness, ECKey key, byte[] tradersPubKey, @@ -806,4 +813,8 @@ public Set getOrphanSignedWitnesses() { .filter(Objects::nonNull) .collect(Collectors.toSet()); } + + public Set getUnsignedSignerPubKeys() { + return signedWitnessService.getUnsignedSignerPubKeys(); + } } diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java index 4a7918f038a..56983fea866 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java @@ -32,7 +32,6 @@ import java.util.Arrays; import java.util.Optional; import java.util.Stack; -import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @@ -60,8 +59,8 @@ public void logSignedWitnesses() { log.info("Orphaned signed account age witnesses:"); orphanSigners.forEach(w -> { log.info("{}: Signer PKH: {} Owner PKH: {} time: {}", w.getVerificationMethod().toString(), - Utilities.bytesAsHexString(Hash.getRipemd160hash(w.getSignerPubKey())).substring(0,7), - Utilities.bytesAsHexString(Hash.getRipemd160hash(w.getWitnessOwnerPubKey())).substring(0,7), + Utilities.bytesAsHexString(Hash.getRipemd160hash(w.getSignerPubKey())).substring(0, 7), + Utilities.bytesAsHexString(Hash.getRipemd160hash(w.getWitnessOwnerPubKey())).substring(0, 7), w.getDate()); logChild(w, " ", new Stack<>()); }); @@ -100,8 +99,10 @@ public void logSigners() { public void logUnsignedSignerPubKeys() { log.info("Unsigned signer pubkeys"); - signedWitnessService.getUnsignedSignersPubKeys().forEach(pubKey -> - log.info("PK hash {}",Utilities.bytesAsHexString(Hash.getRipemd160hash(pubKey)))); + signedWitnessService.getUnsignedSignerPubKeys().forEach(signedWitness -> + log.info("PK hash {} date {}", + Utilities.bytesAsHexString(Hash.getRipemd160hash(signedWitness.getSignerPubKey())), + signedWitness.getDate())); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index fa38884e6b7..10d9c6000ef 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2739,6 +2739,12 @@ 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 +popup.accountSigning.unsignedPubKeys.signed=Pubkeys were signed +popup.accountSigning.unsignedPubKeys.result.headline=Signing completed +popup.accountSigning.unsignedPubKeys.result.signed=Signed pubkeys +popup.accountSigning.unsignedPubKeys.result.failed=Failed to sign #################################################################### # Notifications diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignUnsignedPubKeysWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignUnsignedPubKeysWindow.java new file mode 100644 index 00000000000..273ab472f76 --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignUnsignedPubKeysWindow.java @@ -0,0 +1,196 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.main.overlays.windows; + +import bisq.desktop.components.AutoTooltipButton; +import bisq.desktop.components.InputTextField; +import bisq.desktop.main.overlays.Overlay; +import bisq.desktop.main.overlays.popups.Popup; + +import bisq.core.account.sign.SignedWitness; +import bisq.core.account.witness.AccountAgeWitnessService; +import bisq.core.locale.Res; +import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; + +import bisq.common.crypto.Hash; +import bisq.common.util.Tuple3; +import bisq.common.util.Utilities; + +import org.bitcoinj.core.Utils; + +import javax.inject.Inject; + +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; + +import javafx.geometry.VPos; + +import javafx.collections.FXCollections; + +import javafx.util.Callback; + +import java.util.ArrayList; +import java.util.List; + +import lombok.extern.slf4j.Slf4j; + +import static bisq.desktop.util.FormBuilder.*; + +@Slf4j +public class SignUnsignedPubKeysWindow extends Overlay { + + private InputTextField searchTextField; + private ListView unsignedPubKeys = new ListView<>(); + private InputTextField privateKey; + private final AccountAgeWitnessService accountAgeWitnessService; + private final ArbitratorManager arbitratorManager; + private ListView signedWitnessListView = new ListView<>(); + private ListView failedView = new ListView<>(); + private List signedWitnessList = new ArrayList<>(); + private List failed = new ArrayList<>(); + private Callback, ListCell> signedWitnessCellFactory; + + @Inject + public SignUnsignedPubKeysWindow(AccountAgeWitnessService accountAgeWitnessService, + ArbitratorManager arbitratorManager) { + this.accountAgeWitnessService = accountAgeWitnessService; + this.arbitratorManager = arbitratorManager; + + signedWitnessCellFactory = new Callback<>() { + @Override + public ListCell call( + ListView param) { + return new ListCell<>() { + @Override + protected void updateItem(SignedWitness item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty) { + setText(Utilities.bytesAsHexString(Hash.getRipemd160hash(item.getSignerPubKey()))); + } else { + setText(null); + } + } + }; + } + }; + } + + @Override + public void show() { + width = 1000; + rowIndex = -1; + createGridPane(); + + gridPane.setPrefHeight(600); + gridPane.getColumnConstraints().get(1).setHgrow(Priority.NEVER); + headLine(Res.get("popup.accountSigning.singleAccountSelect.headline")); + type = Type.Attention; + + addHeadLine(); + addUnsignedPubKeysContent(); + addECKeyField(); + addButtons(); + applyStyles(); + + display(); + } + + private void addUnsignedPubKeysContent() { + Tuple3, VBox> unsignedPubKeysTuple = addTopLabelListView(gridPane, ++rowIndex, + Res.get("popup.accountSigning.unsignedPubKeys.headline")); + unsignedPubKeys = unsignedPubKeysTuple.second; + unsignedPubKeys.setCellFactory(signedWitnessCellFactory); + unsignedPubKeys.setItems(FXCollections.observableArrayList( + accountAgeWitnessService.getUnsignedSignerPubKeys())); + } + + private void addECKeyField() { + privateKey = addInputTextField(gridPane, ++rowIndex, Res.get("popup.accountSigning.signAccounts.ECKey")); + GridPane.setVgrow(privateKey, Priority.ALWAYS); + GridPane.setValignment(privateKey, VPos.TOP); + } + + private void removeContent() { + removeRowsFromGridPane(gridPane, 1, 3); + rowIndex = 1; + } + + private void signPubKeys() { + removeContent(); + headLineLabel.setText(Res.get("popup.accountSigning.unsignedPubKeys.signed")); + var arbitratorKey = arbitratorManager.getRegistrationKey(privateKey.getText()); + if (arbitratorKey != null) { + var arbitratorPubKeyAsHex = Utils.HEX.encode(arbitratorKey.getPubKey()); + var isKeyValid = arbitratorManager.isPublicKeyInList(arbitratorPubKeyAsHex); + failed.clear(); + if (isKeyValid) { + unsignedPubKeys.getItems().forEach(signedWitness -> { + var result = accountAgeWitnessService.arbitratorSignOrphanPubKey(arbitratorKey, + signedWitness.getSignerPubKey(), signedWitness.getDate()); + if (result.isEmpty()) { + signedWitnessList.add(signedWitness); + } else { + failed.add("Signing pubkey " + Utilities.bytesAsHexString(Hash.getRipemd160hash( + signedWitness.getSignerPubKey())) + " failed with error " + result); + } + }); + showResult(); + } + } else { + new Popup().error(Res.get("popup.accountSigning.signAccounts.ECKey.error")).onClose(this::hide).show(); + } + } + + private void showResult() { + removeContent(); + closeButton.setVisible(false); + closeButton.setManaged(false); + + Tuple3, VBox> signedTuple = addTopLabelListView(gridPane, ++rowIndex, + Res.get("popup.accountSigning.unsignedPubKeys.result.signed")); + signedWitnessListView = signedTuple.second; + signedWitnessListView.setCellFactory(signedWitnessCellFactory); + signedWitnessListView.setItems(FXCollections.observableArrayList(signedWitnessList)); + Tuple3, VBox> failedTuple = addTopLabelListView(gridPane, ++rowIndex, + Res.get("popup.accountSigning.unsignedPubKeys.result.failed")); + failedView = failedTuple.second; + failedView.setItems(FXCollections.observableArrayList(failed)); + + ((AutoTooltipButton) actionButton).updateText(Res.get("shared.ok")); + actionButton.setOnAction(a -> hide()); + } + + @Override + protected void addButtons() { + var buttonTuple = add2ButtonsAfterGroup(gridPane, ++rowIndex + 1, + Res.get("popup.accountSigning.unsignedPubKeys.sign"), Res.get("shared.cancel")); + + actionButton = buttonTuple.first; + actionButton.setDisable(unsignedPubKeys.getItems().size() == 0); + actionButton.setOnAction(e -> signPubKeys()); + + closeButton = (AutoTooltipButton) buttonTuple.second; + closeButton.setOnAction(e -> hide()); + + } +} diff --git a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/arbitration/ArbitratorView.java b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/arbitration/ArbitratorView.java index d8d87c73844..90e1ceb0935 100644 --- a/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/arbitration/ArbitratorView.java +++ b/desktop/src/main/java/bisq/desktop/main/support/dispute/agent/arbitration/ArbitratorView.java @@ -24,6 +24,7 @@ import bisq.desktop.main.overlays.windows.DisputeSummaryWindow; import bisq.desktop.main.overlays.windows.SignPaymentAccountsWindow; import bisq.desktop.main.overlays.windows.SignSpecificWitnessWindow; +import bisq.desktop.main.overlays.windows.SignUnsignedPubKeysWindow; import bisq.desktop.main.overlays.windows.TradeDetailsWindow; import bisq.desktop.main.support.dispute.agent.DisputeAgentView; @@ -51,6 +52,7 @@ public class ArbitratorView extends DisputeAgentView { private final SignPaymentAccountsWindow signPaymentAccountsWindow; private final SignSpecificWitnessWindow signSpecificWitnessWindow; + private final SignUnsignedPubKeysWindow signUnsignedPubKeysWindow; @Inject public ArbitratorView(ArbitrationManager arbitrationManager, @@ -64,7 +66,8 @@ public ArbitratorView(ArbitrationManager arbitrationManager, AccountAgeWitnessService accountAgeWitnessService, @Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys, SignPaymentAccountsWindow signPaymentAccountsWindow, - SignSpecificWitnessWindow signSpecificWitnessWindow) { + SignSpecificWitnessWindow signSpecificWitnessWindow, + SignUnsignedPubKeysWindow signUnsignedPubKeysWindow) { super(arbitrationManager, keyRing, tradeManager, @@ -77,6 +80,7 @@ public ArbitratorView(ArbitrationManager arbitrationManager, useDevPrivilegeKeys); this.signPaymentAccountsWindow = signPaymentAccountsWindow; this.signSpecificWitnessWindow = signSpecificWitnessWindow; + this.signUnsignedPubKeysWindow = signUnsignedPubKeysWindow; } @Override @@ -95,6 +99,8 @@ protected void handleKeyPressed(KeyEvent event) { signPaymentAccountsWindow.show(); } else if (Utilities.isAltOrCtrlPressed(KeyCode.P, event)) { signSpecificWitnessWindow.show(); + } else if (Utilities.isAltOrCtrlPressed(KeyCode.O, event)) { + signUnsignedPubKeysWindow.show(); } } } From 9a8622c410c3380acdb205c895ca722843de72e2 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Fri, 3 Jul 2020 17:35:54 +0200 Subject: [PATCH 7/7] Fix codacy comments --- .../account/sign/SignedWitnessService.java | 26 +++++++++---------- .../windows/SignUnsignedPubKeysWindow.java | 12 ++++----- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java index cd70333aeea..e3a22f08982 100644 --- a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java +++ b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java @@ -51,7 +51,6 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -59,7 +58,6 @@ import java.util.Map; import java.util.Set; import java.util.Stack; -import java.util.stream.Collector; import java.util.stream.Collectors; import lombok.Getter; @@ -489,16 +487,16 @@ private void doRepublishAllSignedWitnesses() { // Remove SignedWitnesses that are signed by TRADE that also have an ARBITRATOR signature // for the same ownerPubKey and AccountAgeWitnessHash - private void cleanSignedWitnesses() { - var orphans = getRootSignedWitnessSet(false); - var signedWitnessesCopy = new HashSet<>(signedWitnessMap.values()); - signedWitnessesCopy.forEach(sw -> orphans.forEach(orphan -> { - if (sw.getVerificationMethod() == SignedWitness.VerificationMethod.ARBITRATOR && - Arrays.equals(sw.getWitnessOwnerPubKey(), orphan.getWitnessOwnerPubKey()) && - Arrays.equals(sw.getAccountAgeWitnessHash(), orphan.getAccountAgeWitnessHash())) { - signedWitnessMap.remove(orphan.getHashAsByteArray()); - log.info("Remove duplicate SignedWitness: {}", orphan.toString()); - } - })); - } +// private void cleanSignedWitnesses() { +// var orphans = getRootSignedWitnessSet(false); +// var signedWitnessesCopy = new HashSet<>(signedWitnessMap.values()); +// signedWitnessesCopy.forEach(sw -> orphans.forEach(orphan -> { +// if (sw.getVerificationMethod() == SignedWitness.VerificationMethod.ARBITRATOR && +// Arrays.equals(sw.getWitnessOwnerPubKey(), orphan.getWitnessOwnerPubKey()) && +// Arrays.equals(sw.getAccountAgeWitnessHash(), orphan.getAccountAgeWitnessHash())) { +// signedWitnessMap.remove(orphan.getHashAsByteArray()); +// log.info("Remove duplicate SignedWitness: {}", orphan.toString()); +// } +// })); +// } } diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignUnsignedPubKeysWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignUnsignedPubKeysWindow.java index 273ab472f76..97f3d67a24d 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignUnsignedPubKeysWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignUnsignedPubKeysWindow.java @@ -53,18 +53,18 @@ import lombok.extern.slf4j.Slf4j; -import static bisq.desktop.util.FormBuilder.*; +import static bisq.desktop.util.FormBuilder.add2ButtonsAfterGroup; +import static bisq.desktop.util.FormBuilder.addInputTextField; +import static bisq.desktop.util.FormBuilder.addTopLabelListView; +import static bisq.desktop.util.FormBuilder.removeRowsFromGridPane; @Slf4j public class SignUnsignedPubKeysWindow extends Overlay { - private InputTextField searchTextField; private ListView unsignedPubKeys = new ListView<>(); private InputTextField privateKey; private final AccountAgeWitnessService accountAgeWitnessService; private final ArbitratorManager arbitratorManager; - private ListView signedWitnessListView = new ListView<>(); - private ListView failedView = new ListView<>(); private List signedWitnessList = new ArrayList<>(); private List failed = new ArrayList<>(); private Callback, ListCell> signedWitnessCellFactory; @@ -168,12 +168,12 @@ private void showResult() { Tuple3, VBox> signedTuple = addTopLabelListView(gridPane, ++rowIndex, Res.get("popup.accountSigning.unsignedPubKeys.result.signed")); - signedWitnessListView = signedTuple.second; + ListView signedWitnessListView = signedTuple.second; signedWitnessListView.setCellFactory(signedWitnessCellFactory); signedWitnessListView.setItems(FXCollections.observableArrayList(signedWitnessList)); Tuple3, VBox> failedTuple = addTopLabelListView(gridPane, ++rowIndex, Res.get("popup.accountSigning.unsignedPubKeys.result.failed")); - failedView = failedTuple.second; + ListView failedView = failedTuple.second; failedView.setItems(FXCollections.observableArrayList(failed)); ((AutoTooltipButton) actionButton).updateText(Res.get("shared.ok"));