Skip to content

Commit

Permalink
feat: add param verbose=0|1 to RPC getbestchainlock
Browse files Browse the repository at this point in the history
It adds ability to receive raw chainlock in hex format (compatible with ZMQ subscription)
  • Loading branch information
knst committed Jan 9, 2025
1 parent 3edc738 commit 1ecb6b5
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 19 deletions.
60 changes: 41 additions & 19 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,39 +232,61 @@ 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);
const llmq::CChainLockSig clsig = llmq_ctx.clhandler->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);
}
},
};
}
Expand Down
1 change: 1 addition & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
3 changes: 3 additions & 0 deletions test/functional/interface_zmq_dash.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand Down Expand Up @@ -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)

Expand Down

0 comments on commit 1ecb6b5

Please sign in to comment.