mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-10-01 01:35:48 -04:00
register arbitrator from api and use throughout trade
This commit is contained in:
parent
c2f5adac9b
commit
716f62797d
@ -17,7 +17,11 @@
|
||||
|
||||
package bisq.core.api;
|
||||
|
||||
import bisq.core.btc.model.AddressEntry;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.support.SupportType;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
||||
import bisq.core.support.dispute.refund.refundagent.RefundAgent;
|
||||
@ -33,7 +37,7 @@ import org.bitcoinj.core.ECKey;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -57,6 +61,8 @@ class CoreDisputeAgentsService {
|
||||
private final User user;
|
||||
private final Config config;
|
||||
private final KeyRing keyRing;
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final ArbitratorManager arbitratorManager;
|
||||
private final MediatorManager mediatorManager;
|
||||
private final RefundAgentManager refundAgentManager;
|
||||
private final P2PService p2PService;
|
||||
@ -67,12 +73,16 @@ class CoreDisputeAgentsService {
|
||||
public CoreDisputeAgentsService(User user,
|
||||
Config config,
|
||||
KeyRing keyRing,
|
||||
BtcWalletService btcWalletService,
|
||||
ArbitratorManager arbitratorManager,
|
||||
MediatorManager mediatorManager,
|
||||
RefundAgentManager refundAgentManager,
|
||||
P2PService p2PService) {
|
||||
this.user = user;
|
||||
this.config = config;
|
||||
this.keyRing = keyRing;
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.arbitratorManager = arbitratorManager;
|
||||
this.mediatorManager = mediatorManager;
|
||||
this.refundAgentManager = refundAgentManager;
|
||||
this.p2PService = p2PService;
|
||||
@ -97,7 +107,14 @@ class CoreDisputeAgentsService {
|
||||
String signature;
|
||||
switch (supportType.get()) {
|
||||
case ARBITRATION:
|
||||
throw new IllegalArgumentException("arbitrators must be registered in a Bisq UI");
|
||||
if (user.getRegisteredArbitrator() != null) {
|
||||
log.warn("ignoring request to re-register as arbitrator");
|
||||
return;
|
||||
}
|
||||
ecKey = arbitratorManager.getRegistrationKey(registrationKey);
|
||||
signature = arbitratorManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
|
||||
registerArbitrator(nodeAddress, languageCodes, ecKey, signature);
|
||||
return;
|
||||
case MEDIATION:
|
||||
if (user.getRegisteredMediator() != null) {
|
||||
log.warn("ignoring request to re-register as mediator");
|
||||
@ -124,6 +141,30 @@ class CoreDisputeAgentsService {
|
||||
}
|
||||
}
|
||||
|
||||
private void registerArbitrator(NodeAddress nodeAddress,
|
||||
List<String> languageCodes,
|
||||
ECKey ecKey,
|
||||
String signature) {
|
||||
AddressEntry arbitratorAddressEntry = btcWalletService.getArbitratorAddressEntry(); // TODO (woodser): switch to XMR; no reason for arbitrator to have BTC address / pub key
|
||||
Arbitrator arbitrator = new Arbitrator(
|
||||
p2PService.getAddress(),
|
||||
arbitratorAddressEntry.getPubKey(),
|
||||
arbitratorAddressEntry.getAddressString(),
|
||||
keyRing.getPubKeyRing(),
|
||||
new ArrayList<>(languageCodes),
|
||||
new Date().getTime(),
|
||||
ecKey.getPubKey(),
|
||||
signature,
|
||||
"",
|
||||
null,
|
||||
null);
|
||||
arbitratorManager.addDisputeAgent(arbitrator, () -> {
|
||||
}, errorMessage -> {
|
||||
});
|
||||
arbitratorManager.getDisputeAgentByNodeAddress(nodeAddress).orElseThrow(() ->
|
||||
new IllegalStateException("could not register arbitrator"));
|
||||
}
|
||||
|
||||
private void registerMediator(NodeAddress nodeAddress,
|
||||
List<String> languageCodes,
|
||||
ECKey ecKey,
|
||||
|
@ -637,6 +637,14 @@ public class XmrWalletService {
|
||||
}
|
||||
}
|
||||
|
||||
public XmrAddressEntry getArbitratorAddressEntry() {
|
||||
XmrAddressEntry.Context context = XmrAddressEntry.Context.ARBITRATOR;
|
||||
Optional<XmrAddressEntry> addressEntry = getAddressEntryListAsImmutableList().stream()
|
||||
.filter(e -> context == e.getContext())
|
||||
.findAny();
|
||||
return getOrCreateAddressEntry(context, addressEntry);
|
||||
}
|
||||
|
||||
public Optional<XmrAddressEntry> getAddressEntry(String offerId, XmrAddressEntry.Context context) {
|
||||
return getAddressEntryListAsImmutableList().stream().filter(e -> offerId.equals(e.getOfferId())).filter(e -> context == e.getContext()).findAny();
|
||||
}
|
||||
@ -672,6 +680,19 @@ public class XmrWalletService {
|
||||
swapTradeEntryToAvailableEntry(offerId, XmrAddressEntry.Context.TRADE_PAYOUT);
|
||||
}
|
||||
|
||||
private XmrAddressEntry getOrCreateAddressEntry(XmrAddressEntry.Context context,
|
||||
Optional<XmrAddressEntry> addressEntry) {
|
||||
if (addressEntry.isPresent()) {
|
||||
return addressEntry.get();
|
||||
} else {
|
||||
MoneroSubaddress subaddress = wallet.createSubaddress(0);
|
||||
XmrAddressEntry entry = new XmrAddressEntry(subaddress.getIndex(), subaddress.getAddress(), context, null, null);
|
||||
log.info("getOrCreateAddressEntry: add new XmrAddressEntry {}", entry);
|
||||
xmrAddressEntryList.addAddressEntry(entry);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<XmrAddressEntry> findAddressEntry(String address, XmrAddressEntry.Context context) {
|
||||
return getAddressEntryListAsImmutableList().stream().filter(e -> address.equals(e.getAddressString())).filter(e -> context == e.getContext()).findAny();
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.PaymentAccountUtil;
|
||||
import bisq.core.provider.price.MarketPrice;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.user.User;
|
||||
@ -69,7 +69,7 @@ public class CreateOfferService {
|
||||
private final User user;
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final TradeStatisticsManager tradeStatisticsManager;
|
||||
private final MediatorManager mediatorManager;
|
||||
private final ArbitratorManager arbitratorManager;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -85,7 +85,7 @@ public class CreateOfferService {
|
||||
User user,
|
||||
BtcWalletService btcWalletService,
|
||||
TradeStatisticsManager tradeStatisticsManager,
|
||||
MediatorManager mediatorManager) {
|
||||
ArbitratorManager arbitratorManager) {
|
||||
this.offerUtil = offerUtil;
|
||||
this.txFeeEstimationService = txFeeEstimationService;
|
||||
this.priceFeedService = priceFeedService;
|
||||
@ -94,7 +94,7 @@ public class CreateOfferService {
|
||||
this.user = user;
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||
this.mediatorManager = mediatorManager;
|
||||
this.arbitratorManager = arbitratorManager;
|
||||
}
|
||||
|
||||
|
||||
@ -192,7 +192,7 @@ public class CreateOfferService {
|
||||
makerFeeAsCoin);
|
||||
|
||||
// select signing arbitrator
|
||||
Mediator arbitrator = DisputeAgentSelection.getLeastUsedArbitrator(tradeStatisticsManager, mediatorManager); // TODO (woodser): using mediator manager for arbitrators
|
||||
Arbitrator arbitrator = DisputeAgentSelection.getLeastUsedArbitrator(tradeStatisticsManager, arbitratorManager);
|
||||
|
||||
OfferPayload offerPayload = new OfferPayload(offerId,
|
||||
creationTime,
|
||||
@ -214,7 +214,7 @@ public class CreateOfferService {
|
||||
bankId,
|
||||
acceptedBanks,
|
||||
Version.VERSION,
|
||||
btcWalletService.getLastBlockSeenHeight(),
|
||||
btcWalletService.getLastBlockSeenHeight(), // TODO (woodser): switch to XMR
|
||||
txFeeToUse.value,
|
||||
makerFeeAsCoin.value,
|
||||
buyerSecurityDepositAsCoin.value,
|
||||
|
@ -22,7 +22,7 @@ import bisq.core.filter.FilterManager;
|
||||
import bisq.core.locale.CurrencyUtil;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.PaymentAccountUtil;
|
||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import bisq.core.trade.TradeUtils;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.user.User;
|
||||
@ -216,7 +216,7 @@ public class OfferFilter {
|
||||
public boolean hasValidSignature(Offer offer) {
|
||||
|
||||
// get arbitrator
|
||||
Mediator arbitrator = user.getAcceptedMediatorByAddress(offer.getOfferPayload().getArbitratorSigner());
|
||||
Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner());
|
||||
if (arbitrator == null) return false; // invalid arbitrator
|
||||
|
||||
// validate arbitrator signature
|
||||
|
@ -33,11 +33,11 @@ import bisq.core.offer.messages.SignOfferResponse;
|
||||
import bisq.core.offer.placeoffer.PlaceOfferModel;
|
||||
import bisq.core.offer.placeoffer.PlaceOfferProtocol;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
||||
import bisq.core.trade.TradableList;
|
||||
import bisq.core.trade.TradeUtils;
|
||||
import bisq.core.trade.closed.ClosedTradableManager;
|
||||
import bisq.core.trade.handlers.TransactionResultHandler;
|
||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
@ -634,7 +634,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||
try {
|
||||
|
||||
// verify this node is an arbitrator
|
||||
Mediator thisArbitrator = user.getRegisteredMediator();
|
||||
Arbitrator thisArbitrator = user.getRegisteredArbitrator();
|
||||
NodeAddress thisAddress = p2PService.getNetworkNode().getNodeAddress();
|
||||
if (thisArbitrator == null || !thisArbitrator.getNodeAddress().equals(thisAddress)) {
|
||||
errorMessage = "Cannot sign offer because we are not a registered arbitrator";
|
||||
|
@ -19,7 +19,7 @@ package bisq.core.offer.placeoffer.tasks;
|
||||
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.offer.placeoffer.PlaceOfferModel;
|
||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import bisq.core.trade.TradeUtils;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@ -39,7 +39,7 @@ public class MakerProcessesSignOfferResponse extends Task<PlaceOfferModel> {
|
||||
runInterceptHook();
|
||||
|
||||
// validate arbitrator signature
|
||||
Mediator arbitrator = checkNotNull(model.getUser().getAcceptedMediatorByAddress(offer.getOfferPayload().getArbitratorSigner()), "user.getAcceptedMediatorByAddress(arbitratorSigner) must not be null");
|
||||
Arbitrator arbitrator = checkNotNull(model.getUser().getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner()), "user.getAcceptedArbitratorByAddress(arbitratorSigner) must not be null");
|
||||
if (!TradeUtils.isArbitratorSignatureValid(model.getSignOfferResponse().getSignedOfferPayload(), arbitrator)) {
|
||||
throw new RuntimeException("Offer payload has invalid arbitrator signature");
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import bisq.core.btc.model.XmrAddressEntry;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.offer.messages.SignOfferRequest;
|
||||
import bisq.core.offer.placeoffer.PlaceOfferModel;
|
||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import bisq.network.p2p.AckMessage;
|
||||
import bisq.network.p2p.DecryptedDirectMessageListener;
|
||||
import bisq.network.p2p.DecryptedMessageWithPubKey;
|
||||
@ -74,7 +74,7 @@ public class MakerSendsSignOfferRequest extends Task<PlaceOfferModel> {
|
||||
returnAddress);
|
||||
|
||||
// get signing arbitrator
|
||||
Mediator arbitrator = checkNotNull(model.getUser().getAcceptedMediatorByAddress(offer.getOfferPayload().getArbitratorSigner()), "user.getAcceptedMediatorByAddress(mediatorNodeAddress) must not be null");
|
||||
Arbitrator arbitrator = checkNotNull(model.getUser().getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner()), "user.getAcceptedArbitratorByAddress(arbitratorNodeAddress) must not be null");
|
||||
|
||||
// complete on successful ack message
|
||||
DecryptedDirectMessageListener ackListener = new DecryptedDirectMessageListener() {
|
||||
|
@ -75,8 +75,6 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
|
||||
|
||||
checkArgument(offer.getDate().getTime() > 0,
|
||||
"Date must not be 0. date=" + offer.getDate().toString());
|
||||
|
||||
System.out.println("OFFER PRICE: " + offer.getPrice());
|
||||
|
||||
checkNotNull(offer.getCurrencyCode(), "Currency is null");
|
||||
checkNotNull(offer.getDirection(), "Direction is null");
|
||||
|
@ -31,8 +31,8 @@ import bisq.core.offer.SignedOffer;
|
||||
import bisq.core.offer.availability.OfferAvailabilityModel;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
||||
import bisq.core.trade.Trade.Phase;
|
||||
import bisq.core.trade.closed.ClosedTradableManager;
|
||||
@ -374,7 +374,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||
if (isArbitrator) {
|
||||
|
||||
// verify this node is registered arbitrator
|
||||
Mediator thisArbitrator = user.getRegisteredMediator();
|
||||
Arbitrator thisArbitrator = user.getRegisteredArbitrator();
|
||||
NodeAddress thisAddress = p2PService.getNetworkNode().getNodeAddress();
|
||||
if (thisArbitrator == null || !thisArbitrator.getNodeAddress().equals(thisAddress)) {
|
||||
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because we are not an arbitrator", sender, request.getTradeId());
|
||||
@ -508,7 +508,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||
//System.out.println("TradeManager trade.setTradingPeerNodeAddress(): " + sender);
|
||||
//trade.setTradingPeerNodeAddress(sender);
|
||||
// TODO (woodser): what if maker's address changes while offer open, or taker's address changes after multisig deposit available? need to verify and update. see OpenOfferManager.maybeUpdatePersistedOffers()
|
||||
trade.setArbitratorPubKeyRing(user.getAcceptedMediatorByAddress(sender).getPubKeyRing());
|
||||
trade.setArbitratorPubKeyRing(user.getAcceptedArbitratorByAddress(sender).getPubKeyRing());
|
||||
trade.setMakerPubKeyRing(trade.getOffer().getPubKeyRing());
|
||||
initTradeAndProtocol(trade, getTradeProtocol(trade));
|
||||
trade.getSelf().setReserveTxHash(openOffer.getReserveTxHash()); // TODO (woodser): initialize in initTradeAndProtocol?
|
||||
|
@ -24,7 +24,7 @@ import bisq.common.util.Tuple2;
|
||||
import bisq.common.util.Utilities;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.offer.OfferPayload;
|
||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import bisq.core.trade.messages.InitTradeRequest;
|
||||
import java.util.Objects;
|
||||
|
||||
@ -47,7 +47,7 @@ public class TradeUtils {
|
||||
* @param signedOfferPayload is a signed offer payload
|
||||
* @return true if the arbitrator's signature is valid for the offer
|
||||
*/
|
||||
public static boolean isArbitratorSignatureValid(OfferPayload signedOfferPayload, Mediator arbitrator) {
|
||||
public static boolean isArbitratorSignatureValid(OfferPayload signedOfferPayload, Arbitrator arbitrator) {
|
||||
|
||||
// remove arbitrator signature from signed payload
|
||||
String signature = signedOfferPayload.getArbitratorSignature();
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
package bisq.core.trade.protocol.tasks.taker;
|
||||
|
||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.messages.InitTradeRequest;
|
||||
import bisq.core.trade.protocol.tasks.TradeTask;
|
||||
@ -80,7 +80,7 @@ public class TakerSendsInitTradeRequestToArbitrator extends TradeTask {
|
||||
private void sendInitTradeRequest(NodeAddress arbitratorNodeAddress, SendDirectMessageListener listener) {
|
||||
|
||||
// get registered arbitrator
|
||||
Mediator arbitrator = processModel.getUser().getAcceptedMediatorByAddress(arbitratorNodeAddress);
|
||||
Arbitrator arbitrator = processModel.getUser().getAcceptedArbitratorByAddress(arbitratorNodeAddress);
|
||||
if (arbitrator == null) throw new RuntimeException("Node address " + arbitratorNodeAddress + " is not a registered arbitrator");
|
||||
|
||||
// set pub keys
|
||||
|
@ -1374,6 +1374,7 @@ setting.info.msg=When selling BTC for XMR you can use the auto-confirm feature t
|
||||
# Account
|
||||
####################################################################
|
||||
|
||||
account.tab.arbitratorRegistration=Arbitrator registration
|
||||
account.tab.mediatorRegistration=Mediator registration
|
||||
account.tab.refundAgentRegistration=Refund agent registration
|
||||
account.tab.signing=Signing
|
||||
|
@ -125,7 +125,13 @@ public class AccountView extends ActivatableView<TabPane, Void> {
|
||||
};
|
||||
|
||||
keyEventEventHandler = event -> {
|
||||
if (Utilities.isAltOrCtrlPressed(KeyCode.D, event) && mediatorRegistrationTab == null) {
|
||||
if (Utilities.isAltOrCtrlPressed(KeyCode.R, event) && arbitratorRegistrationTab == null) {
|
||||
closeOtherExtraTabs(arbitratorRegistrationTab);
|
||||
arbitratorRegistrationTab = new Tab(Res.get("account.tab.arbitratorRegistration").toUpperCase());
|
||||
arbitratorRegistrationTab.setClosable(true);
|
||||
root.getTabs().add(arbitratorRegistrationTab);
|
||||
navigation.navigateTo(MainView.class, AccountView.class, ArbitratorRegistrationView.class);
|
||||
} else if (Utilities.isAltOrCtrlPressed(KeyCode.D, event) && mediatorRegistrationTab == null) {
|
||||
closeOtherExtraTabs(mediatorRegistrationTab);
|
||||
mediatorRegistrationTab = new Tab(Res.get("account.tab.mediatorRegistration").toUpperCase());
|
||||
mediatorRegistrationTab.setClosable(true);
|
||||
|
@ -782,15 +782,9 @@ public class GUIUtil {
|
||||
|
||||
public static boolean canCreateOrTakeOfferOrShowPopup(User user, Navigation navigation) {
|
||||
|
||||
// TODO (woodser): use refund agents to dispute arbitration?
|
||||
if (!user.hasAcceptedRefundAgents()) {
|
||||
log.warn("There are no refund agents available"); // TODO (woodser): refund agents changing from [4444] to [] causing this error
|
||||
//new Popup().warning(Res.get("popup.warning.noArbitratorsAvailable")).show();
|
||||
//return false;
|
||||
}
|
||||
|
||||
if (!user.hasAcceptedMediators()) {
|
||||
new Popup().warning(Res.get("popup.warning.noMediatorsAvailable")).show();
|
||||
if (!user.hasAcceptedArbitrators()) {
|
||||
log.warn("There are no arbitrators available");
|
||||
new Popup().warning(Res.get("popup.warning.noArbitratorsAvailable")).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -35,9 +35,8 @@ If you don't use *screen*, open 4 terminal windows and run in each one of them:
|
||||
|
||||
1. `make seednode`
|
||||
2. `make arbitrator-desktop`
|
||||
3. If this is the first time launching the arbitrator desktop application, register the arbitrator and mediator after the interface opens:
|
||||
1. Go to the *Account* tab and press `cmd+n`. Confirm the registration of the arbitrator.
|
||||
2. From the *Account* tab press `cmd+d` and confirm the registration of the mediator.
|
||||
3. If this is the first time launching the arbitrator desktop application, register the arbitrator after the interface opens:
|
||||
1. Go to the *Account* tab and press `cmd+r`. Confirm the registration of the arbitrator.
|
||||
4. `make alice-desktop` or if you want to run Alice as a daemon: `make alice-daemon`
|
||||
5. `make bob-desktop` or if you want to run Bob as a daemon: `make bob-daemon`
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user