diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 239cf676f0e..bd50e248552 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -147,7 +147,7 @@ public P2PService(NetworkNode networkNode, this.networkNode.addConnectionListener(this); this.networkNode.addMessageListener(this); - this.requestDataManager.addListener(this); + this.requestDataManager.setListener(this); // We need to have both the initial data delivered and the hidden service published networkReadyBinding = EasyBind.combine(hiddenServicePublished, preliminaryDataReceived, @@ -317,6 +317,11 @@ public void onPreliminaryDataReceived() { public void onUpdatedDataReceived() { if (!isBootstrapped) { isBootstrapped = true; + // We don't use a listener at mailboxMessageService as we require the correct + // order of execution. The p2pServiceListeners must be called after + // mailboxMessageService.onUpdatedDataReceived. + mailboxMessageService.onUpdatedDataReceived(); + p2pServiceListeners.forEach(P2PServiceListener::onUpdatedDataReceived); p2PDataStorage.onBootstrapComplete(); } diff --git a/p2p/src/main/java/bisq/network/p2p/mailbox/MailboxMessageService.java b/p2p/src/main/java/bisq/network/p2p/mailbox/MailboxMessageService.java index 53143f69533..f1855ca0ed5 100644 --- a/p2p/src/main/java/bisq/network/p2p/mailbox/MailboxMessageService.java +++ b/p2p/src/main/java/bisq/network/p2p/mailbox/MailboxMessageService.java @@ -112,7 +112,7 @@ */ @Singleton @Slf4j -public class MailboxMessageService implements SetupListener, RequestDataManager.Listener, HashMapChangedListener, +public class MailboxMessageService implements SetupListener, HashMapChangedListener, PersistedDataHost { private static final long REPUBLISH_DELAY_SEC = TimeUnit.MINUTES.toSeconds(2); @@ -155,7 +155,6 @@ public MailboxMessageService(NetworkNode networkNode, this.clock = clock; this.republishMailboxEntries = republishMailboxEntries; - this.requestDataManager.addListener(this); this.networkNode.addSetupListener(this); this.persistenceManager.initialize(mailboxMessageList, PersistenceManager.Source.PRIVATE_LOW_PRIO); @@ -224,6 +223,19 @@ public void readPersisted(Runnable completeHandler) { // API /////////////////////////////////////////////////////////////////////////////////////////// + // We don't listen on requestDataManager directly as we require the correct + // order of execution. The p2pService is handling the correct order of execution and we get called + // directly from there. + public void onUpdatedDataReceived() { + if (!isBootstrapped) { + isBootstrapped = true; + // Only now we start listening and processing. The p2PDataStorage is our cache for data we have received + // after the hidden service was ready. + addHashMapChangedListenerAndApply(); + maybeRepublishMailBoxMessages(); + } + } + public void sendEncryptedMailboxMessage(NodeAddress peer, PubKeyRing peersPubKeyRing, MailboxMessage mailboxMessage, @@ -238,8 +250,9 @@ public void sendEncryptedMailboxMessage(NodeAddress peer, "My node address must not be null at sendEncryptedMailboxMessage"); checkArgument(!keyRing.getPubKeyRing().equals(peersPubKeyRing), "We got own keyring instead of that from peer"); - if (!isBootstrapped) + if (!isBootstrapped) { throw new NetworkNotReadyException(); + } if (networkNode.getAllConnections().isEmpty()) { sendMailboxMessageListener.onFault("There are no P2P network nodes connected. " + @@ -349,30 +362,6 @@ public void onHiddenServicePublished() { } - /////////////////////////////////////////////////////////////////////////////////////////// - // RequestDataManager.Listener implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onPreliminaryDataReceived() { - } - - @Override - public void onUpdatedDataReceived() { - if (!isBootstrapped) { - isBootstrapped = true; - // Only now we start listening and processing. The p2PDataStorage is our cache for data we have received - // after the hidden service was ready. - addHashMapChangedListenerAndApply(); - maybeRepublishMailBoxMessages(); - } - } - - @Override - public void onDataReceived() { - } - - /////////////////////////////////////////////////////////////////////////////////////////// // HashMapChangedListener implementation for ProtectedStorageEntry items /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/p2p/src/main/java/bisq/network/p2p/peers/getdata/RequestDataManager.java b/p2p/src/main/java/bisq/network/p2p/peers/getdata/RequestDataManager.java index de9dc754f59..7bf2d8fd361 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/getdata/RequestDataManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/getdata/RequestDataManager.java @@ -40,11 +40,9 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -53,6 +51,7 @@ import org.jetbrains.annotations.Nullable; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; @Slf4j public class RequestDataManager implements MessageListener, ConnectionListener, PeerManager.Listener { @@ -91,7 +90,10 @@ default void onNoSeedNodeAvailable() { private final P2PDataStorage dataStorage; private final PeerManager peerManager; private final List seedNodeAddresses; - private final Set listeners = new HashSet<>(); + + // As we use Guice injection we cannot set the listener in our constructor but the P2PService calls the setListener + // in it's constructor so we can guarantee it is not null. + private Listener listener; private final Map handlerMap = new HashMap<>(); private final Map getDataRequestHandlers = new HashMap<>(); @@ -147,8 +149,10 @@ public void shutDown() { // API /////////////////////////////////////////////////////////////////////////////////////////// - public void addListener(Listener listener) { - listeners.add(listener); + // We only support one listener as P2PService will manage calls on other clients in the correct order of execution. + // The listener is set from the P2PService constructor so we can guarantee it is not null. + public void setListener(Listener listener) { + this.listener = listener; } public boolean requestPreliminaryData() { @@ -334,16 +338,16 @@ public void onComplete() { // We delay because it can be that we get the HS published before we receive the // preliminary data and the onPreliminaryDataReceived call triggers the // dataUpdateRequested set to true, so we would also call the onUpdatedDataReceived. - UserThread.runAfter(() -> listeners.forEach(Listener::onPreliminaryDataReceived), 100, TimeUnit.MILLISECONDS); + UserThread.runAfter(checkNotNull(listener)::onPreliminaryDataReceived, 100, TimeUnit.MILLISECONDS); } // 2. Later we get a response from requestUpdatesData if (dataUpdateRequested) { dataUpdateRequested = false; - listeners.forEach(Listener::onUpdatedDataReceived); + checkNotNull(listener).onUpdatedDataReceived(); } - listeners.forEach(Listener::onDataReceived); + checkNotNull(listener).onDataReceived(); } @Override @@ -371,9 +375,9 @@ public void onFault(String errorMessage, @Nullable Connection connection) { // Notify listeners if (!nodeAddressOfPreliminaryDataRequest.isPresent()) { if (peerManager.isSeedNode(nodeAddress)) { - listeners.forEach(Listener::onNoSeedNodeAvailable); + checkNotNull(listener).onNoSeedNodeAvailable(); } else { - listeners.forEach(Listener::onNoPeersAvailable); + checkNotNull(listener).onNoPeersAvailable(); } }