diff --git a/core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java b/core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java index 61d37409..a77a5d62 100644 --- a/core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java +++ b/core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java @@ -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 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 languageCodes, ECKey ecKey, diff --git a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java index 52f6b004..e6820968 100644 --- a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java @@ -637,6 +637,14 @@ public class XmrWalletService { } } + public XmrAddressEntry getArbitratorAddressEntry() { + XmrAddressEntry.Context context = XmrAddressEntry.Context.ARBITRATOR; + Optional addressEntry = getAddressEntryListAsImmutableList().stream() + .filter(e -> context == e.getContext()) + .findAny(); + return getOrCreateAddressEntry(context, addressEntry); + } + public Optional 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 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 findAddressEntry(String address, XmrAddressEntry.Context context) { return getAddressEntryListAsImmutableList().stream().filter(e -> address.equals(e.getAddressString())).filter(e -> context == e.getContext()).findAny(); } diff --git a/core/src/main/java/bisq/core/offer/CreateOfferService.java b/core/src/main/java/bisq/core/offer/CreateOfferService.java index 8814379b..ccfb8331 100644 --- a/core/src/main/java/bisq/core/offer/CreateOfferService.java +++ b/core/src/main/java/bisq/core/offer/CreateOfferService.java @@ -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, diff --git a/core/src/main/java/bisq/core/offer/OfferFilter.java b/core/src/main/java/bisq/core/offer/OfferFilter.java index 57d5c44d..466202ab 100644 --- a/core/src/main/java/bisq/core/offer/OfferFilter.java +++ b/core/src/main/java/bisq/core/offer/OfferFilter.java @@ -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 diff --git a/core/src/main/java/bisq/core/offer/OpenOfferManager.java b/core/src/main/java/bisq/core/offer/OpenOfferManager.java index 395e4e03..6d591929 100644 --- a/core/src/main/java/bisq/core/offer/OpenOfferManager.java +++ b/core/src/main/java/bisq/core/offer/OpenOfferManager.java @@ -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"; diff --git a/core/src/main/java/bisq/core/offer/placeoffer/tasks/MakerProcessesSignOfferResponse.java b/core/src/main/java/bisq/core/offer/placeoffer/tasks/MakerProcessesSignOfferResponse.java index 1dd9e016..fad5d1c7 100644 --- a/core/src/main/java/bisq/core/offer/placeoffer/tasks/MakerProcessesSignOfferResponse.java +++ b/core/src/main/java/bisq/core/offer/placeoffer/tasks/MakerProcessesSignOfferResponse.java @@ -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 { 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"); } diff --git a/core/src/main/java/bisq/core/offer/placeoffer/tasks/MakerSendsSignOfferRequest.java b/core/src/main/java/bisq/core/offer/placeoffer/tasks/MakerSendsSignOfferRequest.java index ddf0a900..2d2807bc 100644 --- a/core/src/main/java/bisq/core/offer/placeoffer/tasks/MakerSendsSignOfferRequest.java +++ b/core/src/main/java/bisq/core/offer/placeoffer/tasks/MakerSendsSignOfferRequest.java @@ -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 { 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() { diff --git a/core/src/main/java/bisq/core/offer/placeoffer/tasks/ValidateOffer.java b/core/src/main/java/bisq/core/offer/placeoffer/tasks/ValidateOffer.java index 678c9ddf..8e2f904d 100644 --- a/core/src/main/java/bisq/core/offer/placeoffer/tasks/ValidateOffer.java +++ b/core/src/main/java/bisq/core/offer/placeoffer/tasks/ValidateOffer.java @@ -75,8 +75,6 @@ public class ValidateOffer extends Task { 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"); diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java index d4bba8a3..cb96a65f 100644 --- a/core/src/main/java/bisq/core/trade/TradeManager.java +++ b/core/src/main/java/bisq/core/trade/TradeManager.java @@ -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? diff --git a/core/src/main/java/bisq/core/trade/TradeUtils.java b/core/src/main/java/bisq/core/trade/TradeUtils.java index 528d0e9a..c8d5911e 100644 --- a/core/src/main/java/bisq/core/trade/TradeUtils.java +++ b/core/src/main/java/bisq/core/trade/TradeUtils.java @@ -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(); diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerSendsInitTradeRequestToArbitrator.java b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerSendsInitTradeRequestToArbitrator.java index 56559423..346e0cf6 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerSendsInitTradeRequestToArbitrator.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerSendsInitTradeRequestToArbitrator.java @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index da0cb8bc..35ea463a 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -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 diff --git a/desktop/src/main/java/bisq/desktop/main/account/AccountView.java b/desktop/src/main/java/bisq/desktop/main/account/AccountView.java index 543bc82c..64c394f1 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/AccountView.java +++ b/desktop/src/main/java/bisq/desktop/main/account/AccountView.java @@ -125,7 +125,13 @@ public class AccountView extends ActivatableView { }; 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); diff --git a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java index 3ee66a4a..d381ffab 100644 --- a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java @@ -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; } diff --git a/docs/installing.md b/docs/installing.md index 1062b696..17ef0224 100644 --- a/docs/installing.md +++ b/docs/installing.md @@ -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`