From 0e3c426ac16f4559d4284a00645dc12d70472ac2 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 29 Jul 2023 23:57:01 +0000 Subject: [PATCH 1/4] rpc: implement getgeneralinfo --- src/rpc/node.cpp | 39 +++++++++++++++++++++++++++ test/functional/rpc_getgeneralinfo.py | 28 +++++++++++++++++++ test/functional/test_runner.py | 1 + 3 files changed, 68 insertions(+) create mode 100755 test/functional/rpc_getgeneralinfo.py diff --git a/src/rpc/node.cpp b/src/rpc/node.cpp index 6b3662996c..660f38877b 100644 --- a/src/rpc/node.cpp +++ b/src/rpc/node.cpp @@ -4,6 +4,9 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include +#include +#include #include #include #include @@ -14,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -184,6 +188,40 @@ static RPCHelpMan getmemoryinfo() }; } +static RPCHelpMan getgeneralinfo() +{ + return RPCHelpMan{"getgeneralinfo", + "Returns data about the bitcoin daemon.\n", + {}, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "clientversion", "Client version"}, + {RPCResult::Type::STR, "useragent", "Client name"}, + {RPCResult::Type::STR, "datadir", "Data directory path"}, + {RPCResult::Type::STR, "blocksdir", "Blocks directory path"}, + {RPCResult::Type::NUM, "startuptime", "Startup time"}, + } + }, + RPCExamples{ + HelpExampleCli("getgeneralinfo", "") + + HelpExampleRpc("getgeneralinfo", "") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const ArgsManager& args{EnsureAnyArgsman(request.context)}; + + UniValue obj(UniValue::VOBJ); + obj.pushKV("clientversion", FormatFullVersion()); + obj.pushKV("useragent", strSubVersion); + obj.pushKV("datadir", fs::PathToString(args.GetDataDirNet())); + obj.pushKV("blocksdir", fs::PathToString(args.GetBlocksDirPath())); + obj.pushKV("startuptime", GetStartupTime()); + return obj; +}, + }; +} + static void EnableOrDisableLogCategories(UniValue cats, bool enable) { cats = cats.get_array(); for (unsigned int i = 0; i < cats.size(); ++i) { @@ -397,6 +435,7 @@ void RegisterNodeRPCCommands(CRPCTable& t) { static const CRPCCommand commands[]{ {"control", &getmemoryinfo}, + {"control", &getgeneralinfo}, {"control", &logging}, {"util", &getindexinfo}, {"hidden", &setmocktime}, diff --git a/test/functional/rpc_getgeneralinfo.py b/test/functional/rpc_getgeneralinfo.py new file mode 100755 index 0000000000..bb2b6c049a --- /dev/null +++ b/test/functional/rpc_getgeneralinfo.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the getgeneralinfo RPC.""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, try_rpc + +class GetGeneralInfoTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = False + self.num_nodes = 1 + + def skip_test_if_missing_module(self): + self.skip_if_no_cli() + + def run_test(self): + """Test getgeneralinfo.""" + """Check if 'getgeneralinfo' is callable.""" + try_rpc(None, None, self.nodes[0].getgeneralinfo, None, None) + """Check if 'getgeneralinfo' is idempotent (multiple requests return same data).""" + json1 = self.nodes[0].getgeneralinfo() + json2 = self.nodes[0].getgeneralinfo() + assert_equal(json1, json2) + +if __name__ == '__main__': + GetGeneralInfoTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index fbf48a0e4d..ee16a0fe25 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -377,6 +377,7 @@ BASE_SCRIPTS = [ 'feature_settings.py', 'rpc_getdescriptorinfo.py', 'rpc_mempool_info.py', + 'rpc_getgeneralinfo.py', 'rpc_help.py', 'feature_dirsymlinks.py', 'feature_help.py', From 98aedae44332523b176487808c95beca68e95c3f Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 10 May 2022 02:33:58 +0000 Subject: [PATCH 2/4] QA: Actually check getgeneralinfo results are correct --- test/functional/rpc_getgeneralinfo.py | 34 +++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/test/functional/rpc_getgeneralinfo.py b/test/functional/rpc_getgeneralinfo.py index bb2b6c049a..ed9c3198ed 100755 --- a/test/functional/rpc_getgeneralinfo.py +++ b/test/functional/rpc_getgeneralinfo.py @@ -1,28 +1,48 @@ #!/usr/bin/env python3 -# Copyright (c) 2020 The Bitcoin Core developers +# Copyright (c) 2020-2022 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the getgeneralinfo RPC.""" +import os.path +import time + from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, try_rpc +from test_framework.util import assert_equal class GetGeneralInfoTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = False self.num_nodes = 1 - - def skip_test_if_missing_module(self): - self.skip_if_no_cli() + self.mock_starttime = int(time.time()) # needs to be newer than blocks in db + self.extra_args = [ + [f'-mocktime={self.mock_starttime}',] + ] def run_test(self): """Test getgeneralinfo.""" - """Check if 'getgeneralinfo' is callable.""" - try_rpc(None, None, self.nodes[0].getgeneralinfo, None, None) + self.nodes[0].setmocktime(self.mock_starttime + 20) + """Check if 'getgeneralinfo' is idempotent (multiple requests return same data).""" json1 = self.nodes[0].getgeneralinfo() json2 = self.nodes[0].getgeneralinfo() assert_equal(json1, json2) + """Check 'getgeneralinfo' result is correct.""" + ver_search = f'version {json1["clientversion"]} (' + with open(self.nodes[0].debug_log_path, encoding='utf-8') as dl: + for line in dl: + if line.find(ver_search) >= 0: + break + else: + raise AssertionError(f"Failed to find clientversion '{json1['clientversion']}' in debug log") + + assert_equal(json1['useragent'], self.nodes[0].getnetworkinfo()['subversion']) + + net_datadir = str(self.nodes[0].chain_path) + assert_equal(json1['datadir'], net_datadir) + assert_equal(json1['blocksdir'], os.path.join(net_datadir, "blocks")) + assert_equal(json1['startuptime'], self.mock_starttime) + if __name__ == '__main__': GetGeneralInfoTest().main() From 34bf07df61bb629ef92755c25ca298a734039214 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 22 Sep 2023 19:20:20 +0000 Subject: [PATCH 3/4] Bugfix: QA: rpc_getgeneralinfo: Adapt test for startuptime ignoring mocktime --- test/functional/rpc_getgeneralinfo.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/functional/rpc_getgeneralinfo.py b/test/functional/rpc_getgeneralinfo.py index ed9c3198ed..0caa2d9ea3 100755 --- a/test/functional/rpc_getgeneralinfo.py +++ b/test/functional/rpc_getgeneralinfo.py @@ -14,14 +14,11 @@ class GetGeneralInfoTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = False self.num_nodes = 1 - self.mock_starttime = int(time.time()) # needs to be newer than blocks in db - self.extra_args = [ - [f'-mocktime={self.mock_starttime}',] - ] + self.min_starttime = int(time.time()) def run_test(self): """Test getgeneralinfo.""" - self.nodes[0].setmocktime(self.mock_starttime + 20) + max_starttime = int(time.time()) + 1 """Check if 'getgeneralinfo' is idempotent (multiple requests return same data).""" json1 = self.nodes[0].getgeneralinfo() @@ -42,7 +39,8 @@ class GetGeneralInfoTest(BitcoinTestFramework): net_datadir = str(self.nodes[0].chain_path) assert_equal(json1['datadir'], net_datadir) assert_equal(json1['blocksdir'], os.path.join(net_datadir, "blocks")) - assert_equal(json1['startuptime'], self.mock_starttime) + # startuptime is set before mocktime param is loaded + assert json1['startuptime'] >= self.min_starttime and json1['startuptime'] <= max_starttime if __name__ == '__main__': GetGeneralInfoTest().main() From 84b8a92eb3d21a5552e17407bcb23954a847ddb3 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 10 Oct 2023 00:30:11 +0000 Subject: [PATCH 4/4] Bugfix: QA/fuzz: Add getgeneralinfo to RPC_COMMANDS_SAFE_FOR_FUZZING --- src/test/fuzz/rpc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp index 270cab58e2..bd0b828e35 100644 --- a/src/test/fuzz/rpc.cpp +++ b/src/test/fuzz/rpc.cpp @@ -129,6 +129,7 @@ const std::vector RPC_COMMANDS_SAFE_FOR_FUZZING{ "getdeploymentinfo", "getdescriptorinfo", "getdifficulty", + "getgeneralinfo", "getindexinfo", "getmemoryinfo", "getmempoolancestors",