From df5152c5ee0466ab8760cf160c66db982283ac0b Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Fri, 17 Jul 2026 19:38:35 -0300 Subject: [PATCH] fix(amm): back off network identity probes --- apps/amm/src/ActiveNetwork.cpp | 29 ++++++++++++++++++++++++ apps/amm/src/ActiveNetwork.h | 2 ++ apps/amm/src/AmmUiBackend.cpp | 14 +++++++++--- apps/amm/src/AmmUiBackend.h | 1 + apps/amm/tests/cpp/ActiveNetworkTest.cpp | 17 ++++++++++++++ 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/apps/amm/src/ActiveNetwork.cpp b/apps/amm/src/ActiveNetwork.cpp index c92bc73..6397357 100644 --- a/apps/amm/src/ActiveNetwork.cpp +++ b/apps/amm/src/ActiveNetwork.cpp @@ -1,5 +1,8 @@ #include "ActiveNetwork.h" +#include +#include + #include #include #include @@ -30,6 +33,7 @@ bool ActiveNetwork::load() m_network = {}; m_network.status = QStringLiteral("config_missing"); m_expectedIdentity.clear(); + m_failedIdentityProbes = 0; const QByteArray selected = qgetenv(NETWORK_ENV); m_network.id = selected.isEmpty() @@ -93,8 +97,28 @@ bool ActiveNetwork::needsIdentityProbe() const || m_network.status == QStringLiteral("network_unknown"); } +int ActiveNetwork::identityRetryDelayMs() const +{ + static constexpr int retryDelays[] = { + 1000, + 2000, + 4000, + 8000, + 16000, + 30000, + }; + if (m_failedIdentityProbes <= 0 + || m_network.status != QStringLiteral("network_unknown")) { + return 0; + } + const int index = (std::min)(m_failedIdentityProbes, + static_cast(std::size(retryDelays))) - 1; + return retryDelays[index]; +} + void ActiveNetwork::sequencerChanged(bool available) { + m_failedIdentityProbes = 0; if (isConfigured()) clearIdentity(available ? QStringLiteral("loading") : QStringLiteral("network_unknown")); } @@ -103,6 +127,8 @@ void ActiveNetwork::reachabilityChanged(bool reachable, bool wasReachable) { if (!isConfigured()) return; + if (!reachable || !wasReachable) + m_failedIdentityProbes = 0; if (!reachable) clearIdentity(QStringLiteral("network_unknown")); else if (!wasReachable) @@ -118,10 +144,13 @@ void ActiveNetwork::beginIdentityProbe() void ActiveNetwork::finishIdentityProbe(const QString& identity) { if (identity.isEmpty()) { + m_failedIdentityProbes = (std::min)(m_failedIdentityProbes + 1, 6); clearIdentity(QStringLiteral("network_unknown")); } else if (identity != m_expectedIdentity) { + m_failedIdentityProbes = 0; clearIdentity(QStringLiteral("network_mismatch")); } else { + m_failedIdentityProbes = 0; m_network.status = QStringLiteral("ready"); m_network.fingerprint = (isDevnet() ? QStringLiteral("channel:") : QStringLiteral("block10:")) diff --git a/apps/amm/src/ActiveNetwork.h b/apps/amm/src/ActiveNetwork.h index 9b1db9b..c0ed875 100644 --- a/apps/amm/src/ActiveNetwork.h +++ b/apps/amm/src/ActiveNetwork.h @@ -19,6 +19,7 @@ public: bool isConfigured() const; bool isDevnet() const; bool needsIdentityProbe() const; + int identityRetryDelayMs() const; ActiveNetworkSnapshot snapshot() const { return m_network; } void sequencerChanged(bool available); @@ -33,4 +34,5 @@ private: ActiveNetworkSnapshot m_network; QString m_expectedIdentity; + int m_failedIdentityProbes = 0; }; diff --git a/apps/amm/src/AmmUiBackend.cpp b/apps/amm/src/AmmUiBackend.cpp index 67b8fd5..f2f5a12 100644 --- a/apps/amm/src/AmmUiBackend.cpp +++ b/apps/amm/src/AmmUiBackend.cpp @@ -69,13 +69,17 @@ AmmUiBackend::AmmUiBackend(LogosAPI* logosAPI, QObject* parent) m_newPosition(std::make_unique( m_wallet.get(), m_ammClient.get(), m_sequencer.get())), m_net(new QNetworkAccessManager(this)), - m_transactionTimer(new QTimer(this)) + m_transactionTimer(new QTimer(this)), + m_identityRetryTimer(new QTimer(this)) { setNewPositionQuoteResult({}); setNewPositionSubmitResult({}); m_transactionTimer->setInterval(5000); connect(m_transactionTimer, &QTimer::timeout, this, &AmmUiBackend::pollTransactions); + m_identityRetryTimer->setSingleShot(true); + connect(m_identityRetryTimer, &QTimer::timeout, + this, &AmmUiBackend::probeNetworkIdentity); m_network.load(); connect(m_walletController.get(), &WalletController::stateChanged, @@ -157,8 +161,6 @@ void AmmUiBackend::refreshNewPositionContext(QVariantMap request) else { request = m_newPositionHints; } - if (m_network.status() == QStringLiteral("network_unknown")) - probeNetworkIdentity(); const quint64 generation = ++m_contextGeneration; m_newPosition->contextAsync( request, m_network.snapshot(), isWalletOpen(), refreshWalletAccounts, @@ -222,11 +224,13 @@ void AmmUiBackend::syncWalletState() const bool addressChanged = previousAddress != state.sequencerAddress; if (addressChanged) { + m_identityRetryTimer->stop(); m_pendingTransactions.clear(); m_transactionTimer->stop(); m_network.sequencerChanged(!state.sequencerAddress.isEmpty()); } if (addressChanged || wasReachable != state.sequencerReachable) { + m_identityRetryTimer->stop(); m_network.reachabilityChanged(state.sequencerReachable, wasReachable); } if (walletWasOpen && !state.isWalletOpen) @@ -245,6 +249,7 @@ void AmmUiBackend::syncWalletState() void AmmUiBackend::probeNetworkIdentity() { if (m_identityProbeInFlight + || m_identityRetryTimer->isActive() || !m_network.isConfigured() || sequencerAddr().isEmpty()) { return; @@ -283,6 +288,9 @@ void AmmUiBackend::probeNetworkIdentity() ? channelIdFromResponse(payload) : blockHashFromResponse(payload); m_network.finishIdentityProbe(actual); + const int retryDelay = m_network.identityRetryDelayMs(); + if (retryDelay > 0) + m_identityRetryTimer->start(retryDelay); reply->deleteLater(); publishNetworkContext(); }); diff --git a/apps/amm/src/AmmUiBackend.h b/apps/amm/src/AmmUiBackend.h index 6355b83..7eabb89 100644 --- a/apps/amm/src/AmmUiBackend.h +++ b/apps/amm/src/AmmUiBackend.h @@ -74,6 +74,7 @@ private: QNetworkAccessManager* m_net; QTimer* m_transactionTimer; + QTimer* m_identityRetryTimer; ActiveNetwork m_network; QVariantMap m_newPositionHints; diff --git a/apps/amm/tests/cpp/ActiveNetworkTest.cpp b/apps/amm/tests/cpp/ActiveNetworkTest.cpp index 239f3e8..6374aae 100644 --- a/apps/amm/tests/cpp/ActiveNetworkTest.cpp +++ b/apps/amm/tests/cpp/ActiveNetworkTest.cpp @@ -40,7 +40,24 @@ int main() "loaded network should await identity")) return 1; + network.finishIdentityProbe({}); + if (!expect(network.identityRetryDelayMs() == 1000, + "first failed identity probe should back off for one second")) + return 1; + network.finishIdentityProbe({}); + if (!expect(network.identityRetryDelayMs() == 2000, + "repeated identity failures should increase the delay")) + return 1; + for (int attempt = 0; attempt < 8; ++attempt) + network.finishIdentityProbe({}); + if (!expect(network.identityRetryDelayMs() == 30000, + "identity retry delay should cap at thirty seconds")) + return 1; + network.sequencerChanged(true); + if (!expect(network.identityRetryDelayMs() == 0, + "sequencer changes should reset identity retry backoff")) + return 1; network.finishIdentityProbe(QString(64, QLatin1Char('d'))); if (!expect(network.status() == QStringLiteral("network_mismatch"), "wrong identity should block the network"))