diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 871ef727b15ab..a54fab9c0bf28 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -232,24 +232,38 @@ static RPCHelpMan getbestblockhash() static RPCHelpMan getbestchainlock() { return RPCHelpMan{"getbestchainlock", - "\nReturns information about the best ChainLock. Throws an error if there is no known ChainLock yet.", - {}, - RPCResult{ - RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::STR_HEX, "hash", "The block hash hex-encoded"}, - {RPCResult::Type::NUM, "height", "The block height or index"}, - {RPCResult::Type::STR_HEX, "signature", "The ChainLock's BLS signature"}, - {RPCResult::Type::BOOL, "known_block", "True if the block is known by our node"}, - }}, + "\nIf verbose is 0, returns a string that is serialized, hex-encoded data for the best ChainLock.\n" + "If verbose is 1, returns information about the best ChainLock.\n" + "Throws an error if there is no known ChainLock yet.", + { + {"verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a json object"}, + }, + { + RPCResult{"for verbose = 0", + RPCResult::Type::STR, "data", "The serialized, hex-encoded data for best ChainLock"}, + RPCResult{"for verbose = 1", + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hash", "The block hash hex-encoded"}, + {RPCResult::Type::NUM, "height", "The block height or index"}, + {RPCResult::Type::STR_HEX, "signature", "The ChainLock's BLS signature"}, + {RPCResult::Type::BOOL, "known_block", "True if the block is known by our node"}, + }}, + }, RPCExamples{ HelpExampleCli("getbestchainlock", "") + HelpExampleRpc("getbestchainlock", "") }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - UniValue result(UniValue::VOBJ); - + int verbose = 1; + if (!request.params[0].isNull()) { + if (request.params[0].isBool()) { + verbose = request.params[0].get_bool() ? 1 : 0; + } else { + verbose = request.params[0].get_int(); + } + } const NodeContext& node = EnsureAnyNodeContext(request.context); const LLMQContext& llmq_ctx = EnsureLLMQContext(node); @@ -257,14 +271,22 @@ static RPCHelpMan getbestchainlock() if (clsig.IsNull()) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to find any ChainLock"); } - result.pushKV("blockhash", clsig.getBlockHash().GetHex()); - result.pushKV("height", clsig.getHeight()); - result.pushKV("signature", clsig.getSig().ToString()); + if (verbose) { + UniValue result(UniValue::VOBJ); - const ChainstateManager& chainman = EnsureChainman(node); - LOCK(cs_main); - result.pushKV("known_block", chainman.m_blockman.LookupBlockIndex(clsig.getBlockHash()) != nullptr); - return result; + result.pushKV("blockhash", clsig.getBlockHash().GetHex()); + result.pushKV("height", clsig.getHeight()); + result.pushKV("signature", clsig.getSig().ToString()); + + const ChainstateManager& chainman = EnsureChainman(node); + LOCK(cs_main); + result.pushKV("known_block", chainman.m_blockman.LookupBlockIndex(clsig.getBlockHash()) != nullptr); + return result; + } else { + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << clsig; + return HexStr(ssTx); + } }, }; } diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 4ad0c584076ea..1306d5ac95440 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -112,6 +112,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getblockheaders", 2, "verbose" }, { "getchaintxstats", 0, "nblocks" }, { "getmerkleblocks", 2, "count" }, + { "getbestchainlock", 0, "verbose" }, { "gettransaction", 1, "include_watchonly" }, { "gettransaction", 2, "verbose" }, { "getrawtransaction", 1, "verbose" }, diff --git a/test/functional/interface_zmq_dash.py b/test/functional/interface_zmq_dash.py index b1f64e445da49..275440c1b1817 100755 --- a/test/functional/interface_zmq_dash.py +++ b/test/functional/interface_zmq_dash.py @@ -136,6 +136,8 @@ def run_test(self): self.zmq_context = zmq.Context() # Initialize the network self.nodes[0].sporkupdate("SPORK_17_QUORUM_DKG_ENABLED", 0) + self.log.info("Test RPC hex getbestchainlock before any CL appeared") + assert_raises_rpc_error(-32603, "Unable to find any ChainLock", self.nodes[0].getbestchainlock, 0) self.wait_for_sporks_same() self.activate_v19(expected_activation_height=900) self.log.info("Activated v19 at height:" + str(self.nodes[0].getblockcount())) @@ -263,6 +265,7 @@ def test_chainlock_publishers(self): assert_equal(uint256_to_string(zmq_chain_lock.blockHash), rpc_chain_lock_hash) assert_equal(zmq_chain_locked_block.hash, rpc_chain_lock_hash) assert_equal(zmq_chain_lock.sig.hex(), rpc_best_chain_lock_sig) + assert_equal(zmq_chain_lock.serialize().hex(), self.nodes[0].getbestchainlock(0)) # Unsubscribe from ChainLock messages self.unsubscribe(chain_lock_publishers)