From e06fb284e4eabb7e5fe4a50ba08951826ce47848 Mon Sep 17 00:00:00 2001 From: Bernard Labno Date: Tue, 5 Nov 2019 20:14:30 +0100 Subject: [PATCH 1/3] Move Payment account creation and removal from UI to core --- .../core/exceptions/NotFoundException.java | 24 ++ .../core/exceptions/ValidationException.java | 39 ++ .../payment/PaymentAccountInUseException.java | 24 ++ .../core/payment/PaymentAccountManager.java | 130 +++++++ .../validation/AltCoinAddressValidator.java | 12 +- .../main/java/bisq/core/user/Preferences.java | 2 +- .../payment/PaymentAccountManagerTest.java | 358 ++++++++++++++++++ .../AltCoinAccountsDataModel.java | 69 +--- .../fiataccounts/FiatAccountsDataModel.java | 66 +--- 9 files changed, 613 insertions(+), 111 deletions(-) create mode 100644 core/src/main/java/bisq/core/exceptions/NotFoundException.java create mode 100644 core/src/main/java/bisq/core/exceptions/ValidationException.java create mode 100644 core/src/main/java/bisq/core/payment/PaymentAccountInUseException.java create mode 100644 core/src/main/java/bisq/core/payment/PaymentAccountManager.java create mode 100644 core/src/test/java/bisq/core/payment/PaymentAccountManagerTest.java diff --git a/core/src/main/java/bisq/core/exceptions/NotFoundException.java b/core/src/main/java/bisq/core/exceptions/NotFoundException.java new file mode 100644 index 00000000000..c59fd1be9fe --- /dev/null +++ b/core/src/main/java/bisq/core/exceptions/NotFoundException.java @@ -0,0 +1,24 @@ +/* + * 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.exceptions; + +public class NotFoundException extends RuntimeException { + public NotFoundException(String message) { + super(message); + } +} diff --git a/core/src/main/java/bisq/core/exceptions/ValidationException.java b/core/src/main/java/bisq/core/exceptions/ValidationException.java new file mode 100644 index 00000000000..ac0de27971d --- /dev/null +++ b/core/src/main/java/bisq/core/exceptions/ValidationException.java @@ -0,0 +1,39 @@ +/* + * 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.exceptions; + +/** + * Copy of ValidationException from javax.validation:validation-api + */ +public class ValidationException extends RuntimeException { + public ValidationException(String message) { + super(message); + } + + public ValidationException() { + super(); + } + + public ValidationException(String message, Throwable cause) { + super(message, cause); + } + + public ValidationException(Throwable cause) { + super(cause); + } +} diff --git a/core/src/main/java/bisq/core/payment/PaymentAccountInUseException.java b/core/src/main/java/bisq/core/payment/PaymentAccountInUseException.java new file mode 100644 index 00000000000..c509e7c6f37 --- /dev/null +++ b/core/src/main/java/bisq/core/payment/PaymentAccountInUseException.java @@ -0,0 +1,24 @@ +/* + * 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.payment; + +public class PaymentAccountInUseException extends RuntimeException { + public PaymentAccountInUseException(String msg) { + super(msg); + } +} diff --git a/core/src/main/java/bisq/core/payment/PaymentAccountManager.java b/core/src/main/java/bisq/core/payment/PaymentAccountManager.java new file mode 100644 index 00000000000..57e6af036a7 --- /dev/null +++ b/core/src/main/java/bisq/core/payment/PaymentAccountManager.java @@ -0,0 +1,130 @@ +/* + * 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.payment; + +import bisq.core.account.witness.AccountAgeWitnessService; +import bisq.core.exceptions.NotFoundException; +import bisq.core.exceptions.ValidationException; +import bisq.core.locale.CryptoCurrency; +import bisq.core.locale.CurrencyUtil; +import bisq.core.locale.FiatCurrency; +import bisq.core.locale.TradeCurrency; +import bisq.core.offer.Offer; +import bisq.core.offer.OpenOfferManager; +import bisq.core.payment.validation.AltCoinAddressValidator; +import bisq.core.trade.TradeManager; +import bisq.core.user.Preferences; +import bisq.core.user.User; +import bisq.core.util.validation.InputValidator; + +import com.google.inject.Inject; + +import java.util.List; + +import static java.lang.String.format; + +public class PaymentAccountManager { + + private final AccountAgeWitnessService accountAgeWitnessService; + private final OpenOfferManager openOfferManager; + private final Preferences preferences; + private final TradeManager tradeManager; + private final User user; + private AltCoinAddressValidator altCoinAddressValidator; + + @Inject + public PaymentAccountManager(AccountAgeWitnessService accountAgeWitnessService, + AltCoinAddressValidator altCoinAddressValidator, + OpenOfferManager openOfferManager, + Preferences preferences, + TradeManager tradeManager, + User user) { + this.accountAgeWitnessService = accountAgeWitnessService; + this.altCoinAddressValidator = altCoinAddressValidator; + this.openOfferManager = openOfferManager; + this.preferences = preferences; + this.tradeManager = tradeManager; + this.user = user; + } + + public PaymentAccount addPaymentAccount(PaymentAccount paymentAccount) { + if (paymentAccount instanceof CryptoCurrencyAccount) { + CryptoCurrencyAccount cryptoCurrencyAccount = (CryptoCurrencyAccount) paymentAccount; + TradeCurrency tradeCurrency = cryptoCurrencyAccount.getSingleTradeCurrency(); + if (tradeCurrency == null) { + throw new ValidationException("CryptoCurrency account must have exactly one trade currency"); + } + altCoinAddressValidator.setCurrencyCode(tradeCurrency.getCode()); + InputValidator.ValidationResult validationResult = altCoinAddressValidator.validate(cryptoCurrencyAccount.getAddress()); + if (!validationResult.isValid) { + throw new ValidationException(validationResult.errorMessage); + } + } + + TradeCurrency singleTradeCurrency = paymentAccount.getSingleTradeCurrency(); + List tradeCurrencies = paymentAccount.getTradeCurrencies(); + if (singleTradeCurrency != null) { + if (singleTradeCurrency instanceof FiatCurrency) + preferences.addFiatCurrency((FiatCurrency) singleTradeCurrency); + else + preferences.addCryptoCurrency((CryptoCurrency) singleTradeCurrency); + } else if (!tradeCurrencies.isEmpty()) { + tradeCurrencies.forEach(tradeCurrency -> { + if (tradeCurrency instanceof FiatCurrency) + preferences.addFiatCurrency((FiatCurrency) tradeCurrency); + else + preferences.addCryptoCurrency((CryptoCurrency) tradeCurrency); + }); + } + + user.addPaymentAccount(paymentAccount); + + if (!(paymentAccount instanceof CryptoCurrencyAccount)) { + if (singleTradeCurrency == null && !tradeCurrencies.isEmpty()) { + if (tradeCurrencies.contains(CurrencyUtil.getDefaultTradeCurrency())) + paymentAccount.setSelectedTradeCurrency(CurrencyUtil.getDefaultTradeCurrency()); + else + paymentAccount.setSelectedTradeCurrency(tradeCurrencies.get(0)); + + } + accountAgeWitnessService.publishMyAccountAgeWitness(paymentAccount.getPaymentAccountPayload()); + } + return paymentAccount; + } + + public void removePaymentAccount(String id) { + PaymentAccount paymentAccount = user.getPaymentAccount(id); + if (paymentAccount == null) { + throw new NotFoundException(format("Payment account %s not found", id)); + } + boolean isPaymentAccountUsed = openOfferManager.getObservableList().stream() + .anyMatch(openOffer -> id.equals(openOffer.getOffer().getMakerPaymentAccountId())); + if (isPaymentAccountUsed) { + throw new PaymentAccountInUseException(format("Payment account %s is used for open offer", id)); + } + isPaymentAccountUsed = tradeManager.getTradableList().stream() + .anyMatch(trade -> { + Offer offer = trade.getOffer(); + return null != offer && id.equals(offer.getMakerPaymentAccountId()) || id.equals(trade.getTakerPaymentAccountId()); + }); + if (isPaymentAccountUsed) { + throw new PaymentAccountInUseException(format("Payment account %s is used for open trade", id)); + } + user.removePaymentAccount(paymentAccount); + } +} diff --git a/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java b/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java index c6028c52e64..fe9a614fea3 100644 --- a/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java +++ b/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java @@ -22,6 +22,10 @@ import bisq.core.locale.Res; import bisq.core.util.validation.InputValidator; +import bisq.asset.AddressValidationResult; +import bisq.asset.Asset; +import bisq.asset.AssetRegistry; + import bisq.common.app.DevEnv; import com.google.inject.Inject; @@ -30,14 +34,8 @@ import lombok.extern.slf4j.Slf4j; - - -import bisq.asset.AddressValidationResult; -import bisq.asset.Asset; -import bisq.asset.AssetRegistry; - @Slf4j -public final class AltCoinAddressValidator extends InputValidator { +public class AltCoinAddressValidator extends InputValidator { private final AssetRegistry assetRegistry; private String currencyCode; diff --git a/core/src/main/java/bisq/core/user/Preferences.java b/core/src/main/java/bisq/core/user/Preferences.java index d022eb9dae9..a69bbbebcb8 100644 --- a/core/src/main/java/bisq/core/user/Preferences.java +++ b/core/src/main/java/bisq/core/user/Preferences.java @@ -78,7 +78,7 @@ @Slf4j @Singleton -public final class Preferences implements PersistedDataHost, BridgeAddressProvider { +public class Preferences implements PersistedDataHost, BridgeAddressProvider { private static final ArrayList BTC_MAIN_NET_EXPLORERS = new ArrayList<>(Arrays.asList( new BlockChainExplorer("Blockstream.info", "https://blockstream.info/tx/", "https://blockstream.info/address/"), diff --git a/core/src/test/java/bisq/core/payment/PaymentAccountManagerTest.java b/core/src/test/java/bisq/core/payment/PaymentAccountManagerTest.java new file mode 100644 index 00000000000..a0ea884ffc5 --- /dev/null +++ b/core/src/test/java/bisq/core/payment/PaymentAccountManagerTest.java @@ -0,0 +1,358 @@ +/* + * 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.payment; + +import bisq.core.account.witness.AccountAgeWitnessService; +import bisq.core.exceptions.NotFoundException; +import bisq.core.exceptions.ValidationException; +import bisq.core.locale.CryptoCurrency; +import bisq.core.locale.FiatCurrency; +import bisq.core.locale.GlobalSettings; +import bisq.core.locale.TradeCurrency; +import bisq.core.offer.Offer; +import bisq.core.offer.OpenOffer; +import bisq.core.offer.OpenOfferManager; +import bisq.core.payment.payload.PaymentAccountPayload; +import bisq.core.payment.validation.AltCoinAddressValidator; +import bisq.core.trade.Trade; +import bisq.core.trade.TradeManager; +import bisq.core.user.Preferences; +import bisq.core.user.User; +import bisq.core.util.validation.InputValidator; + +import javafx.collections.FXCollections; + +import java.util.UUID; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static java.lang.String.format; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class PaymentAccountManagerTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + private AccountAgeWitnessService accountAgeWitnessService; + private AltCoinAddressValidator altCoinAddressValidator; + private OpenOfferManager openOfferManager; + private PaymentAccountManager paymentAccountManager; + private Preferences preferences; + private TradeManager tradeManager; + private User user; + private TradeCurrency globalDefaultTradeCurrency; + private FiatCurrency defaultTradeCurrency; + + @Before + public void setUp() { + accountAgeWitnessService = mock(AccountAgeWitnessService.class); + altCoinAddressValidator = mock(AltCoinAddressValidator.class); + openOfferManager = mock(OpenOfferManager.class); + preferences = mock(Preferences.class); + tradeManager = mock(TradeManager.class); + user = mock(User.class); + paymentAccountManager = new PaymentAccountManager(accountAgeWitnessService, altCoinAddressValidator, openOfferManager, preferences, tradeManager, user); + globalDefaultTradeCurrency = GlobalSettings.getDefaultTradeCurrency(); + defaultTradeCurrency = new FiatCurrency("CAD"); + GlobalSettings.setDefaultTradeCurrency(defaultTradeCurrency); + } + + @After + public void tearDown() { + GlobalSettings.setDefaultTradeCurrency(globalDefaultTradeCurrency); + } + + @Test + public void addPaymentAccount_cryptoAccountMissingSingleTradeCurrency_throwsValidationException() { + // Given + CryptoCurrencyAccount account = new CryptoCurrencyAccount(); + expectedException.expect(ValidationException.class); + expectedException.expectMessage("CryptoCurrency account must have exactly one trade currency"); + + // When + paymentAccountManager.addPaymentAccount(account); + } + + @Test + public void addPaymentAccount_invalidAddressForCryptoAccount_throwsValidationException() { + // Given + CryptoCurrencyAccount account = new CryptoCurrencyAccount(); + account.init(); + account.setSingleTradeCurrency(new CryptoCurrency("ABCDEF", "Abc")); + String validationErrorMessage = UUID.randomUUID().toString(); + String address = UUID.randomUUID().toString(); + account.setAddress(address); + InputValidator.ValidationResult validationResult = new InputValidator.ValidationResult(false, validationErrorMessage); + when(altCoinAddressValidator.validate(address)).thenReturn(validationResult); + expectedException.expect(ValidationException.class); + expectedException.expectMessage(validationErrorMessage); + + // When + paymentAccountManager.addPaymentAccount(account); + } + + @Ignore("Not implemented yet") + @Test + public void addPaymentAccount_validationFailsForFiatAccount_throwsValidationException() { + // Given + + // When + + // Then + fail("Not implemented yet"); + } + + @Test + public void addPaymentAccount_validationSuccess_callsAddPaymentAccountOnTheUser() { + // Given + AliPayAccount account = new AliPayAccount(); + account.init(); + String address = UUID.randomUUID().toString(); + account.setAccountNr(address); + + // When + paymentAccountManager.addPaymentAccount(account); + + // Then + verify(user).addPaymentAccount(account); + } + + @Test + public void addPaymentAccount_validationSuccess_returnsSamePaymentAccount() { + // Given + CashDepositAccount account = new CashDepositAccount(); + account.init(); + String requirements = UUID.randomUUID().toString(); + account.setRequirements(requirements); + + // When + PaymentAccount result = paymentAccountManager.addPaymentAccount(account); + + // Then + assertSame(account, result); + } + + @Test + public void addPaymentAccount_singleTradeCurrencyIsFiat_addsCurrencyToPreferences() { + // Given + CashDepositAccount account = new CashDepositAccount(); + account.init(); + FiatCurrency tradeCurrency = new FiatCurrency("USD"); + account.setSingleTradeCurrency(tradeCurrency); + + // When + paymentAccountManager.addPaymentAccount(account); + + // Then + verify(preferences).addFiatCurrency(tradeCurrency); + } + + @Test + public void addPaymentAccount_singleTradeCurrencyIsCrypto_addsCurrencyToPreferences() { + // Given + CashDepositAccount account = new CashDepositAccount(); + account.init(); + CryptoCurrency tradeCurrency = new CryptoCurrency("XBT", "Bitcoin"); + account.setSingleTradeCurrency(tradeCurrency); + + // When + paymentAccountManager.addPaymentAccount(account); + + // Then + verify(preferences).addCryptoCurrency(tradeCurrency); + } + + @Test + public void addPaymentAccount_oneFiatAndOneCryptoTradeCurrency_addsCurrenciesToPreferences() { + // Given + CashDepositAccount account = new CashDepositAccount(); + account.init(); + FiatCurrency fiatCurrency = new FiatCurrency("GBP"); + CryptoCurrency cryptoCurrency = new CryptoCurrency("XMR", "Monero"); + account.getTradeCurrencies().add(fiatCurrency); + account.getTradeCurrencies().add(cryptoCurrency); + + // When + paymentAccountManager.addPaymentAccount(account); + + // Then + verify(preferences).addCryptoCurrency(cryptoCurrency); + verify(preferences).addFiatCurrency(fiatCurrency); + } + + @Test + public void addPaymentAccount_cryptoAccount_doesNotPublishAccountAgeWitness() { + // Given + when(altCoinAddressValidator.validate(any())).thenReturn(new InputValidator.ValidationResult(true)); + CryptoCurrencyAccount account = new CryptoCurrencyAccount(); + account.init(); + account.setSingleTradeCurrency(new CryptoCurrency("XMR", "Monero")); + account.setAddress(UUID.randomUUID().toString()); + PaymentAccountPayload paymentAccountPayload = account.getPaymentAccountPayload(); + + // When + paymentAccountManager.addPaymentAccount(account); + + // Then + assertNotNull(paymentAccountPayload); + verify(accountAgeWitnessService, never()).publishMyAccountAgeWitness(paymentAccountPayload); + } + + @Test + public void addPaymentAccount_fiatAccount_publishesAccountAgeWitness() { + // Given + RevolutAccount account = new RevolutAccount(); + account.init(); + PaymentAccountPayload paymentAccountPayload = account.getPaymentAccountPayload(); + + // When + paymentAccountManager.addPaymentAccount(account); + + // Then + assertNotNull(paymentAccountPayload); + verify(accountAgeWitnessService).publishMyAccountAgeWitness(paymentAccountPayload); + } + + @Test + public void addPaymentAccount_fiatAccountHasDefaultTradeCurrencyAsSecondCurrency_setDefaultTradeCurrencyAsSelectedTradeCurrency() { + // Given + RevolutAccount account = new RevolutAccount(); + account.init(); + account.getTradeCurrencies().clear(); + FiatCurrency aud = new FiatCurrency("AUD"); + account.getTradeCurrencies().add(aud); + account.getTradeCurrencies().add(defaultTradeCurrency); + + // When + paymentAccountManager.addPaymentAccount(account); + + // Then + assertNotEquals(defaultTradeCurrency, aud); + assertEquals(defaultTradeCurrency, account.getSelectedTradeCurrency()); + } + + @Test + public void addPaymentAccount_multipleTradeCurrencies_setFirstOneAsSelectedTradeCurrency() { + // Given + RevolutAccount account = new RevolutAccount(); + account.init(); + account.getTradeCurrencies().clear(); + FiatCurrency gbp = new FiatCurrency("GBP"); + FiatCurrency aud = new FiatCurrency("AUD"); + account.getTradeCurrencies().add(gbp); + account.getTradeCurrencies().add(aud); + + // When + paymentAccountManager.addPaymentAccount(account); + + // Then + assertNotEquals(defaultTradeCurrency, aud); + assertNotEquals(defaultTradeCurrency, gbp); + assertEquals(gbp, account.getSelectedTradeCurrency()); + } + + @Test + public void removePaymentAccount_nullId_throwNotFoundException() { +// Given + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("Payment account null not found"); +// When + paymentAccountManager.removePaymentAccount(null); + } + + @Test + public void removePaymentAccount_accountUsedInOpenOffer_throwsException() { +// Given + String id = getRandomString(); + when(user.getPaymentAccount(id)).thenReturn(mock(PaymentAccount.class)); + Offer offer = mock(Offer.class); + when(offer.getMakerPaymentAccountId()).thenReturn(id); + OpenOffer openOffer = mock(OpenOffer.class); + when(openOffer.getOffer()).thenReturn(offer); + when(openOfferManager.getObservableList()).thenReturn(FXCollections.observableArrayList(openOffer)); + expectedException.expect(PaymentAccountInUseException.class); + expectedException.expectMessage(format("Payment account %s is used for open offer", id)); + +// When + paymentAccountManager.removePaymentAccount(id); + } + + @Test + public void removePaymentAccount_accountUsedInOpenTradeForMaker_throwsException() { +// Given + String id = getRandomString(); + when(user.getPaymentAccount(id)).thenReturn(mock(PaymentAccount.class)); + Offer offer = mock(Offer.class); + when(offer.getMakerPaymentAccountId()).thenReturn(id); + Trade trade = mock(Trade.class); + when(trade.getOffer()).thenReturn(offer); + when(tradeManager.getTradableList()).thenReturn(FXCollections.observableArrayList(trade)); + when(openOfferManager.getObservableList()).thenReturn(FXCollections.observableArrayList()); + expectedException.expect(PaymentAccountInUseException.class); + expectedException.expectMessage(format("Payment account %s is used for open trade", id)); + +// When + paymentAccountManager.removePaymentAccount(id); + } + + @Test + public void removePaymentAccount_accountUsedInOpenTradeForTaker_throwsException() { +// Given + String id = getRandomString(); + when(user.getPaymentAccount(id)).thenReturn(mock(PaymentAccount.class)); + Trade trade = mock(Trade.class); + when(trade.getTakerPaymentAccountId()).thenReturn(id); + when(tradeManager.getTradableList()).thenReturn(FXCollections.observableArrayList(trade)); + when(openOfferManager.getObservableList()).thenReturn(FXCollections.observableArrayList()); + expectedException.expect(PaymentAccountInUseException.class); + expectedException.expectMessage(format("Payment account %s is used for open trade", id)); + +// When + paymentAccountManager.removePaymentAccount(id); + } + + @Test + public void removePaymentAccount_accountExistAndIsNotUsed_removeAccount() { +// Given + String id = getRandomString(); + PaymentAccount paymentAccount = mock(PaymentAccount.class); + when(user.getPaymentAccount(id)).thenReturn(paymentAccount); + when(tradeManager.getTradableList()).thenReturn(FXCollections.observableArrayList()); + when(openOfferManager.getObservableList()).thenReturn(FXCollections.observableArrayList()); + +// When + paymentAccountManager.removePaymentAccount(id); + +// Then + verify(user).removePaymentAccount(paymentAccount); + } + + private String getRandomString() { + return UUID.randomUUID().toString(); + } +} diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java index 4d315a9293b..30b6fe8eca8 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java @@ -20,14 +20,10 @@ import bisq.desktop.common.model.ActivatableDataModel; import bisq.desktop.util.GUIUtil; -import bisq.core.account.witness.AccountAgeWitnessService; -import bisq.core.locale.CryptoCurrency; -import bisq.core.locale.FiatCurrency; -import bisq.core.locale.TradeCurrency; -import bisq.core.offer.OpenOfferManager; import bisq.core.payment.AssetAccount; import bisq.core.payment.PaymentAccount; -import bisq.core.trade.TradeManager; +import bisq.core.payment.PaymentAccountInUseException; +import bisq.core.payment.PaymentAccountManager; import bisq.core.user.Preferences; import bisq.core.user.User; @@ -43,16 +39,14 @@ import javafx.collections.SetChangeListener; import java.util.ArrayList; -import java.util.List; +import java.util.Comparator; import java.util.stream.Collectors; class AltCoinAccountsDataModel extends ActivatableDataModel { + private final PaymentAccountManager paymentAccountManager; private final User user; private final Preferences preferences; - private final OpenOfferManager openOfferManager; - private final TradeManager tradeManager; - private final AccountAgeWitnessService accountAgeWitnessService; final ObservableList paymentAccounts = FXCollections.observableArrayList(); private final SetChangeListener setChangeListener; private final String accountsFileName = "AltcoinPaymentAccounts"; @@ -60,18 +54,13 @@ class AltCoinAccountsDataModel extends ActivatableDataModel { private final CorruptedDatabaseFilesHandler corruptedDatabaseFilesHandler; @Inject - public AltCoinAccountsDataModel(User user, + public AltCoinAccountsDataModel(PaymentAccountManager paymentAccountManager, User user, Preferences preferences, - OpenOfferManager openOfferManager, - TradeManager tradeManager, - AccountAgeWitnessService accountAgeWitnessService, PersistenceProtoResolver persistenceProtoResolver, CorruptedDatabaseFilesHandler corruptedDatabaseFilesHandler) { + this.paymentAccountManager = paymentAccountManager; this.user = user; this.preferences = preferences; - this.openOfferManager = openOfferManager; - this.tradeManager = tradeManager; - this.accountAgeWitnessService = accountAgeWitnessService; this.persistenceProtoResolver = persistenceProtoResolver; this.corruptedDatabaseFilesHandler = corruptedDatabaseFilesHandler; setChangeListener = change -> fillAndSortPaymentAccounts(); @@ -88,7 +77,7 @@ private void fillAndSortPaymentAccounts() { paymentAccounts.setAll(user.getPaymentAccounts().stream() .filter(paymentAccount -> paymentAccount.getPaymentMethod().isAsset()) .collect(Collectors.toList())); - paymentAccounts.sort((o1, o2) -> o1.getCreationDate().compareTo(o2.getCreationDate())); + paymentAccounts.sort(Comparator.comparing(PaymentAccount::getCreationDate)); } } @@ -103,41 +92,16 @@ protected void deactivate() { /////////////////////////////////////////////////////////////////////////////////////////// public void onSaveNewAccount(PaymentAccount paymentAccount) { - TradeCurrency singleTradeCurrency = paymentAccount.getSingleTradeCurrency(); - List tradeCurrencies = paymentAccount.getTradeCurrencies(); - if (singleTradeCurrency != null) { - if (singleTradeCurrency instanceof FiatCurrency) - preferences.addFiatCurrency((FiatCurrency) singleTradeCurrency); - else - preferences.addCryptoCurrency((CryptoCurrency) singleTradeCurrency); - } else if (tradeCurrencies != null && !tradeCurrencies.isEmpty()) { - tradeCurrencies.stream().forEach(tradeCurrency -> { - if (tradeCurrency instanceof FiatCurrency) - preferences.addFiatCurrency((FiatCurrency) tradeCurrency); - else - preferences.addCryptoCurrency((CryptoCurrency) tradeCurrency); - }); - } - - user.addPaymentAccount(paymentAccount); - - if (!(paymentAccount instanceof AssetAccount)) - accountAgeWitnessService.publishMyAccountAgeWitness(paymentAccount.getPaymentAccountPayload()); + paymentAccountManager.addPaymentAccount(paymentAccount); } public boolean onDeleteAccount(PaymentAccount paymentAccount) { - boolean isPaymentAccountUsed = openOfferManager.getObservableList().stream() - .filter(o -> o.getOffer().getMakerPaymentAccountId().equals(paymentAccount.getId())) - .findAny() - .isPresent(); - isPaymentAccountUsed = isPaymentAccountUsed || tradeManager.getTradableList().stream() - .filter(t -> t.getOffer().getMakerPaymentAccountId().equals(paymentAccount.getId()) || - paymentAccount.getId().equals(t.getTakerPaymentAccountId())) - .findAny() - .isPresent(); - if (!isPaymentAccountUsed) - user.removePaymentAccount(paymentAccount); - return isPaymentAccountUsed; + try { + paymentAccountManager.removePaymentAccount(paymentAccount.getId()); + return false; + } catch(PaymentAccountInUseException e) { + return true; + } } public void onSelectAccount(PaymentAccount paymentAccount) { @@ -146,9 +110,10 @@ public void onSelectAccount(PaymentAccount paymentAccount) { public void exportAccounts(Stage stage) { if (user.getPaymentAccounts() != null) { - ArrayList accounts = new ArrayList<>(user.getPaymentAccounts().stream() + ArrayList accounts = user.getPaymentAccounts() + .stream() .filter(paymentAccount -> paymentAccount instanceof AssetAccount) - .collect(Collectors.toList())); + .collect(Collectors.toCollection(ArrayList::new)); GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage, persistenceProtoResolver, corruptedDatabaseFilesHandler); } } diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java index ed281def8e0..e8873915de4 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java @@ -20,15 +20,10 @@ import bisq.desktop.common.model.ActivatableDataModel; import bisq.desktop.util.GUIUtil; -import bisq.core.account.witness.AccountAgeWitnessService; -import bisq.core.locale.CryptoCurrency; -import bisq.core.locale.CurrencyUtil; -import bisq.core.locale.FiatCurrency; -import bisq.core.locale.TradeCurrency; -import bisq.core.offer.OpenOfferManager; import bisq.core.payment.AssetAccount; import bisq.core.payment.PaymentAccount; -import bisq.core.trade.TradeManager; +import bisq.core.payment.PaymentAccountInUseException; +import bisq.core.payment.PaymentAccountManager; import bisq.core.user.Preferences; import bisq.core.user.User; @@ -50,11 +45,9 @@ class FiatAccountsDataModel extends ActivatableDataModel { + private final PaymentAccountManager paymentAccountManager; private final User user; private final Preferences preferences; - private final OpenOfferManager openOfferManager; - private final TradeManager tradeManager; - private final AccountAgeWitnessService accountAgeWitnessService; final ObservableList paymentAccounts = FXCollections.observableArrayList(); private final SetChangeListener setChangeListener; private final String accountsFileName = "FiatPaymentAccounts"; @@ -62,18 +55,13 @@ class FiatAccountsDataModel extends ActivatableDataModel { private final CorruptedDatabaseFilesHandler corruptedDatabaseFilesHandler; @Inject - public FiatAccountsDataModel(User user, + public FiatAccountsDataModel(PaymentAccountManager paymentAccountManager, User user, Preferences preferences, - OpenOfferManager openOfferManager, - TradeManager tradeManager, - AccountAgeWitnessService accountAgeWitnessService, PersistenceProtoResolver persistenceProtoResolver, CorruptedDatabaseFilesHandler corruptedDatabaseFilesHandler) { + this.paymentAccountManager = paymentAccountManager; this.user = user; this.preferences = preferences; - this.openOfferManager = openOfferManager; - this.tradeManager = tradeManager; - this.accountAgeWitnessService = accountAgeWitnessService; this.persistenceProtoResolver = persistenceProtoResolver; this.corruptedDatabaseFilesHandler = corruptedDatabaseFilesHandler; setChangeListener = change -> fillAndSortPaymentAccounts(); @@ -106,41 +94,16 @@ protected void deactivate() { /////////////////////////////////////////////////////////////////////////////////////////// public void onSaveNewAccount(PaymentAccount paymentAccount) { - TradeCurrency singleTradeCurrency = paymentAccount.getSingleTradeCurrency(); - List tradeCurrencies = paymentAccount.getTradeCurrencies(); - if (singleTradeCurrency != null) { - if (singleTradeCurrency instanceof FiatCurrency) - preferences.addFiatCurrency((FiatCurrency) singleTradeCurrency); - else - preferences.addCryptoCurrency((CryptoCurrency) singleTradeCurrency); - } else if (tradeCurrencies != null && !tradeCurrencies.isEmpty()) { - if (tradeCurrencies.contains(CurrencyUtil.getDefaultTradeCurrency())) - paymentAccount.setSelectedTradeCurrency(CurrencyUtil.getDefaultTradeCurrency()); - else - paymentAccount.setSelectedTradeCurrency(tradeCurrencies.get(0)); - - tradeCurrencies.forEach(tradeCurrency -> { - if (tradeCurrency instanceof FiatCurrency) - preferences.addFiatCurrency((FiatCurrency) tradeCurrency); - else - preferences.addCryptoCurrency((CryptoCurrency) tradeCurrency); - }); - } - - user.addPaymentAccount(paymentAccount); - - accountAgeWitnessService.publishMyAccountAgeWitness(paymentAccount.getPaymentAccountPayload()); + paymentAccountManager.addPaymentAccount(paymentAccount); } public boolean onDeleteAccount(PaymentAccount paymentAccount) { - boolean isPaymentAccountUsed = openOfferManager.getObservableList().stream() - .anyMatch(o -> o.getOffer().getMakerPaymentAccountId().equals(paymentAccount.getId())); - isPaymentAccountUsed = isPaymentAccountUsed || tradeManager.getTradableList().stream() - .anyMatch(t -> t.getOffer().getMakerPaymentAccountId().equals(paymentAccount.getId()) || - paymentAccount.getId().equals(t.getTakerPaymentAccountId())); - if (!isPaymentAccountUsed) - user.removePaymentAccount(paymentAccount); - return isPaymentAccountUsed; + try { + paymentAccountManager.removePaymentAccount(paymentAccount.getId()); + return false; + } catch (PaymentAccountInUseException e) { + return true; + } } public void onSelectAccount(PaymentAccount paymentAccount) { @@ -149,9 +112,10 @@ public void onSelectAccount(PaymentAccount paymentAccount) { public void exportAccounts(Stage stage) { if (user.getPaymentAccounts() != null) { - ArrayList accounts = new ArrayList<>(user.getPaymentAccounts().stream() + ArrayList accounts = user.getPaymentAccounts() + .stream() .filter(paymentAccount -> !(paymentAccount instanceof AssetAccount)) - .collect(Collectors.toList())); + .collect(Collectors.toCollection(ArrayList::new)); GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage, persistenceProtoResolver, corruptedDatabaseFilesHandler); } } From 05b57d6b92e82a5e0acd41dfa35d2f2688a0bbf7 Mon Sep 17 00:00:00 2001 From: Bernard Labno Date: Tue, 19 Nov 2019 16:52:18 +0100 Subject: [PATCH 2/3] Apply chimp1984 comments regarding not using exceptions for the flow control --- .../payment/PaymentAccountInUseException.java | 24 --------- .../core/payment/PaymentAccountManager.java | 20 ++++--- .../payment/PaymentAccountManagerTest.java | 52 ++++++++++++++----- .../account/content/PaymentAccountsView.java | 4 +- .../AltCoinAccountsDataModel.java | 8 +-- .../fiataccounts/FiatAccountsDataModel.java | 8 +-- 6 files changed, 55 insertions(+), 61 deletions(-) delete mode 100644 core/src/main/java/bisq/core/payment/PaymentAccountInUseException.java diff --git a/core/src/main/java/bisq/core/payment/PaymentAccountInUseException.java b/core/src/main/java/bisq/core/payment/PaymentAccountInUseException.java deleted file mode 100644 index c509e7c6f37..00000000000 --- a/core/src/main/java/bisq/core/payment/PaymentAccountInUseException.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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.payment; - -public class PaymentAccountInUseException extends RuntimeException { - public PaymentAccountInUseException(String msg) { - super(msg); - } -} diff --git a/core/src/main/java/bisq/core/payment/PaymentAccountManager.java b/core/src/main/java/bisq/core/payment/PaymentAccountManager.java index 57e6af036a7..ecb8c68218a 100644 --- a/core/src/main/java/bisq/core/payment/PaymentAccountManager.java +++ b/core/src/main/java/bisq/core/payment/PaymentAccountManager.java @@ -107,15 +107,12 @@ public PaymentAccount addPaymentAccount(PaymentAccount paymentAccount) { return paymentAccount; } - public void removePaymentAccount(String id) { - PaymentAccount paymentAccount = user.getPaymentAccount(id); - if (paymentAccount == null) { - throw new NotFoundException(format("Payment account %s not found", id)); - } + public boolean removePaymentAccount(PaymentAccount paymentAccount) { + String id = paymentAccount.getId(); boolean isPaymentAccountUsed = openOfferManager.getObservableList().stream() .anyMatch(openOffer -> id.equals(openOffer.getOffer().getMakerPaymentAccountId())); if (isPaymentAccountUsed) { - throw new PaymentAccountInUseException(format("Payment account %s is used for open offer", id)); + return false; } isPaymentAccountUsed = tradeManager.getTradableList().stream() .anyMatch(trade -> { @@ -123,8 +120,17 @@ public void removePaymentAccount(String id) { return null != offer && id.equals(offer.getMakerPaymentAccountId()) || id.equals(trade.getTakerPaymentAccountId()); }); if (isPaymentAccountUsed) { - throw new PaymentAccountInUseException(format("Payment account %s is used for open trade", id)); + return false; } user.removePaymentAccount(paymentAccount); + return true; + } + + public boolean removePaymentAccount(String id) { + PaymentAccount paymentAccount = user.getPaymentAccount(id); + if (paymentAccount == null) { + throw new NotFoundException(format("Payment account %s not found", id)); + } + return removePaymentAccount(paymentAccount); } } diff --git a/core/src/test/java/bisq/core/payment/PaymentAccountManagerTest.java b/core/src/test/java/bisq/core/payment/PaymentAccountManagerTest.java index a0ea884ffc5..a78098e1131 100644 --- a/core/src/test/java/bisq/core/payment/PaymentAccountManagerTest.java +++ b/core/src/test/java/bisq/core/payment/PaymentAccountManagerTest.java @@ -46,7 +46,6 @@ import org.junit.Test; import org.junit.rules.ExpectedException; -import static java.lang.String.format; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -282,58 +281,67 @@ public void removePaymentAccount_nullId_throwNotFoundException() { expectedException.expect(NotFoundException.class); expectedException.expectMessage("Payment account null not found"); // When - paymentAccountManager.removePaymentAccount(null); + paymentAccountManager.removePaymentAccount((String) null); } @Test public void removePaymentAccount_accountUsedInOpenOffer_throwsException() { // Given String id = getRandomString(); - when(user.getPaymentAccount(id)).thenReturn(mock(PaymentAccount.class)); + PaymentAccount paymentAccount = mock(PaymentAccount.class); + when(paymentAccount.getId()).thenReturn(id); + when(user.getPaymentAccount(id)).thenReturn(paymentAccount); Offer offer = mock(Offer.class); when(offer.getMakerPaymentAccountId()).thenReturn(id); OpenOffer openOffer = mock(OpenOffer.class); when(openOffer.getOffer()).thenReturn(offer); when(openOfferManager.getObservableList()).thenReturn(FXCollections.observableArrayList(openOffer)); - expectedException.expect(PaymentAccountInUseException.class); - expectedException.expectMessage(format("Payment account %s is used for open offer", id)); // When - paymentAccountManager.removePaymentAccount(id); + boolean result = paymentAccountManager.removePaymentAccount(id); + +// Then + assertFalse(result); } @Test public void removePaymentAccount_accountUsedInOpenTradeForMaker_throwsException() { // Given String id = getRandomString(); - when(user.getPaymentAccount(id)).thenReturn(mock(PaymentAccount.class)); + PaymentAccount paymentAccount = mock(PaymentAccount.class); + when(paymentAccount.getId()).thenReturn(id); + when(user.getPaymentAccount(id)).thenReturn(paymentAccount); Offer offer = mock(Offer.class); when(offer.getMakerPaymentAccountId()).thenReturn(id); Trade trade = mock(Trade.class); when(trade.getOffer()).thenReturn(offer); when(tradeManager.getTradableList()).thenReturn(FXCollections.observableArrayList(trade)); when(openOfferManager.getObservableList()).thenReturn(FXCollections.observableArrayList()); - expectedException.expect(PaymentAccountInUseException.class); - expectedException.expectMessage(format("Payment account %s is used for open trade", id)); // When - paymentAccountManager.removePaymentAccount(id); + boolean result = paymentAccountManager.removePaymentAccount(id); + +// Then + assertFalse(result); } @Test public void removePaymentAccount_accountUsedInOpenTradeForTaker_throwsException() { // Given String id = getRandomString(); - when(user.getPaymentAccount(id)).thenReturn(mock(PaymentAccount.class)); + PaymentAccount paymentAccount = mock(PaymentAccount.class); + when(paymentAccount.getId()).thenReturn(id); + when(user.getPaymentAccount(id)).thenReturn(paymentAccount); Trade trade = mock(Trade.class); when(trade.getTakerPaymentAccountId()).thenReturn(id); when(tradeManager.getTradableList()).thenReturn(FXCollections.observableArrayList(trade)); when(openOfferManager.getObservableList()).thenReturn(FXCollections.observableArrayList()); - expectedException.expect(PaymentAccountInUseException.class); - expectedException.expectMessage(format("Payment account %s is used for open trade", id)); // When - paymentAccountManager.removePaymentAccount(id); + boolean result = paymentAccountManager.removePaymentAccount(id); + +// Then + assertFalse(result); } @Test @@ -352,6 +360,22 @@ public void removePaymentAccount_accountExistAndIsNotUsed_removeAccount() { verify(user).removePaymentAccount(paymentAccount); } + @Test + public void removePaymentAccount_accountExistAndIsNotUsed_returnsTrue() { +// Given + String id = getRandomString(); + PaymentAccount paymentAccount = mock(PaymentAccount.class); + when(user.getPaymentAccount(id)).thenReturn(paymentAccount); + when(tradeManager.getTradableList()).thenReturn(FXCollections.observableArrayList()); + when(openOfferManager.getObservableList()).thenReturn(FXCollections.observableArrayList()); + +// When + boolean result = paymentAccountManager.removePaymentAccount(id); + +// Then + assertTrue(result); + } + private String getRandomString() { return UUID.randomUUID().toString(); } 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 92e674e457a..55f76736135 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 @@ -82,8 +82,8 @@ protected void onDeleteAccount(PaymentAccount paymentAccount) { new Popup<>().warning(Res.get("shared.askConfirmDeleteAccount")) .actionButtonText(Res.get("shared.yes")) .onAction(() -> { - boolean isPaymentAccountUsed = deleteAccountFromModel(paymentAccount); - if (!isPaymentAccountUsed) + boolean success = deleteAccountFromModel(paymentAccount); + if (success) removeSelectAccountForm(); else UserThread.runAfter(() -> new Popup<>().warning( diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java index 30b6fe8eca8..bb55c090996 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java @@ -22,7 +22,6 @@ import bisq.core.payment.AssetAccount; import bisq.core.payment.PaymentAccount; -import bisq.core.payment.PaymentAccountInUseException; import bisq.core.payment.PaymentAccountManager; import bisq.core.user.Preferences; import bisq.core.user.User; @@ -96,12 +95,7 @@ public void onSaveNewAccount(PaymentAccount paymentAccount) { } public boolean onDeleteAccount(PaymentAccount paymentAccount) { - try { - paymentAccountManager.removePaymentAccount(paymentAccount.getId()); - return false; - } catch(PaymentAccountInUseException e) { - return true; - } + return paymentAccountManager.removePaymentAccount(paymentAccount); } public void onSelectAccount(PaymentAccount paymentAccount) { diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java index e8873915de4..3947fa2c57b 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java @@ -22,7 +22,6 @@ import bisq.core.payment.AssetAccount; import bisq.core.payment.PaymentAccount; -import bisq.core.payment.PaymentAccountInUseException; import bisq.core.payment.PaymentAccountManager; import bisq.core.user.Preferences; import bisq.core.user.User; @@ -98,12 +97,7 @@ public void onSaveNewAccount(PaymentAccount paymentAccount) { } public boolean onDeleteAccount(PaymentAccount paymentAccount) { - try { - paymentAccountManager.removePaymentAccount(paymentAccount.getId()); - return false; - } catch (PaymentAccountInUseException e) { - return true; - } + return paymentAccountManager.removePaymentAccount(paymentAccount); } public void onSelectAccount(PaymentAccount paymentAccount) { From f6c1a3c285f6272bff1506ef03acf4bbde8b5c97 Mon Sep 17 00:00:00 2001 From: Bernard Labno Date: Tue, 19 Nov 2019 17:12:10 +0100 Subject: [PATCH 3/3] Bring back final modifier to AltCoinAddressValidator and Preferences --- .../bisq/core/payment/validation/AltCoinAddressValidator.java | 2 +- core/src/main/java/bisq/core/user/Preferences.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java b/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java index fe9a614fea3..63b15347fca 100644 --- a/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java +++ b/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java @@ -35,7 +35,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j -public class AltCoinAddressValidator extends InputValidator { +public final class AltCoinAddressValidator extends InputValidator { private final AssetRegistry assetRegistry; private String currencyCode; diff --git a/core/src/main/java/bisq/core/user/Preferences.java b/core/src/main/java/bisq/core/user/Preferences.java index a69bbbebcb8..d022eb9dae9 100644 --- a/core/src/main/java/bisq/core/user/Preferences.java +++ b/core/src/main/java/bisq/core/user/Preferences.java @@ -78,7 +78,7 @@ @Slf4j @Singleton -public class Preferences implements PersistedDataHost, BridgeAddressProvider { +public final class Preferences implements PersistedDataHost, BridgeAddressProvider { private static final ArrayList BTC_MAIN_NET_EXPLORERS = new ArrayList<>(Arrays.asList( new BlockChainExplorer("Blockstream.info", "https://blockstream.info/tx/", "https://blockstream.info/address/"),