mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-06-16 10:59:23 -04:00
Refactor persistence (arbitrator)
This commit is contained in:
parent
917dd3522b
commit
b807ee17a6
13 changed files with 210 additions and 233 deletions
|
@ -17,78 +17,126 @@
|
||||||
|
|
||||||
package io.bitsquare.arbitration;
|
package io.bitsquare.arbitration;
|
||||||
|
|
||||||
|
import io.bitsquare.locale.LanguageUtil;
|
||||||
|
import io.bitsquare.persistence.Storage;
|
||||||
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.bitcoinj.core.ECKey;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Arbitrator implements Serializable {
|
import javax.inject.Inject;
|
||||||
private static final long serialVersionUID = -2625059604136756635L;
|
|
||||||
|
|
||||||
|
public class Arbitrator implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Enums
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public enum ID_TYPE {
|
||||||
|
REAL_LIFE_ID,
|
||||||
|
NICKNAME,
|
||||||
|
COMPANY
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum METHOD {
|
||||||
|
TLS_NOTARY,
|
||||||
|
SKYPE_SCREEN_SHARING,
|
||||||
|
SMART_PHONE_VIDEO_CHAT,
|
||||||
|
REQUIRE_REAL_ID,
|
||||||
|
BANK_STATEMENT,
|
||||||
|
OTHER
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ID_VERIFICATION {
|
||||||
|
PASSPORT,
|
||||||
|
GOV_ID,
|
||||||
|
UTILITY_BILLS,
|
||||||
|
FACEBOOK,
|
||||||
|
GOOGLE_PLUS,
|
||||||
|
TWITTER,
|
||||||
|
PGP,
|
||||||
|
BTC_OTC,
|
||||||
|
OTHER
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
transient private Storage<Arbitrator> storage;
|
||||||
|
transient private boolean saveOnEveryUpdate;
|
||||||
|
|
||||||
|
// Persisted fields
|
||||||
private String id;
|
private String id;
|
||||||
private String pubKeyAsHex;
|
|
||||||
private byte[] pubKey;
|
private byte[] pubKey;
|
||||||
private String p2pSigPubKeyAsHex;
|
private PublicKey p2pSigPubKey;
|
||||||
private String name;
|
private String name;
|
||||||
|
private Reputation reputation;
|
||||||
|
|
||||||
|
// editable
|
||||||
private ID_TYPE idType;
|
private ID_TYPE idType;
|
||||||
private List<Locale> languages;
|
private List<Locale> languages;
|
||||||
private Reputation reputation;
|
|
||||||
private Coin fee;
|
private Coin fee;
|
||||||
private List<METHOD> arbitrationMethods;
|
private List<METHOD> arbitrationMethods;
|
||||||
private List<ID_VERIFICATION> idVerifications;
|
private List<ID_VERIFICATION> idVerifications;
|
||||||
|
|
||||||
private String webUrl;
|
private String webUrl;
|
||||||
|
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
public Arbitrator() {
|
|
||||||
|
@Inject
|
||||||
|
public Arbitrator(Storage<Arbitrator> storage, User user) {
|
||||||
|
this.storage = storage;
|
||||||
|
|
||||||
|
Arbitrator persisted = storage.getPersisted(this);
|
||||||
|
if (persisted != null) {
|
||||||
|
//TODO for mock arbitrator
|
||||||
|
id = persisted.getName();
|
||||||
|
|
||||||
|
this.pubKey = persisted.getPubKey();
|
||||||
|
this.p2pSigPubKey = persisted.getP2pSigPubKey();
|
||||||
|
this.name = persisted.getName();
|
||||||
|
this.idType = persisted.getIdType();
|
||||||
|
this.languages = persisted.getLanguages();
|
||||||
|
this.reputation = persisted.getReputation();
|
||||||
|
this.fee = persisted.getFee();
|
||||||
|
this.arbitrationMethods = persisted.getArbitrationMethods();
|
||||||
|
this.idVerifications = persisted.getIdVerifications();
|
||||||
|
this.webUrl = persisted.getWebUrl();
|
||||||
|
this.description = persisted.getDescription();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Mock
|
||||||
|
id = "Manfred Karrer";
|
||||||
|
this.pubKey = new ECKey().getPubKey();
|
||||||
|
this.p2pSigPubKey = user.getP2PSigPubKey();
|
||||||
|
this.name = "Manfred Karrer";
|
||||||
|
this.idType = Arbitrator.ID_TYPE.REAL_LIFE_ID;
|
||||||
|
this.languages = Arrays.asList(LanguageUtil.getDefaultLanguageLocale());
|
||||||
|
this.reputation = new Reputation();
|
||||||
|
this.fee = Coin.parseCoin("0.1");
|
||||||
|
this.arbitrationMethods = Arrays.asList(Arbitrator.METHOD.TLS_NOTARY);
|
||||||
|
this.idVerifications = Arrays.asList(ID_VERIFICATION.PASSPORT);
|
||||||
|
this.webUrl = "https://bitsquare.io";
|
||||||
|
this.description = "Bla bla...";
|
||||||
|
doSave();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Arbitrator(byte[] pubKey,
|
public void save() {
|
||||||
String p2pSigPubKeyAsHex,
|
if (saveOnEveryUpdate)
|
||||||
String name,
|
doSave();
|
||||||
ID_TYPE idType,
|
|
||||||
List<Locale> languages,
|
|
||||||
Reputation reputation,
|
|
||||||
Coin fee,
|
|
||||||
List<METHOD> arbitrationMethods,
|
|
||||||
List<ID_VERIFICATION> idVerifications,
|
|
||||||
String webUrl,
|
|
||||||
String description) {
|
|
||||||
this.pubKey = pubKey;
|
|
||||||
this.p2pSigPubKeyAsHex = p2pSigPubKeyAsHex;
|
|
||||||
this.name = name;
|
|
||||||
this.idType = idType;
|
|
||||||
this.languages = languages;
|
|
||||||
this.reputation = reputation;
|
|
||||||
this.fee = fee;
|
|
||||||
this.arbitrationMethods = arbitrationMethods;
|
|
||||||
this.idVerifications = idVerifications;
|
|
||||||
this.webUrl = webUrl;
|
|
||||||
this.description = description;
|
|
||||||
|
|
||||||
//TODO for mock arbitrator
|
|
||||||
id = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyPersistedArbitrator(Arbitrator persistedArbitrator) {
|
private void doSave() {
|
||||||
this.pubKeyAsHex = persistedArbitrator.getPubKeyAsHex();
|
storage.save();
|
||||||
this.p2pSigPubKeyAsHex = persistedArbitrator.getPubKeyAsHex();
|
|
||||||
this.name = persistedArbitrator.getName();
|
|
||||||
this.idType = persistedArbitrator.getIdType();
|
|
||||||
this.languages = persistedArbitrator.getLanguages();
|
|
||||||
this.reputation = persistedArbitrator.getReputation();
|
|
||||||
this.fee = persistedArbitrator.getFee();
|
|
||||||
this.arbitrationMethods = persistedArbitrator.getArbitrationMethods();
|
|
||||||
this.idVerifications = persistedArbitrator.getIdVerifications();
|
|
||||||
this.webUrl = persistedArbitrator.getWebUrl();
|
|
||||||
this.description = persistedArbitrator.getDescription();
|
|
||||||
|
|
||||||
//TODO for mock arbitrator
|
|
||||||
id = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -114,20 +162,48 @@ public class Arbitrator implements Serializable {
|
||||||
return id != null && id.equals(other.getId());
|
return id != null && id.equals(other.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Setters
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public void setSaveOnEveryUpdate(boolean saveOnEveryUpdate) {
|
||||||
|
this.saveOnEveryUpdate = saveOnEveryUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPubKeyAsHex() {
|
public void setDescription(String description) {
|
||||||
return pubKeyAsHex;
|
this.description = description;
|
||||||
|
doSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getPubKey() {
|
public void setIdType(ID_TYPE idType) {
|
||||||
return pubKey;
|
this.idType = idType;
|
||||||
|
doSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getP2pSigPubKeyAsHex() {
|
public void setLanguages(List<Locale> languages) {
|
||||||
return p2pSigPubKeyAsHex;
|
this.languages = languages;
|
||||||
|
doSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFee(Coin fee) {
|
||||||
|
this.fee = fee;
|
||||||
|
doSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArbitrationMethods(List<METHOD> arbitrationMethods) {
|
||||||
|
this.arbitrationMethods = arbitrationMethods;
|
||||||
|
doSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIdVerifications(List<ID_VERIFICATION> idVerifications) {
|
||||||
|
this.idVerifications = idVerifications;
|
||||||
|
doSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWebUrl(String webUrl) {
|
||||||
|
this.webUrl = webUrl;
|
||||||
|
doSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,6 +211,17 @@ public class Arbitrator implements Serializable {
|
||||||
// Getters
|
// Getters
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getPubKey() {
|
||||||
|
return pubKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PublicKey getP2pSigPubKey() {
|
||||||
|
return p2pSigPubKey;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
|
@ -171,36 +258,4 @@ public class Arbitrator implements Serializable {
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Enums
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public enum ID_TYPE {
|
|
||||||
REAL_LIFE_ID,
|
|
||||||
NICKNAME,
|
|
||||||
COMPANY
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum METHOD {
|
|
||||||
TLS_NOTARY,
|
|
||||||
SKYPE_SCREEN_SHARING,
|
|
||||||
SMART_PHONE_VIDEO_CHAT,
|
|
||||||
REQUIRE_REAL_ID,
|
|
||||||
BANK_STATEMENT,
|
|
||||||
OTHER
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ID_VERIFICATION {
|
|
||||||
PASSPORT,
|
|
||||||
GOV_ID,
|
|
||||||
UTILITY_BILLS,
|
|
||||||
FACEBOOK,
|
|
||||||
GOOGLE_PLUS,
|
|
||||||
TWITTER,
|
|
||||||
PGP,
|
|
||||||
BTC_OTC,
|
|
||||||
OTHER
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,7 @@
|
||||||
package io.bitsquare.gui.main;
|
package io.bitsquare.gui.main;
|
||||||
|
|
||||||
import io.bitsquare.app.UpdateProcess;
|
import io.bitsquare.app.UpdateProcess;
|
||||||
import io.bitsquare.arbitration.Arbitrator;
|
|
||||||
import io.bitsquare.arbitration.ArbitratorService;
|
import io.bitsquare.arbitration.ArbitratorService;
|
||||||
import io.bitsquare.arbitration.Reputation;
|
|
||||||
import io.bitsquare.btc.BitcoinNetwork;
|
import io.bitsquare.btc.BitcoinNetwork;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.common.viewfx.model.ViewModel;
|
import io.bitsquare.common.viewfx.model.ViewModel;
|
||||||
|
@ -28,27 +26,18 @@ import io.bitsquare.fiat.FiatAccount;
|
||||||
import io.bitsquare.fiat.FiatAccountType;
|
import io.bitsquare.fiat.FiatAccountType;
|
||||||
import io.bitsquare.gui.util.BSFormatter;
|
import io.bitsquare.gui.util.BSFormatter;
|
||||||
import io.bitsquare.locale.CountryUtil;
|
import io.bitsquare.locale.CountryUtil;
|
||||||
import io.bitsquare.locale.LanguageUtil;
|
|
||||||
import io.bitsquare.p2p.BaseP2PService;
|
import io.bitsquare.p2p.BaseP2PService;
|
||||||
import io.bitsquare.p2p.BootstrapState;
|
import io.bitsquare.p2p.BootstrapState;
|
||||||
import io.bitsquare.p2p.ClientNode;
|
import io.bitsquare.p2p.ClientNode;
|
||||||
import io.bitsquare.p2p.MessageService;
|
|
||||||
import io.bitsquare.persistence.Persistence;
|
import io.bitsquare.persistence.Persistence;
|
||||||
import io.bitsquare.trade.Trade;
|
import io.bitsquare.trade.Trade;
|
||||||
import io.bitsquare.trade.TradeManager;
|
import io.bitsquare.trade.TradeManager;
|
||||||
import io.bitsquare.user.AccountSettings;
|
import io.bitsquare.user.AccountSettings;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
import io.bitsquare.util.Utilities;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
import org.bitcoinj.core.ECKey;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Currency;
|
import java.util.Currency;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
@ -110,7 +99,7 @@ class MainViewModel implements ViewModel {
|
||||||
private AccountSettings accountSettings;
|
private AccountSettings accountSettings;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MainViewModel(User user, WalletService walletService, ClientNode clientNode, MessageService messageService,
|
public MainViewModel(User user, WalletService walletService, ClientNode clientNode,
|
||||||
ArbitratorService arbitratorService,
|
ArbitratorService arbitratorService,
|
||||||
TradeManager tradeManager, BitcoinNetwork bitcoinNetwork, UpdateProcess updateProcess,
|
TradeManager tradeManager, BitcoinNetwork bitcoinNetwork, UpdateProcess updateProcess,
|
||||||
BSFormatter formatter, Persistence persistence, AccountSettings accountSettings) {
|
BSFormatter formatter, Persistence persistence, AccountSettings accountSettings) {
|
||||||
|
@ -129,7 +118,6 @@ class MainViewModel implements ViewModel {
|
||||||
updateProcess.state.addListener((observableValue, oldValue, newValue) -> applyUpdateState(newValue));
|
updateProcess.state.addListener((observableValue, oldValue, newValue) -> applyUpdateState(newValue));
|
||||||
applyUpdateState(updateProcess.state.get());
|
applyUpdateState(updateProcess.state.get());
|
||||||
|
|
||||||
user.currentFiatAccountProperty().addListener((observable, oldValue, newValue) -> persistence.write(user));
|
|
||||||
currentBankAccount.bind(user.currentFiatAccountProperty());
|
currentBankAccount.bind(user.currentFiatAccountProperty());
|
||||||
user.fiatAccountsObservableList().addListener((ListChangeListener<FiatAccount>) change -> {
|
user.fiatAccountsObservableList().addListener((ListChangeListener<FiatAccount>) change -> {
|
||||||
bankAccountsComboBoxDisable.set(change.getList().isEmpty());
|
bankAccountsComboBoxDisable.set(change.getList().isEmpty());
|
||||||
|
@ -211,11 +199,6 @@ class MainViewModel implements ViewModel {
|
||||||
updateNumPendingTrades();
|
updateNumPendingTrades();
|
||||||
showAppScreen.set(true);
|
showAppScreen.set(true);
|
||||||
|
|
||||||
// For alpha version
|
|
||||||
// uses messageService, so don't call it before backend is ready
|
|
||||||
if (accountSettings.getAcceptedArbitrators().isEmpty())
|
|
||||||
accountSettings.addAcceptedArbitrator(getMockArbitrator());
|
|
||||||
|
|
||||||
// For alpha version
|
// For alpha version
|
||||||
if (!user.isRegistered()) {
|
if (!user.isRegistered()) {
|
||||||
FiatAccount fiatAccount = new FiatAccount(FiatAccountType.IRC,
|
FiatAccount fiatAccount = new FiatAccount(FiatAccountType.IRC,
|
||||||
|
@ -226,8 +209,6 @@ class MainViewModel implements ViewModel {
|
||||||
"Demo (E.g. IBAN) ",
|
"Demo (E.g. IBAN) ",
|
||||||
"Demo (E.g. BIC) ");
|
"Demo (E.g. BIC) ");
|
||||||
user.addFiatAccount(fiatAccount);
|
user.addFiatAccount(fiatAccount);
|
||||||
persistence.write(user);
|
|
||||||
|
|
||||||
user.setAccountID(walletService.getRegistrationAddressEntry().toString());
|
user.setAccountID(walletService.getRegistrationAddressEntry().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,30 +332,4 @@ class MainViewModel implements ViewModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Arbitrator getMockArbitrator() {
|
|
||||||
byte[] pubKey = new ECKey().getPubKey();
|
|
||||||
String p2pSigPubKeyAsHex = Utilities.getHexStringFromPublicKey(user.getP2PSigPubKey());
|
|
||||||
List<Locale> languages = new ArrayList<>();
|
|
||||||
languages.add(LanguageUtil.getDefaultLanguageLocale());
|
|
||||||
List<Arbitrator.METHOD> arbitrationMethods = new ArrayList<>();
|
|
||||||
arbitrationMethods.add(Arbitrator.METHOD.TLS_NOTARY);
|
|
||||||
List<Arbitrator.ID_VERIFICATION> idVerifications = new ArrayList<>();
|
|
||||||
idVerifications.add(Arbitrator.ID_VERIFICATION.PASSPORT);
|
|
||||||
idVerifications.add(Arbitrator.ID_VERIFICATION.GOV_ID);
|
|
||||||
|
|
||||||
Arbitrator arbitrator = new Arbitrator(pubKey,
|
|
||||||
p2pSigPubKeyAsHex,
|
|
||||||
"Manfred Karrer",
|
|
||||||
Arbitrator.ID_TYPE.REAL_LIFE_ID,
|
|
||||||
languages,
|
|
||||||
new Reputation(),
|
|
||||||
Coin.parseCoin("0.1"),
|
|
||||||
arbitrationMethods,
|
|
||||||
idVerifications,
|
|
||||||
"https://bitsquare.io",
|
|
||||||
"Bla bla...");
|
|
||||||
|
|
||||||
arbitratorService.addArbitrator(arbitrator);
|
|
||||||
return arbitrator;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,6 @@ public class ArbitratorBrowserView extends ActivatableView<Pane, Void> implement
|
||||||
@FXML
|
@FXML
|
||||||
public void onSelect() {
|
public void onSelect() {
|
||||||
accountSettings.addAcceptedArbitrator(currentArbitrator);
|
accountSettings.addAcceptedArbitrator(currentArbitrator);
|
||||||
persistence.write(accountSettings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|
|
@ -19,7 +19,6 @@ package io.bitsquare.gui.main.account.arbitrator.registration;
|
||||||
|
|
||||||
import io.bitsquare.arbitration.Arbitrator;
|
import io.bitsquare.arbitration.Arbitrator;
|
||||||
import io.bitsquare.arbitration.ArbitratorService;
|
import io.bitsquare.arbitration.ArbitratorService;
|
||||||
import io.bitsquare.arbitration.Reputation;
|
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.common.viewfx.view.ActivatableView;
|
import io.bitsquare.common.viewfx.view.ActivatableView;
|
||||||
import io.bitsquare.common.viewfx.view.FxmlView;
|
import io.bitsquare.common.viewfx.view.FxmlView;
|
||||||
|
@ -27,7 +26,6 @@ import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator;
|
||||||
import io.bitsquare.gui.util.BSFormatter;
|
import io.bitsquare.gui.util.BSFormatter;
|
||||||
import io.bitsquare.locale.BSResources;
|
import io.bitsquare.locale.BSResources;
|
||||||
import io.bitsquare.locale.LanguageUtil;
|
import io.bitsquare.locale.LanguageUtil;
|
||||||
import io.bitsquare.persistence.Persistence;
|
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
import io.bitsquare.util.Utilities;
|
import io.bitsquare.util.Utilities;
|
||||||
|
|
||||||
|
@ -79,18 +77,17 @@ public class ArbitratorRegistrationView extends ActivatableView<AnchorPane, Void
|
||||||
private List<Locale> languageList = new ArrayList<>();
|
private List<Locale> languageList = new ArrayList<>();
|
||||||
private List<Arbitrator.METHOD> methodList = new ArrayList<>();
|
private List<Arbitrator.METHOD> methodList = new ArrayList<>();
|
||||||
private List<Arbitrator.ID_VERIFICATION> idVerificationList = new ArrayList<>();
|
private List<Arbitrator.ID_VERIFICATION> idVerificationList = new ArrayList<>();
|
||||||
private Arbitrator arbitrator = new Arbitrator();
|
|
||||||
|
|
||||||
private final Persistence persistence;
|
private final Arbitrator arbitrator;
|
||||||
private final WalletService walletService;
|
private final WalletService walletService;
|
||||||
private final ArbitratorService messageService;
|
private final ArbitratorService messageService;
|
||||||
private final User user;
|
private final User user;
|
||||||
private final BSFormatter formatter;
|
private final BSFormatter formatter;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private ArbitratorRegistrationView(Persistence persistence, WalletService walletService,
|
private ArbitratorRegistrationView(Arbitrator arbitrator, WalletService walletService,
|
||||||
ArbitratorService messageService, User user, BSFormatter formatter) {
|
ArbitratorService messageService, User user, BSFormatter formatter) {
|
||||||
this.persistence = persistence;
|
this.arbitrator = arbitrator;
|
||||||
this.walletService = walletService;
|
this.walletService = walletService;
|
||||||
this.messageService = messageService;
|
this.messageService = messageService;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
@ -101,15 +98,7 @@ public class ArbitratorRegistrationView extends ActivatableView<AnchorPane, Void
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
accordion.setExpandedPane(profileTitledPane);
|
accordion.setExpandedPane(profileTitledPane);
|
||||||
|
|
||||||
Arbitrator persistedArbitrator = (Arbitrator) persistence.read(arbitrator);
|
|
||||||
if (persistedArbitrator != null) {
|
|
||||||
arbitrator.applyPersistedArbitrator(persistedArbitrator);
|
|
||||||
applyArbitrator();
|
applyArbitrator();
|
||||||
}
|
|
||||||
else {
|
|
||||||
languageList.add(LanguageUtil.getDefaultLanguageLocale());
|
|
||||||
languagesTextField.setText(formatter.languageLocalesToString(languageList));
|
|
||||||
}
|
|
||||||
|
|
||||||
languageComboBox.setItems(FXCollections.observableArrayList(LanguageUtil.getAllLanguageLocales()));
|
languageComboBox.setItems(FXCollections.observableArrayList(LanguageUtil.getAllLanguageLocales()));
|
||||||
languageComboBox.setConverter(new StringConverter<Locale>() {
|
languageComboBox.setConverter(new StringConverter<Locale>() {
|
||||||
|
@ -255,9 +244,16 @@ public class ArbitratorRegistrationView extends ActivatableView<AnchorPane, Void
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void onSaveProfile() {
|
public void onSaveProfile() {
|
||||||
arbitrator = getEditedArbitrator();
|
arbitrator.setSaveOnEveryUpdate(false);
|
||||||
if (arbitrator != null) {
|
arbitrator.setWebUrl(webPageTextField.getText());
|
||||||
persistence.write(arbitrator);
|
arbitrator.setFee(formatter.parseToCoin(arbitrationFeeTextField.getText()));
|
||||||
|
arbitrator.setIdType(idType);
|
||||||
|
arbitrator.setIdVerifications(idVerificationList);
|
||||||
|
arbitrator.setLanguages(languageList);
|
||||||
|
arbitrator.setArbitrationMethods(methodList);
|
||||||
|
|
||||||
|
arbitrator.setSaveOnEveryUpdate(true);
|
||||||
|
arbitrator.save();
|
||||||
|
|
||||||
if (isEditMode) {
|
if (isEditMode) {
|
||||||
close();
|
close();
|
||||||
|
@ -266,7 +262,6 @@ public class ArbitratorRegistrationView extends ActivatableView<AnchorPane, Void
|
||||||
setupPaySecurityDepositScreen();
|
setupPaySecurityDepositScreen();
|
||||||
accordion.setExpandedPane(paySecurityDepositTitledPane);
|
accordion.setExpandedPane(paySecurityDepositTitledPane);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
messageService.addArbitrator(arbitrator);
|
messageService.addArbitrator(arbitrator);
|
||||||
}
|
}
|
||||||
|
@ -366,27 +361,6 @@ public class ArbitratorRegistrationView extends ActivatableView<AnchorPane, Void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Arbitrator getEditedArbitrator() {
|
|
||||||
byte[] pubKey = walletService.getArbitratorDepositAddressEntry().getPubKey();
|
|
||||||
String p2pSigPubKeyAsHex = Utilities.getHexStringFromPublicKey(user.getP2PSigPubKey());
|
|
||||||
String name = nameTextField.getText();
|
|
||||||
Coin fee = formatter.parseToCoin(arbitrationFeeTextField.getText());
|
|
||||||
String webUrl = webPageTextField.getText();
|
|
||||||
String description = descriptionTextArea.getText();
|
|
||||||
|
|
||||||
return new Arbitrator(pubKey,
|
|
||||||
p2pSigPubKeyAsHex,
|
|
||||||
name,
|
|
||||||
idType,
|
|
||||||
languageList,
|
|
||||||
new Reputation(),
|
|
||||||
fee,
|
|
||||||
methodList,
|
|
||||||
idVerificationList,
|
|
||||||
webUrl,
|
|
||||||
description);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void close() {
|
private void close() {
|
||||||
Stage stage = (Stage) root.getScene().getWindow();
|
Stage stage = (Stage) root.getScene().getWindow();
|
||||||
stage.close();
|
stage.close();
|
||||||
|
|
|
@ -95,7 +95,6 @@ class FiatAccountDataModel implements Activatable, DataModel {
|
||||||
primaryID.get(),
|
primaryID.get(),
|
||||||
secondaryID.get());
|
secondaryID.get());
|
||||||
user.addFiatAccount(fiatAccount);
|
user.addFiatAccount(fiatAccount);
|
||||||
saveUser();
|
|
||||||
allFiatAccounts.setAll(user.fiatAccountsObservableList());
|
allFiatAccounts.setAll(user.fiatAccountsObservableList());
|
||||||
countryNotInAcceptedCountriesList.set(!accountSettings.getAcceptedCountries().contains(country.get()));
|
countryNotInAcceptedCountriesList.set(!accountSettings.getAcceptedCountries().contains(country.get()));
|
||||||
reset();
|
reset();
|
||||||
|
@ -103,7 +102,6 @@ class FiatAccountDataModel implements Activatable, DataModel {
|
||||||
|
|
||||||
void removeBankAccount() {
|
void removeBankAccount() {
|
||||||
user.removeFiatAccount(user.currentFiatAccountProperty().get());
|
user.removeFiatAccount(user.currentFiatAccountProperty().get());
|
||||||
saveUser();
|
|
||||||
allFiatAccounts.setAll(user.fiatAccountsObservableList());
|
allFiatAccounts.setAll(user.fiatAccountsObservableList());
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
@ -112,13 +110,11 @@ class FiatAccountDataModel implements Activatable, DataModel {
|
||||||
// already added it before
|
// already added it before
|
||||||
void addCountryToAcceptedCountriesList() {
|
void addCountryToAcceptedCountriesList() {
|
||||||
accountSettings.addAcceptedCountry(country.get());
|
accountSettings.addAcceptedCountry(country.get());
|
||||||
saveSettings();
|
|
||||||
countryNotInAcceptedCountriesList.set(false);
|
countryNotInAcceptedCountriesList.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectBankAccount(FiatAccount fiatAccount) {
|
void selectBankAccount(FiatAccount fiatAccount) {
|
||||||
user.setCurrentFiatAccount(fiatAccount);
|
user.setCurrentFiatAccount(fiatAccount);
|
||||||
persistence.write(user);
|
|
||||||
|
|
||||||
if (fiatAccount != null) {
|
if (fiatAccount != null) {
|
||||||
title.set(fiatAccount.getNameOfBank());
|
title.set(fiatAccount.getNameOfBank());
|
||||||
|
@ -177,12 +173,4 @@ class FiatAccountDataModel implements Activatable, DataModel {
|
||||||
country.set(null);
|
country.set(null);
|
||||||
currency.set(null);
|
currency.set(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveUser() {
|
|
||||||
persistence.write(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveSettings() {
|
|
||||||
persistence.write(accountSettings);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,6 @@ class IrcAccountDataModel implements Activatable, DataModel {
|
||||||
"irc",
|
"irc",
|
||||||
"irc");
|
"irc");
|
||||||
user.addFiatAccount(fiatAccount);
|
user.addFiatAccount(fiatAccount);
|
||||||
saveUser();
|
|
||||||
allFiatAccounts.setAll(user.fiatAccountsObservableList());
|
allFiatAccounts.setAll(user.fiatAccountsObservableList());
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
@ -103,8 +102,4 @@ class IrcAccountDataModel implements Activatable, DataModel {
|
||||||
type.set(null);
|
type.set(null);
|
||||||
currency.set(null);
|
currency.set(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveUser() {
|
|
||||||
persistence.write(user);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,6 @@ class RestrictionsDataModel implements Activatable, DataModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deactivate() {
|
public void deactivate() {
|
||||||
// no-op
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ObservableList<Country> getAllCountriesFor(Region selectedRegion) {
|
ObservableList<Country> getAllCountriesFor(Region selectedRegion) {
|
||||||
|
@ -90,14 +89,12 @@ class RestrictionsDataModel implements Activatable, DataModel {
|
||||||
void removeLanguage(Locale locale) {
|
void removeLanguage(Locale locale) {
|
||||||
languageList.remove(locale);
|
languageList.remove(locale);
|
||||||
accountSettings.removeAcceptedLanguageLocale(locale);
|
accountSettings.removeAcceptedLanguageLocale(locale);
|
||||||
saveSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void addCountry(Country country) {
|
void addCountry(Country country) {
|
||||||
if (!countryList.contains(country) && country != null) {
|
if (!countryList.contains(country) && country != null) {
|
||||||
countryList.add(country);
|
countryList.add(country);
|
||||||
accountSettings.addAcceptedCountry(country);
|
accountSettings.addAcceptedCountry(country);
|
||||||
saveSettings();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,24 +103,16 @@ class RestrictionsDataModel implements Activatable, DataModel {
|
||||||
// In addAcceptedCountry there is a check to no add duplicates, so it works correctly for now
|
// In addAcceptedCountry there is a check to no add duplicates, so it works correctly for now
|
||||||
CountryUtil.getAllEuroCountries().stream().forEach(accountSettings::addAcceptedCountry);
|
CountryUtil.getAllEuroCountries().stream().forEach(accountSettings::addAcceptedCountry);
|
||||||
countryList.setAll(accountSettings.getAcceptedCountries());
|
countryList.setAll(accountSettings.getAcceptedCountries());
|
||||||
saveSettings();
|
|
||||||
return countryList;
|
return countryList;
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeCountry(Country country) {
|
void removeCountry(Country country) {
|
||||||
countryList.remove(country);
|
countryList.remove(country);
|
||||||
accountSettings.removeAcceptedCountry(country);
|
accountSettings.removeAcceptedCountry(country);
|
||||||
saveSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeArbitrator(Arbitrator arbitrator) {
|
void removeArbitrator(Arbitrator arbitrator) {
|
||||||
arbitratorList.remove(arbitrator);
|
arbitratorList.remove(arbitrator);
|
||||||
accountSettings.removeAcceptedArbitrator(arbitrator);
|
accountSettings.removeAcceptedArbitrator(arbitrator);
|
||||||
saveSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void saveSettings() {
|
|
||||||
persistence.write(accountSettings);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,11 @@ import io.bitsquare.util.FileUtil;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InvalidClassException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
@ -70,7 +73,23 @@ public class Storage<T extends Serializable> {
|
||||||
throw new RuntimeException("storageFile = null. Call init before using read/write.");
|
throw new RuntimeException("storageFile = null. Call init before using read/write.");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return (T) FileUtil.read(storageFile);
|
T persistedObject = (T) FileUtil.read(storageFile);
|
||||||
|
|
||||||
|
// If we did not get any exception we can be sure the data are consistent so we make a backup
|
||||||
|
FileUtil.backupFile(storageFile, new File(Paths.get(dir.getAbsolutePath(), "backups").toString()),
|
||||||
|
serializable.getClass().getSimpleName() + ".ser");
|
||||||
|
return persistedObject;
|
||||||
|
} catch (InvalidClassException e) {
|
||||||
|
log.error("Version of persisted class has changed. We cannot read the persisted data anymore. We make a backup and remove the inconsistent file.");
|
||||||
|
try {
|
||||||
|
// In case the persisted data have been critical (keys) we keep a backup which might be used for recovery
|
||||||
|
FileUtil.removeAndBackupFile(storageFile, new File(Paths.get(dir.getAbsolutePath(), "inconsistent").toString()),
|
||||||
|
serializable.getClass().getSimpleName() + ".ser");
|
||||||
|
} catch (IOException e1) {
|
||||||
|
e1.printStackTrace();
|
||||||
|
log.error(e1.getMessage());
|
||||||
|
// We swallow Exception if backup fails
|
||||||
|
}
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
log.info("File not available. That is OK for the first run.");
|
log.info("File not available. That is OK for the first run.");
|
||||||
} catch (IOException | ClassNotFoundException e) {
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
|
|
|
@ -59,8 +59,8 @@ public class Contract implements Serializable {
|
||||||
this.takerAccountID = takerAccountID;
|
this.takerAccountID = takerAccountID;
|
||||||
this.offererFiatAccount = offererFiatAccount;
|
this.offererFiatAccount = offererFiatAccount;
|
||||||
this.takerFiatAccount = takerFiatAccount;
|
this.takerFiatAccount = takerFiatAccount;
|
||||||
this.offererP2PSigPubKeyAsString = Utilities.getHexStringFromPublicKey(offererP2PSigPubKey);
|
this.offererP2PSigPubKeyAsString = Utilities.getHexFromPubKey(offererP2PSigPubKey);
|
||||||
this.takerP2PSigPubKeyAsString = Utilities.getHexStringFromPublicKey(takerP2PSigPubKey);
|
this.takerP2PSigPubKeyAsString = Utilities.getHexFromPubKey(takerP2PSigPubKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -45,13 +45,12 @@ public class AccountSettings implements Serializable {
|
||||||
private List<Country> acceptedCountryLocales = new ArrayList<>();
|
private List<Country> acceptedCountryLocales = new ArrayList<>();
|
||||||
private List<Arbitrator> acceptedArbitrators = new ArrayList<>();
|
private List<Arbitrator> acceptedArbitrators = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public AccountSettings(Storage<AccountSettings> storage) {
|
public AccountSettings(Storage<AccountSettings> storage, Arbitrator defaultArbitrator) {
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
|
|
||||||
AccountSettings persisted = storage.getPersisted(this);
|
AccountSettings persisted = storage.getPersisted(this);
|
||||||
|
@ -63,7 +62,7 @@ public class AccountSettings implements Serializable {
|
||||||
else {
|
else {
|
||||||
acceptedLanguageLocales = Arrays.asList(LanguageUtil.getDefaultLanguageLocale(), LanguageUtil.getEnglishLanguageLocale());
|
acceptedLanguageLocales = Arrays.asList(LanguageUtil.getDefaultLanguageLocale(), LanguageUtil.getEnglishLanguageLocale());
|
||||||
acceptedCountryLocales = Arrays.asList(CountryUtil.getDefaultCountry());
|
acceptedCountryLocales = Arrays.asList(CountryUtil.getDefaultCountry());
|
||||||
acceptedArbitrators = new ArrayList<>();
|
acceptedArbitrators = Arrays.asList(defaultArbitrator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ package io.bitsquare.util;
|
||||||
import org.bitcoinj.core.Utils;
|
import org.bitcoinj.core.Utils;
|
||||||
import org.bitcoinj.utils.Threading;
|
import org.bitcoinj.utils.Threading;
|
||||||
|
|
||||||
|
import com.google.common.io.Files;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
@ -117,4 +119,18 @@ public class FileUtil {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void removeAndBackupFile(File storageFile, File dir, String name) throws IOException {
|
||||||
|
if (!dir.exists())
|
||||||
|
dir.mkdir();
|
||||||
|
|
||||||
|
writeTempFileToFile(storageFile, new File(dir, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void backupFile(File storageFile, File dir, String name) throws IOException {
|
||||||
|
if (!dir.exists())
|
||||||
|
dir.mkdir();
|
||||||
|
|
||||||
|
Files.copy(storageFile, new File(dir, name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,7 +295,7 @@ public class Utilities {
|
||||||
return animationTimer;
|
return animationTimer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getHexStringFromPublicKey(PublicKey publicKey) {
|
public static String getHexFromPubKey(PublicKey publicKey) {
|
||||||
final X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
|
final X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
|
||||||
return Utils.HEX.encode(x509EncodedKeySpec.getEncoded());
|
return Utils.HEX.encode(x509EncodedKeySpec.getEncoded());
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,10 @@
|
||||||
|
|
||||||
package io.bitsquare.trade.protocol.placeoffer;
|
package io.bitsquare.trade.protocol.placeoffer;
|
||||||
|
|
||||||
import io.bitsquare.arbitration.Arbitrator;
|
|
||||||
import io.bitsquare.btc.BitcoinNetwork;
|
import io.bitsquare.btc.BitcoinNetwork;
|
||||||
import io.bitsquare.btc.FeePolicy;
|
import io.bitsquare.btc.FeePolicy;
|
||||||
import io.bitsquare.btc.UserAgent;
|
import io.bitsquare.btc.UserAgent;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.crypto.EncryptionService;
|
|
||||||
import io.bitsquare.fiat.FiatAccountType;
|
|
||||||
import io.bitsquare.locale.CountryUtil;
|
|
||||||
import io.bitsquare.locale.LanguageUtil;
|
|
||||||
import io.bitsquare.offer.Direction;
|
|
||||||
import io.bitsquare.offer.Offer;
|
|
||||||
import io.bitsquare.offer.OfferBookService;
|
import io.bitsquare.offer.OfferBookService;
|
||||||
import io.bitsquare.offer.tomp2p.TomP2POfferBookService;
|
import io.bitsquare.offer.tomp2p.TomP2POfferBookService;
|
||||||
import io.bitsquare.p2p.BootstrapState;
|
import io.bitsquare.p2p.BootstrapState;
|
||||||
|
@ -40,16 +33,11 @@ import io.bitsquare.persistence.Persistence;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
import org.bitcoinj.core.Address;
|
import org.bitcoinj.core.Address;
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
import org.bitcoinj.utils.Threading;
|
import org.bitcoinj.utils.Threading;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Currency;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
@ -301,7 +289,7 @@ public class PlaceOfferProtocolTest {
|
||||||
faultHandler);
|
faultHandler);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
private Offer getOffer() throws NoSuchAlgorithmException {
|
/* private Offer getOffer() throws NoSuchAlgorithmException {
|
||||||
return new Offer(OFFER_ID,
|
return new Offer(OFFER_ID,
|
||||||
new EncryptionService().getGeneratedDSAKeyPair().getPublic(),
|
new EncryptionService().getGeneratedDSAKeyPair().getPublic(),
|
||||||
Direction.BUY,
|
Direction.BUY,
|
||||||
|
@ -317,5 +305,5 @@ public class PlaceOfferProtocolTest {
|
||||||
Arrays.asList(CountryUtil.getDefaultCountry()),
|
Arrays.asList(CountryUtil.getDefaultCountry()),
|
||||||
Arrays.asList(LanguageUtil.getDefaultLanguageLocale())
|
Arrays.asList(LanguageUtil.getDefaultLanguageLocale())
|
||||||
);
|
);
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue