diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 3f4da599eb6..03877655715 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -414,6 +414,18 @@ void RPCExecutor::request(const QString &command, const QString& wallet_name) return; } + // Avoid passing a very large string to the Qt text widget. Appending + // many megabytes of HTML to QTextEdit causes the UI thread to hang + // while the document is re-laid-out. Direct the user to bitcoin-cli + // for commands whose output exceeds the display limit. + if (result.size() > 1_MiB) { + Q_EMIT reply(RPCConsole::CMD_REPLY, + QString("Response too large to display (%1 bytes). " + "Use bitcoin-cli to retrieve the full result.") + .arg(result.size())); + return; + } + Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result)); } catch (UniValue& objError) diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index 68130df88aa..32ffc6888d3 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -34,8 +35,27 @@ static RPCMethod rpcNestedTest_rpc() }; } +// Returns a hex string larger than the RPCExecutor display limit (1 MiB) to +// exercise the large-output path. RPCExecuteCommandLine returns the full +// result; the truncation for display is enforced by RPCExecutor::request(). +static RPCMethod rpcLargeOutput_rpc() +{ + return RPCMethod{ + "rpcLargeOutput", + "return a string larger than 1 MiB", + {}, + RPCResult{RPCResult::Type::STR_HEX, "", ""}, + RPCExamples{""}, + [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { + // 1 MiB + 1 byte of hex characters + return UniValue{std::string(1_MiB + 1, 'a')}; + }, + }; +} + static const CRPCCommand vRPCCommands[] = { {"rpcNestedTest", &rpcNestedTest_rpc}, + {"rpcLargeOutput", &rpcLargeOutput_rpc}, }; void RPCNestedTests::rpcNestedTests() @@ -139,4 +159,10 @@ void RPCNestedTests::rpcNestedTests() QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tolerate empty arguments when using , QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tolerate empty arguments when using , QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tolerate empty arguments when using , + + // Verify that RPCExecuteCommandLine returns the full result for large + // outputs. The size guard lives in RPCExecutor::request() and only affects + // what is rendered in the console widget, not the underlying RPC call. + RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcLargeOutput"); + QVERIFY(result.size() > 1_MiB); }