From 3721d88386909f82e4405ded60b1599b933f6653 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Fri, 17 Jul 2026 19:59:47 -0300 Subject: [PATCH] fix(wallet): guard deferred controller callbacks --- apps/shared/wallet/src/WalletController.cpp | 45 ++++++++++--------- .../tests/cpp/LogosWalletProviderTest.cpp | 26 +++++++++++ 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/apps/shared/wallet/src/WalletController.cpp b/apps/shared/wallet/src/WalletController.cpp index bdf2439..8694dd3 100644 --- a/apps/shared/wallet/src/WalletController.cpp +++ b/apps/shared/wallet/src/WalletController.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -127,31 +128,32 @@ bool WalletController::beginOpen(const QString& config, const QString& storage) emit stateChanged(); } }); + const QPointer guard(this); m_wallet.connectAsync({ config, storage }, - [this, generation, config, storage](WalletSession session) { - if (generation != m_operationGeneration) + [guard, generation, config, storage](WalletSession session) { + if (!guard || generation != guard->m_operationGeneration) return; if (session.failure == WalletFailure::WalletMissing) { - m_state.syncStatus = QStringLiteral("closed"); - m_state.walletExists = false; - emit stateChanged(); + guard->m_state.syncStatus = QStringLiteral("closed"); + guard->m_state.walletExists = false; + emit guard->stateChanged(); return; } if (!session.ok()) { qWarning() << "WalletController: wallet connection failed" << walletFailureCode(session.failure); - m_state.syncStatus = QStringLiteral("error"); - m_state.syncError = walletFailureCode(session.failure); - emit stateChanged(); + guard->m_state.syncStatus = QStringLiteral("error"); + guard->m_state.syncError = walletFailureCode(session.failure); + emit guard->stateChanged(); return; } - m_state.configPath = config; - m_state.storagePath = storage; - m_state.walletExists = QFileInfo::exists(storage) || session.adopted; - m_state.isWalletOpen = true; - m_state.syncStatus = QStringLiteral("ready"); - applySnapshot(session.snapshot); + guard->m_state.configPath = config; + guard->m_state.storagePath = storage; + guard->m_state.walletExists = QFileInfo::exists(storage) || session.adopted; + guard->m_state.isWalletOpen = true; + guard->m_state.syncStatus = QStringLiteral("ready"); + guard->applySnapshot(session.snapshot); }); return true; } @@ -239,18 +241,19 @@ void WalletController::refresh() m_state.syncStatus = QStringLiteral("syncing"); m_state.syncError.clear(); emit stateChanged(); - m_wallet.snapshotAsync(true, [this, generation](WalletSnapshot next) { - if (generation != m_operationGeneration) + const QPointer guard(this); + m_wallet.snapshotAsync(true, [guard, generation](WalletSnapshot next) { + if (!guard || generation != guard->m_operationGeneration) return; if (next.ok()) { - m_state.syncStatus = QStringLiteral("ready"); - applySnapshot(next); + guard->m_state.syncStatus = QStringLiteral("ready"); + guard->applySnapshot(next); } else { qWarning() << "WalletController: wallet refresh failed" << walletFailureCode(next.failure); - m_state.syncStatus = QStringLiteral("error"); - m_state.syncError = walletFailureCode(next.failure); - emit stateChanged(); + guard->m_state.syncStatus = QStringLiteral("error"); + guard->m_state.syncError = walletFailureCode(next.failure); + emit guard->stateChanged(); } }); } diff --git a/apps/shared/wallet/tests/cpp/LogosWalletProviderTest.cpp b/apps/shared/wallet/tests/cpp/LogosWalletProviderTest.cpp index c12c2fb..05573c4 100644 --- a/apps/shared/wallet/tests/cpp/LogosWalletProviderTest.cpp +++ b/apps/shared/wallet/tests/cpp/LogosWalletProviderTest.cpp @@ -64,6 +64,7 @@ private slots: void controllerOpenDoesNotWaitForWalletSync(); void controllerStopsReachabilityChecksAfterDisconnect(); void completedAsyncSnapshotReleasesCallback(); + void deferredCallbacksIgnoreDestroyedController(); }; void LogosWalletProviderTest::adoptsOpenWalletAndCachesSnapshots() @@ -504,6 +505,31 @@ void LogosWalletProviderTest::completedAsyncSnapshotReleasesCallback() QVERIFY(callbackLifetime.expired()); } +void LogosWalletProviderTest::deferredCallbacksIgnoreDestroyedController() +{ + const QString settingsApplication = QStringLiteral("WalletDestroyedCallbackTest"); + QSettings settings(QStringLiteral("Logos"), settingsApplication); + settings.clear(); + + FakeWalletProvider provider; + provider.deferAsync = true; + { + auto controller = std::make_unique(provider, settingsApplication); + QVERIFY(controller->open()); + } + provider.finishConnect(); + + provider.deferAsync = false; + { + auto controller = std::make_unique(provider, settingsApplication); + QVERIFY(controller->open()); + provider.deferAsync = true; + controller->refresh(); + } + provider.finishSnapshot(); + settings.clear(); +} + QTEST_GUILESS_MAIN(LogosWalletProviderTest) #include "LogosWalletProviderTest.moc"