From 969a98aa27be9c38e11eff9264b4293305bb3791 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Fri, 17 Jul 2026 20:03:33 -0300 Subject: [PATCH] fix(wallet): coalesce reachability probes --- apps/shared/wallet/src/WalletController.cpp | 13 ++++- apps/shared/wallet/src/WalletController.h | 3 ++ .../tests/cpp/LogosWalletProviderTest.cpp | 53 +++++++++++++++---- 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/apps/shared/wallet/src/WalletController.cpp b/apps/shared/wallet/src/WalletController.cpp index 00db035..78ff308 100644 --- a/apps/shared/wallet/src/WalletController.cpp +++ b/apps/shared/wallet/src/WalletController.cpp @@ -284,13 +284,24 @@ void WalletController::checkReachability() if (!m_state.isWalletOpen || m_state.sequencerAddress.isEmpty()) return; - const quint64 generation = ++m_reachabilityGeneration; const QString endpoint = m_state.sequencerAddress; + if (m_reachabilityReply && endpoint == m_reachabilityEndpoint) + return; + + const quint64 generation = ++m_reachabilityGeneration; + if (m_reachabilityReply) + m_reachabilityReply->abort(); QNetworkRequest request{QUrl(endpoint)}; request.setTransferTimeout(4000); QNetworkReply* reply = m_network->get(request); + m_reachabilityReply = reply; + m_reachabilityEndpoint = endpoint; connect(reply, &QNetworkReply::finished, this, [this, reply, generation, endpoint]() { + if (m_reachabilityReply == reply) { + m_reachabilityReply = nullptr; + m_reachabilityEndpoint.clear(); + } if (!m_state.isWalletOpen || generation != m_reachabilityGeneration || endpoint != m_state.sequencerAddress) { diff --git a/apps/shared/wallet/src/WalletController.h b/apps/shared/wallet/src/WalletController.h index 2662a6d..a529a22 100644 --- a/apps/shared/wallet/src/WalletController.h +++ b/apps/shared/wallet/src/WalletController.h @@ -6,6 +6,7 @@ #include "WalletProvider.h" class QNetworkAccessManager; +class QNetworkReply; class QTimer; class WalletAccountModel; @@ -70,6 +71,8 @@ private: WalletUiState m_state; WalletAccountModel* m_accountModel; QNetworkAccessManager* m_network; + QNetworkReply* m_reachabilityReply = nullptr; + QString m_reachabilityEndpoint; QTimer* m_reachabilityTimer; bool m_started = false; quint64 m_operationGeneration = 0; diff --git a/apps/shared/wallet/tests/cpp/LogosWalletProviderTest.cpp b/apps/shared/wallet/tests/cpp/LogosWalletProviderTest.cpp index 3edfc7f..817e51f 100644 --- a/apps/shared/wallet/tests/cpp/LogosWalletProviderTest.cpp +++ b/apps/shared/wallet/tests/cpp/LogosWalletProviderTest.cpp @@ -69,6 +69,7 @@ private slots: void completedAsyncSnapshotReleasesCallback(); void deferredCallbacksIgnoreDestroyedController(); void newerReachabilityResultWins(); + void coalescesReachabilityChecksForSameEndpoint(); }; void LogosWalletProviderTest::adoptsOpenWalletAndCachesSnapshots() @@ -540,27 +541,32 @@ void LogosWalletProviderTest::newerReachabilityResultWins() QSettings settings(QStringLiteral("Logos"), settingsApplication); settings.clear(); - QTcpServer server; - QVERIFY(server.listen(QHostAddress::LocalHost)); - const QString endpoint = QStringLiteral("http://127.0.0.1:%1").arg(server.serverPort()); + QTcpServer firstServer; + QTcpServer secondServer; + QVERIFY(firstServer.listen(QHostAddress::LocalHost)); + QVERIFY(secondServer.listen(QHostAddress::LocalHost)); + const QString firstEndpoint = QStringLiteral("http://127.0.0.1:%1") + .arg(firstServer.serverPort()); + const QString secondEndpoint = QStringLiteral("http://127.0.0.1:%1") + .arg(secondServer.serverPort()); FakeWalletProvider provider; - provider.connectResult.snapshot.sequencerAddress = endpoint; + provider.connectResult.snapshot.sequencerAddress = firstEndpoint; WalletController controller(provider, settingsApplication); auto* network = controller.findChild(); QVERIFY(network); QSignalSpy finished(network, &QNetworkAccessManager::finished); QVERIFY(controller.open()); - QTRY_VERIFY(server.hasPendingConnections()); - QTcpSocket* first = server.nextPendingConnection(); + QTRY_VERIFY(firstServer.hasPendingConnections()); + QTcpSocket* first = firstServer.nextPendingConnection(); QVERIFY(first); provider.createAccountResult.accountId = ACCOUNT_B; - provider.createAccountResult.snapshot.sequencerAddress = endpoint; + provider.createAccountResult.snapshot.sequencerAddress = secondEndpoint; QCOMPARE(controller.createAccount(true), ACCOUNT_B); - QTRY_VERIFY(server.hasPendingConnections()); - QTcpSocket* second = server.nextPendingConnection(); + QTRY_VERIFY(secondServer.hasPendingConnections()); + QTcpSocket* second = secondServer.nextPendingConnection(); QVERIFY(second); second->write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"); @@ -574,6 +580,35 @@ void LogosWalletProviderTest::newerReachabilityResultWins() settings.clear(); } +void LogosWalletProviderTest::coalescesReachabilityChecksForSameEndpoint() +{ + const QString settingsApplication = QStringLiteral("WalletReachabilityCoalesceTest"); + QSettings settings(QStringLiteral("Logos"), settingsApplication); + settings.clear(); + + QTcpServer server; + QVERIFY(server.listen(QHostAddress::LocalHost)); + const QString endpoint = QStringLiteral("http://127.0.0.1:%1").arg(server.serverPort()); + + FakeWalletProvider provider; + provider.connectResult.snapshot.sequencerAddress = endpoint; + WalletController controller(provider, settingsApplication); + QVERIFY(controller.open()); + QTRY_VERIFY(server.hasPendingConnections()); + QTcpSocket* request = server.nextPendingConnection(); + QVERIFY(request); + + provider.createAccountResult.accountId = ACCOUNT_B; + provider.createAccountResult.snapshot.sequencerAddress = endpoint; + QCOMPARE(controller.createAccount(true), ACCOUNT_B); + QTest::qWait(50); + QVERIFY(!server.hasPendingConnections()); + + request->write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"); + request->disconnectFromHost(); + settings.clear(); +} + QTEST_GUILESS_MAIN(LogosWalletProviderTest) #include "LogosWalletProviderTest.moc"