diff --git a/src/BlockchainBackend.cpp b/src/BlockchainBackend.cpp index 832c475..342df00 100644 --- a/src/BlockchainBackend.cpp +++ b/src/BlockchainBackend.cpp @@ -19,6 +19,12 @@ const QString BlockchainBackend::BLOCKCHAIN_MODULE_NAME = QStringLiteral("liblogos_blockchain_module"); +void BlockchainBackend::setError(const QString& message) +{ + setLastErrorMessage(message); + setStatus(Error); +} + static QString toLocalPath(const QString& pathInput) { if (pathInput.trimmed().isEmpty()) @@ -26,6 +32,30 @@ static QString toLocalPath(const QString& pathInput) return QUrl::fromUserInput(pathInput).toLocalFile(); } +// invokeRemoteMethod() returns an invalid QVariant (not a LogosResult) when +// the call itself fails to get a reply (timeout, disconnected module, etc.), +// as opposed to the remote method running and reporting failure normally. +static LogosResult toLogosResult(const QVariant& reply) +{ + if (!reply.isValid()) + return LogosResult{false, QVariant(), QStringLiteral("Call failed.")}; + return reply.value(); +} + +static QString toErrorMessage(const LogosResult& result) +{ + return QStringLiteral("Error: %1").arg(result.error.toString()); +} + +// QML callers distinguish success from failure by sniffing for an "Error" +// prefix (see BlockchainView.qml), so both branches must share this type. +// TODO: Currently functions are returning a string (for ok and ko cases), which is later re-parsed to know if the call +// succeeded or failed. We can simplify this by returning the result itself upward. +static QString toDisplayMessage(const LogosResult& result) +{ + return result.success ? result.value.toString() : toErrorMessage(result); +} + BlockchainBackend::BlockchainBackend(LogosAPI* logosAPI, QObject* parent) : BlockchainBackendSimpleSource(parent) , m_logosAPI(logosAPI) @@ -83,7 +113,7 @@ BlockchainBackend::BlockchainBackend(LogosAPI* logosAPI, QObject* parent) m_blockchainClient = m_logosAPI->getClient(BLOCKCHAIN_MODULE_NAME); if (!m_blockchainClient) { - setStatus(ErrorNotInitialized); + setError(QStringLiteral("Module not initialized")); qWarning() << "BlockchainBackend: failed to get blockchain module client"; return; } @@ -105,7 +135,7 @@ BlockchainBackend::BlockchainBackend(LogosAPI* logosAPI, QObject* parent) m_logModel->append(line); }); } else { - setStatus(ErrorSubscribeFailed); + setError(QStringLiteral("Failed to subscribe to events")); } qDebug() << "BlockchainBackend: initialized"; @@ -120,23 +150,20 @@ BlockchainBackend::~BlockchainBackend() void BlockchainBackend::startBlockchain() { if (!m_blockchainClient) { - setStatus(ErrorNotInitialized); + setError(QStringLiteral("Module not initialized")); return; } setStatus(Starting); - QVariant result = m_blockchainClient->invokeRemoteMethod( - BLOCKCHAIN_MODULE_NAME, "start", userConfig(), deploymentConfig()); - int resultCode = result.isValid() ? result.toInt() : -1; + const LogosResult result = toLogosResult(m_blockchainClient->invokeRemoteMethod( + BLOCKCHAIN_MODULE_NAME, "start", userConfig(), deploymentConfig())); - if (resultCode == 0 || resultCode == 1) { + if (result.success) { setStatus(Running); QTimer::singleShot(500, this, [this]() { refreshAccounts(); }); - } else if (resultCode == 2) { - setStatus(ErrorConfigMissing); } else { - setStatus(ErrorStartFailed); + setError(result.error.toString()); } } @@ -146,32 +173,32 @@ void BlockchainBackend::stopBlockchain() return; if (!m_blockchainClient) { - setStatus(ErrorNotInitialized); + setError(QStringLiteral("Module not initialized")); return; } setStatus(Stopping); - QVariant result = m_blockchainClient->invokeRemoteMethod( - BLOCKCHAIN_MODULE_NAME, "stop"); - int resultCode = result.isValid() ? result.toInt() : -1; + const LogosResult result = toLogosResult(m_blockchainClient->invokeRemoteMethod( + BLOCKCHAIN_MODULE_NAME, "stop")); - if (resultCode == 0 || resultCode == 1) + if (result.success) { setStatus(Stopped); - else - setStatus(ErrorStopFailed); + } else { + setError(result.error.toString()); + } } void BlockchainBackend::refreshAccounts() { if (!m_blockchainClient) return; - QVariant result = m_blockchainClient->invokeRemoteMethod( - BLOCKCHAIN_MODULE_NAME, "wallet_get_known_addresses"); - QStringList list = - result.isValid() && result.canConvert() - ? result.toStringList() - : QStringList(); + const LogosResult result = toLogosResult(m_blockchainClient->invokeRemoteMethod( + BLOCKCHAIN_MODULE_NAME, "wallet_get_known_addresses")); + + QStringList list; + if (result.success && result.value.canConvert()) + list = result.value.toStringList(); m_accountsModel->setAddresses(list); @@ -194,10 +221,8 @@ QString BlockchainBackend::getBalance(QString addressHex) if (!m_blockchainClient) { result = QStringLiteral("Error: Module not initialized."); } else { - QVariant v = m_blockchainClient->invokeRemoteMethod( - BLOCKCHAIN_MODULE_NAME, "wallet_get_balance", addressHex); - result = v.isValid() ? v.toString() - : QStringLiteral("Error: Call failed."); + result = toDisplayMessage(toLogosResult(m_blockchainClient->invokeRemoteMethod( + BLOCKCHAIN_MODULE_NAME, "wallet_get_balance", addressHex))); } m_accountsModel->setBalanceForAddress(addressHex, result); @@ -211,11 +236,9 @@ QString BlockchainBackend::transferFunds( return QStringLiteral("Error: Module not initialized."); QStringList senders{fromKeyHex}; - QVariant result = m_blockchainClient->invokeRemoteMethod( + return toDisplayMessage(toLogosResult(m_blockchainClient->invokeRemoteMethod( BLOCKCHAIN_MODULE_NAME, "wallet_transfer_funds", - fromKeyHex, senders, toKeyHex, amountStr, QString()); - return result.isValid() ? result.toString() - : QStringLiteral("Error: Call failed."); + fromKeyHex, senders, toKeyHex, amountStr, QString()))); } int BlockchainBackend::generateConfig( @@ -272,9 +295,10 @@ int BlockchainBackend::generateConfig( const QString jsonToSend = QString::fromUtf8(doc.toJson(QJsonDocument::Compact)); - QVariant result = m_blockchainClient->invokeRemoteMethod( - BLOCKCHAIN_MODULE_NAME, "generate_user_config", jsonToSend); - return result.isValid() ? result.toInt() : -1; + const LogosResult result = toLogosResult(m_blockchainClient->invokeRemoteMethod( + BLOCKCHAIN_MODULE_NAME, "generate_user_config", jsonToSend)); + + return result.success ? 0 : -1; } void BlockchainBackend::clearLogs() diff --git a/src/BlockchainBackend.h b/src/BlockchainBackend.h index ee58990..e10be38 100644 --- a/src/BlockchainBackend.h +++ b/src/BlockchainBackend.h @@ -52,6 +52,7 @@ public slots: private: void fetchBalancesForAccounts(const QStringList& list); + void setError(const QString& message); LogosAPI* m_logosAPI = nullptr; LogosAPIClient* m_blockchainClient = nullptr; diff --git a/src/BlockchainBackend.rep b/src/BlockchainBackend.rep index f518c9e..8184c5c 100644 --- a/src/BlockchainBackend.rep +++ b/src/BlockchainBackend.rep @@ -1,12 +1,13 @@ class BlockchainBackend { - ENUM BlockchainStatus { NotStarted=0, Starting=1, Running=2, Stopping=3, Stopped=4, Error=5, ErrorNotInitialized=6, ErrorConfigMissing=7, ErrorStartFailed=8, ErrorStopFailed=9, ErrorSubscribeFailed=10 } + ENUM BlockchainStatus { NotStarted=0, Starting=1, Running=2, Stopping=3, Stopped=4, Error=5 } PROP(BlockchainStatus status READONLY) PROP(QString userConfig READWRITE) PROP(QString deploymentConfig READWRITE) PROP(bool useGeneratedConfig READWRITE) PROP(QString generatedUserConfigPath READONLY) + PROP(QString lastErrorMessage READONLY) SLOT(void startBlockchain()) SLOT(void stopBlockchain()) diff --git a/src/qml/BlockchainView.qml b/src/qml/BlockchainView.qml index 9816083..a6ff565 100644 --- a/src/qml/BlockchainView.qml +++ b/src/qml/BlockchainView.qml @@ -3,7 +3,7 @@ import QtQuick.Controls import QtQuick.Layouts import Logos.Theme -// BlockchainStatus enum (NotStarted/Starting/Running/.../ErrorSubscribeFailed) +// BlockchainStatus enum (NotStarted/Starting/Running/Stopping/Stopped/Error) // declared in BlockchainBackend.rep — registered with QML by the replica // factory plugin. import Logos.BlockchainBackend 1.0 @@ -48,12 +48,7 @@ Rectangle { case BlockchainBackend.Running: return qsTr("Running") case BlockchainBackend.Stopping: return qsTr("Stopping...") case BlockchainBackend.Stopped: return qsTr("Stopped") - case BlockchainBackend.Error: return qsTr("Error") - case BlockchainBackend.ErrorNotInitialized: return qsTr("Error: Module not initialized") - case BlockchainBackend.ErrorConfigMissing: return qsTr("Error: Config path missing") - case BlockchainBackend.ErrorStartFailed: return qsTr("Error: Failed to start node") - case BlockchainBackend.ErrorStopFailed: return qsTr("Error: Failed to stop node") - case BlockchainBackend.ErrorSubscribeFailed: return qsTr("Error: Failed to subscribe to events") + case BlockchainBackend.Error: return qsTr("Error: %1").arg(root.backend.lastErrorMessage) default: return qsTr("Unknown") } }