diff --git a/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java b/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java index c573b4dea9..a5bc7f4da2 100644 --- a/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java +++ b/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java @@ -18,21 +18,30 @@ package bisq.core.app.misc; import bisq.core.app.BisqExecutable; +import bisq.core.app.TorSetup; import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.dao.DaoSetup; +import bisq.core.dao.monitoring.DaoStateMonitoringService; import bisq.core.dao.node.full.RpcService; import bisq.core.offer.OpenOfferManager; import bisq.core.offer.bsq_swap.OpenBsqSwapOfferService; import bisq.core.payment.TradeLimits; import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; +import bisq.core.user.Cookie; +import bisq.core.user.CookieKey; +import bisq.core.user.User; import bisq.network.p2p.P2PService; +import bisq.network.p2p.P2PServiceListener; +import bisq.network.p2p.peers.PeerManager; +import bisq.common.Timer; import bisq.common.UserThread; import bisq.common.app.AppModule; import bisq.common.app.DevEnv; +import bisq.common.config.BaseCurrencyNetwork; import bisq.common.config.Config; import bisq.common.file.JsonFileManager; import bisq.common.handlers.ResultHandler; @@ -41,6 +50,9 @@ import bisq.common.setup.GracefulShutDownHandler; import bisq.common.util.Profiler; import bisq.common.util.SingleThreadExecutorUtils; +import com.google.inject.Key; +import com.google.inject.name.Names; + import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -51,10 +63,18 @@ public abstract class ExecutableForAppWithP2p extends BisqExecutable { private static final long CHECK_MEMORY_PERIOD_SEC = 300; protected static final long CHECK_SHUTDOWN_SEC = TimeUnit.HOURS.toSeconds(1); protected static final long SHUTDOWN_INTERVAL = TimeUnit.HOURS.toMillis(24); + private static final long CHECK_CONNECTION_LOSS_SEC = 30; + private volatile boolean stopped; private final long startTime = System.currentTimeMillis(); private TradeLimits tradeLimits; private AppSetupWithP2PAndDAO appSetupWithP2PAndDAO; + protected P2PService p2PService; + protected DaoStateMonitoringService daoStateMonitoringService; + + protected Cookie cookie; + private Timer checkConnectionLossTimer; + private Boolean preventPeriodicShutdownAtSeedNode; public ExecutableForAppWithP2p(String fullName, String scriptName, String appName, String version) { super(fullName, scriptName, appName, version); @@ -76,15 +96,69 @@ public abstract class ExecutableForAppWithP2p extends BisqExecutable { super.applyInjector(); appSetupWithP2PAndDAO = injector.getInstance(AppSetupWithP2PAndDAO.class); + p2PService = injector.getInstance(P2PService.class); + cookie = injector.getInstance(User.class).getCookie(); // Pin that as it is used in PaymentMethods and verification in TradeStatistics tradeLimits = injector.getInstance(TradeLimits.class); + daoStateMonitoringService = injector.getInstance(DaoStateMonitoringService.class); + preventPeriodicShutdownAtSeedNode = injector.getInstance(Key.get(boolean.class, + Names.named(Config.PREVENT_PERIODIC_SHUTDOWN_AT_SEED_NODE))); } @Override protected void startApplication() { + cookie.getAsOptionalBoolean(CookieKey.CLEAN_TOR_DIR_AT_RESTART).ifPresent(cleanTorDirAtRestart -> { + if (cleanTorDirAtRestart) { + injector.getInstance(TorSetup.class).cleanupTorFiles(() -> + cookie.remove(CookieKey.CLEAN_TOR_DIR_AT_RESTART), + log::error); + } + }); + + daoStateMonitoringService.addListener(new DaoStateMonitoringService.Listener() { + @Override + public void onCheckpointFailed() { + gracefulShutDown(); + } + }); + + p2PService.addP2PServiceListener(new P2PServiceListener() { + @Override + public void onDataReceived() { + } + + @Override + public void onNoSeedNodeAvailable() { + } + + @Override + public void onNoPeersAvailable() { + } + + @Override + public void onUpdatedDataReceived() { + } + + @Override + public void onTorNodeReady() { + } + + @Override + public void onHiddenServicePublished() { + ExecutableForAppWithP2p.this.onHiddenServicePublished(); + } + }); + appSetupWithP2PAndDAO.start(); } + protected void onHiddenServicePublished() { + if (!preventPeriodicShutdownAtSeedNode) { + startShutDownInterval(); + } + UserThread.runAfter(this::setupConnectionLossCheck, 60); + } + @Override protected void launchApplication() { onApplicationLaunched(); @@ -95,10 +169,26 @@ public abstract class ExecutableForAppWithP2p extends BisqExecutable { log.info("onSetupComplete"); } + + /////////////////////////////////////////////////////////////////////////////////////////// + // UncaughtExceptionHandler implementation + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void handleUncaughtException(Throwable throwable, boolean doShutDown) { + if (throwable instanceof OutOfMemoryError || doShutDown) { + log.error("We got an OutOfMemoryError and shut down"); + gracefulShutDown(() -> log.info("gracefulShutDown complete")); + } + } + // We don't use the gracefulShutDown implementation of the super class as we have a limited set of modules @Override public void gracefulShutDown(ResultHandler resultHandler) { log.info("gracefulShutDown"); + if (checkConnectionLossTimer != null) { + checkConnectionLossTimer.stop(); + } try { if (injector != null) { JsonFileManager.shutDownAllInstances(); @@ -216,4 +306,25 @@ public abstract class ExecutableForAppWithP2p extends BisqExecutable { System.exit(1); }); } + + protected void setupConnectionLossCheck() { + // For dev testing (usually on BTC_REGTEST) we don't want to get the seed shut + // down as it is normal that the seed is the only actively running node. + if (Config.baseCurrencyNetwork() == BaseCurrencyNetwork.BTC_REGTEST) { + return; + } + + if (checkConnectionLossTimer != null) { + return; + } + + checkConnectionLossTimer = UserThread.runPeriodically(() -> { + if (injector.getInstance(PeerManager.class).getNumAllConnectionsLostEvents() > 1) { + // We set a flag to clear tor cache files at re-start. We cannot clear it now as Tor is used and + // that can cause problems. + injector.getInstance(User.class).getCookie().putAsBoolean(CookieKey.CLEAN_TOR_DIR_AT_RESTART, true); + shutDown(this); + } + }, CHECK_CONNECTION_LOSS_SEC); + } } diff --git a/restapi/src/main/java/bisq/restapi/RestApi.java b/restapi/src/main/java/bisq/restapi/RestApi.java index ea78e34401..f27ebb1748 100644 --- a/restapi/src/main/java/bisq/restapi/RestApi.java +++ b/restapi/src/main/java/bisq/restapi/RestApi.java @@ -19,41 +19,22 @@ package bisq.restapi; import bisq.core.account.witness.AccountAgeWitnessService; -import bisq.core.app.TorSetup; import bisq.core.app.misc.ExecutableForAppWithP2p; import bisq.core.dao.SignVerifyService; import bisq.core.dao.governance.bond.reputation.BondedReputationRepository; import bisq.core.dao.governance.bond.role.BondedRolesRepository; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.DaoStateSnapshotService; -import bisq.core.user.Cookie; -import bisq.core.user.CookieKey; import bisq.core.user.Preferences; -import bisq.core.user.User; -import bisq.network.p2p.P2PService; -import bisq.network.p2p.P2PServiceListener; -import bisq.network.p2p.peers.PeerManager; - -import bisq.common.Timer; -import bisq.common.UserThread; import bisq.common.app.Version; -import bisq.common.config.BaseCurrencyNetwork; import bisq.common.config.Config; -import bisq.common.handlers.ResultHandler; - -import com.google.inject.Key; -import com.google.inject.name.Names; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -//todo not sure if the restart handling from seed nodes is required @Slf4j public class RestApi extends ExecutableForAppWithP2p { - private static final long CHECK_CONNECTION_LOSS_SEC = 30; - - private Timer checkConnectionLossTime; @Getter private DaoStateService daoStateService; @Getter @@ -64,6 +45,8 @@ public class RestApi extends ExecutableForAppWithP2p { private BondedRolesRepository bondedRolesRepository; @Getter private SignVerifyService signVerifyService; + private DaoStateSnapshotService daoStateSnapshotService; + private Preferences preferences; public RestApi() { super("Bisq Rest Api", "bisq_restapi", "bisq_restapi", Version.VERSION); @@ -80,113 +63,31 @@ public class RestApi extends ExecutableForAppWithP2p { checkMemory(config, this); } - - /////////////////////////////////////////////////////////////////////////////////////////// - // We continue with a series of synchronous execution tasks - /////////////////////////////////////////////////////////////////////////////////////////// - @Override protected void applyInjector() { super.applyInjector(); - injector.getInstance(DaoStateSnapshotService.class).setResyncDaoStateFromResourcesHandler(this::gracefulShutDown); + preferences = injector.getInstance(Preferences.class); + daoStateService = injector.getInstance(DaoStateService.class); + accountAgeWitnessService = injector.getInstance(AccountAgeWitnessService.class); + bondedReputationRepository = injector.getInstance(BondedReputationRepository.class); + bondedRolesRepository = injector.getInstance(BondedRolesRepository.class); + signVerifyService = injector.getInstance(SignVerifyService.class); + daoStateSnapshotService = injector.getInstance(DaoStateSnapshotService.class); } @Override protected void startApplication() { super.startApplication(); - Cookie cookie = injector.getInstance(User.class).getCookie(); - cookie.getAsOptionalBoolean(CookieKey.CLEAN_TOR_DIR_AT_RESTART).ifPresent(cleanTorDirAtRestart -> { - if (cleanTorDirAtRestart) { - injector.getInstance(TorSetup.class).cleanupTorFiles(() -> - cookie.remove(CookieKey.CLEAN_TOR_DIR_AT_RESTART), - log::error); - } - }); - - injector.getInstance(Preferences.class).setUseFullModeDaoMonitor(false); - - daoStateService = injector.getInstance(DaoStateService.class); - accountAgeWitnessService = injector.getInstance(AccountAgeWitnessService.class); - bondedReputationRepository = injector.getInstance(BondedReputationRepository.class); - bondedRolesRepository = injector.getInstance(BondedRolesRepository.class); - signVerifyService = injector.getInstance(SignVerifyService.class); - - injector.getInstance(P2PService.class).addP2PServiceListener(new P2PServiceListener() { - @Override - public void onDataReceived() { - // Do nothing - } - - @Override - public void onNoSeedNodeAvailable() { - // Do nothing - } - - @Override - public void onNoPeersAvailable() { - // Do nothing - } - - @Override - public void onUpdatedDataReceived() { - // Do nothing - } - - @Override - public void onTorNodeReady() { - // Do nothing - } - - @Override - public void onHiddenServicePublished() { - boolean preventPeriodicShutdownAtSeedNode = injector.getInstance(Key.get(boolean.class, - Names.named(Config.PREVENT_PERIODIC_SHUTDOWN_AT_SEED_NODE))); - if (!preventPeriodicShutdownAtSeedNode) { - startShutDownInterval(); - } - UserThread.runAfter(() -> setupConnectionLossCheck(), 60); - - accountAgeWitnessService.onAllServicesInitialized(); - } - - @Override - public void onSetupFailed(Throwable throwable) { - // Do nothing - } - - @Override - public void onRequestCustomBridges() { - // Do nothing - } - }); - } - - private void setupConnectionLossCheck() { - // For dev testing (usually on BTC_REGTEST) we don't want to get the seed shut - // down as it is normal that the seed is the only actively running node. - if (Config.baseCurrencyNetwork() == BaseCurrencyNetwork.BTC_REGTEST) { - return; - } - - if (checkConnectionLossTime != null) { - return; - } - - checkConnectionLossTime = UserThread.runPeriodically(() -> { - if (injector.getInstance(PeerManager.class).getNumAllConnectionsLostEvents() > 1) { - // We set a flag to clear tor cache files at re-start. We cannot clear it now as Tor is used and - // that can cause problems. - injector.getInstance(User.class).getCookie().putAsBoolean(CookieKey.CLEAN_TOR_DIR_AT_RESTART, true); - shutDown(this); - } - }, CHECK_CONNECTION_LOSS_SEC); - + preferences.setUseFullModeDaoMonitor(false); + daoStateSnapshotService.setResyncDaoStateFromResourcesHandler(this::gracefulShutDown); } @Override - public void gracefulShutDown(ResultHandler resultHandler) { - super.gracefulShutDown(resultHandler); + protected void onHiddenServicePublished() { + super.onHiddenServicePublished(); + + accountAgeWitnessService.onAllServicesInitialized(); } } diff --git a/seednode/src/main/java/bisq/seednode/SeedNodeMain.java b/seednode/src/main/java/bisq/seednode/SeedNodeMain.java index a564a93692..1af6154b6f 100644 --- a/seednode/src/main/java/bisq/seednode/SeedNodeMain.java +++ b/seednode/src/main/java/bisq/seednode/SeedNodeMain.java @@ -17,33 +17,23 @@ package bisq.seednode; -import bisq.core.app.TorSetup; import bisq.core.app.misc.ExecutableForAppWithP2p; -import bisq.core.dao.monitoring.DaoStateMonitoringService; import bisq.core.dao.state.DaoStateSnapshotService; -import bisq.core.user.Cookie; import bisq.core.user.CookieKey; import bisq.core.user.User; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.P2PService; -import bisq.network.p2p.P2PServiceListener; -import bisq.network.p2p.peers.PeerManager; import bisq.network.p2p.seed.SeedNodeRepository; -import bisq.common.Timer; import bisq.common.UserThread; import bisq.common.app.Capabilities; import bisq.common.app.Capability; import bisq.common.app.DevEnv; import bisq.common.app.Version; -import bisq.common.config.BaseCurrencyNetwork; import bisq.common.config.Config; import bisq.common.handlers.ResultHandler; -import com.google.inject.Key; -import com.google.inject.name.Names; - import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -57,14 +47,12 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class SeedNodeMain extends ExecutableForAppWithP2p { - private static final long CHECK_CONNECTION_LOSS_SEC = 30; - public static void main(String[] args) { new SeedNodeMain().execute(args); } private SeedNode seedNode; - private Timer checkConnectionLossTimer; + private DaoStateSnapshotService daoStateSnapshotService; public SeedNodeMain() { super("Bisq Seednode", "bisq-seednode", "bisq_seednode", Version.VERSION); @@ -84,19 +72,6 @@ public class SeedNodeMain extends ExecutableForAppWithP2p { } - /////////////////////////////////////////////////////////////////////////////////////////// - // UncaughtExceptionHandler implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void handleUncaughtException(Throwable throwable, boolean doShutDown) { - if (throwable instanceof OutOfMemoryError || doShutDown) { - log.error("We got an OutOfMemoryError and shut down"); - gracefulShutDown(() -> log.info("gracefulShutDown complete")); - } - } - - /////////////////////////////////////////////////////////////////////////////////////////// // We continue with a series of synchronous execution tasks /////////////////////////////////////////////////////////////////////////////////////////// @@ -105,14 +80,12 @@ public class SeedNodeMain extends ExecutableForAppWithP2p { protected void applyInjector() { super.applyInjector(); + daoStateSnapshotService = injector.getInstance(DaoStateSnapshotService.class); seedNode = new SeedNode(injector); } @Override protected void startApplication() { - super.startApplication(); - - Cookie cookie = injector.getInstance(User.class).getCookie(); if (cookie.getAsOptionalBoolean(CookieKey.DELAY_STARTUP).orElse(false)) { cookie.remove(CookieKey.DELAY_STARTUP); try { @@ -125,24 +98,10 @@ public class SeedNodeMain extends ExecutableForAppWithP2p { } } - cookie.getAsOptionalBoolean(CookieKey.CLEAN_TOR_DIR_AT_RESTART).ifPresent(cleanTorDirAtRestart -> { - if (cleanTorDirAtRestart) { - injector.getInstance(TorSetup.class).cleanupTorFiles(() -> - cookie.remove(CookieKey.CLEAN_TOR_DIR_AT_RESTART), - log::error); - } - }); - + super.startApplication(); seedNode.startApplication(); - injector.getInstance(DaoStateMonitoringService.class).addListener(new DaoStateMonitoringService.Listener() { - @Override - public void onCheckpointFailed() { - gracefulShutDown(); - } - }); - - injector.getInstance(DaoStateSnapshotService.class).setResyncDaoStateFromResourcesHandler( + daoStateSnapshotService.setResyncDaoStateFromResourcesHandler( // We set DELAY_STARTUP and shut down. At start up we delay with a deterministic delay to avoid // that all seeds get restarted at the same time. () -> { @@ -150,61 +109,12 @@ public class SeedNodeMain extends ExecutableForAppWithP2p { shutDown(this); } ); - - injector.getInstance(P2PService.class).addP2PServiceListener(new P2PServiceListener() { - @Override - public void onDataReceived() { - // Do nothing - } - - @Override - public void onNoSeedNodeAvailable() { - // Do nothing - } - - @Override - public void onNoPeersAvailable() { - // Do nothing - } - - @Override - public void onUpdatedDataReceived() { - // Do nothing - } - - @Override - public void onTorNodeReady() { - // Do nothing - } - - @Override - public void onHiddenServicePublished() { - boolean preventPeriodicShutdownAtSeedNode = injector.getInstance(Key.get(boolean.class, - Names.named(Config.PREVENT_PERIODIC_SHUTDOWN_AT_SEED_NODE))); - if (!preventPeriodicShutdownAtSeedNode) { - startShutDownInterval(); - } - UserThread.runAfter(() -> setupConnectionLossCheck(), 60); - } - - @Override - public void onSetupFailed(Throwable throwable) { - // Do nothing - } - - @Override - public void onRequestCustomBridges() { - // Do nothing - } - }); } @Override public void gracefulShutDown(ResultHandler resultHandler) { seedNode.shutDown(); - if (checkConnectionLossTimer != null) { - checkConnectionLossTimer.stop(); - } + super.gracefulShutDown(resultHandler); } @@ -255,25 +165,4 @@ public class SeedNodeMain extends ExecutableForAppWithP2p { NodeAddress myAddress = injector.getInstance(P2PService.class).getAddress(); return seedNodeAddresses.indexOf(myAddress); } - - private void setupConnectionLossCheck() { - // For dev testing (usually on BTC_REGTEST) we don't want to get the seed shut - // down as it is normal that the seed is the only actively running node. - if (Config.baseCurrencyNetwork() == BaseCurrencyNetwork.BTC_REGTEST) { - return; - } - - if (checkConnectionLossTimer != null) { - return; - } - - checkConnectionLossTimer = UserThread.runPeriodically(() -> { - if (injector.getInstance(PeerManager.class).getNumAllConnectionsLostEvents() > 1) { - // We set a flag to clear tor cache files at re-start. We cannot clear it now as Tor is used and - // that can cause problems. - injector.getInstance(User.class).getCookie().putAsBoolean(CookieKey.CLEAN_TOR_DIR_AT_RESTART, true); - shutDown(this); - } - }, CHECK_CONNECTION_LOSS_SEC); - } } diff --git a/statsnode/src/main/java/bisq/statistics/StatisticsMain.java b/statsnode/src/main/java/bisq/statistics/StatisticsMain.java index 282aa184ed..99dfbb8a92 100644 --- a/statsnode/src/main/java/bisq/statistics/StatisticsMain.java +++ b/statsnode/src/main/java/bisq/statistics/StatisticsMain.java @@ -25,7 +25,6 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class StatisticsMain extends ExecutableForAppWithP2p { - public static void main(String[] args) { new StatisticsMain().execute(args); } @@ -44,11 +43,6 @@ public class StatisticsMain extends ExecutableForAppWithP2p { keepRunning(); } - - /////////////////////////////////////////////////////////////////////////////////////////// - // We continue with a series of synchronous execution tasks - /////////////////////////////////////////////////////////////////////////////////////////// - @Override protected void applyInjector() { super.applyInjector();