From 1c74b668b60b01845dc3812be17117cadf8390ac Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Mon, 23 Sep 2019 21:36:55 +0300 Subject: [PATCH] Introduce getbestchainlock rpc and fix llmq-is-cl-conflicts.py (#3094) * Introduce getbestchainlock rpc and fix llmq-is-cl-conflicts.py * Add `known_block` field and move `getbestchainlock` to `blockchain` rpc category * Add CChainLockSig::IsNull() and throw an exception in getbestchainlock if there is no known chainlock yet * drop blockHash initializer --- src/llmq/quorums_chainlocks.cpp | 13 ++++++++++- src/llmq/quorums_chainlocks.h | 2 ++ src/rpc/blockchain.cpp | 29 +++++++++++++++++++++++++ test/functional/llmq-is-cl-conflicts.py | 15 ++++++++++--- 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/llmq/quorums_chainlocks.cpp b/src/llmq/quorums_chainlocks.cpp index 3bd9ecfe81..5a926164f9 100644 --- a/src/llmq/quorums_chainlocks.cpp +++ b/src/llmq/quorums_chainlocks.cpp @@ -23,6 +23,11 @@ static const std::string CLSIG_REQUESTID_PREFIX = "clsig"; CChainLocksHandler* chainLocksHandler; +bool CChainLockSig::IsNull() const +{ + return nHeight == -1 && blockHash == uint256(); +} + std::string CChainLockSig::ToString() const { return strprintf("CChainLockSig(nHeight=%d, blockHash=%s)", nHeight, blockHash.ToString()); @@ -72,6 +77,12 @@ bool CChainLocksHandler::GetChainLockByHash(const uint256& hash, llmq::CChainLoc return true; } +CChainLockSig CChainLocksHandler::GetBestChainLock() +{ + LOCK(cs); + return bestChainLock; +} + void CChainLocksHandler::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) { if (!sporkManager.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED)) { @@ -101,7 +112,7 @@ void CChainLocksHandler::ProcessNewChainLock(NodeId from, const llmq::CChainLock return; } - if (bestChainLock.nHeight != -1 && clsig.nHeight <= bestChainLock.nHeight) { + if (!bestChainLock.IsNull() && clsig.nHeight <= bestChainLock.nHeight) { // no need to process/relay older CLSIGs return; } diff --git a/src/llmq/quorums_chainlocks.h b/src/llmq/quorums_chainlocks.h index 04c2dd2aee..48354845df 100644 --- a/src/llmq/quorums_chainlocks.h +++ b/src/llmq/quorums_chainlocks.h @@ -38,6 +38,7 @@ public: READWRITE(sig); } + bool IsNull() const; std::string ToString() const; }; @@ -85,6 +86,7 @@ public: bool AlreadyHave(const CInv& inv); bool GetChainLockByHash(const uint256& hash, CChainLockSig& ret); + CChainLockSig GetBestChainLock(); void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman); void ProcessNewChainLock(NodeId from, const CChainLockSig& clsig, const uint256& hash); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 60b7d25fcf..c9360d0ed8 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -207,6 +207,34 @@ UniValue getbestblockhash(const JSONRPCRequest& request) return chainActive.Tip()->GetBlockHash().GetHex(); } +UniValue getbestchainlock(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 0) + throw std::runtime_error( + "getbestchainlock\n" + "\nReturns the block hash of the best chainlock. Throws an error if there is no known chainlock yet.\n" + "\nResult:\n" + "{\n" + " \"blockhash\" : \"hash\", (string) The block hash hex encoded\n" + " \"height\" : n, (numeric) The block height or index\n" + " \"known_block\" : true|false (boolean) True if the block is known by our node\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getbestchainlock", "") + + HelpExampleRpc("getbestchainlock", "") + ); + UniValue result(UniValue::VOBJ); + llmq::CChainLockSig clsig = llmq::chainLocksHandler->GetBestChainLock(); + if (clsig.IsNull()) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to find any chainlock"); + } + result.push_back(Pair("blockhash", clsig.blockHash.GetHex())); + result.push_back(Pair("height", clsig.nHeight)); + LOCK(cs_main); + result.push_back(Pair("known_block", mapBlockIndex.count(clsig.blockHash) > 0)); + return result; +} + void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex) { if(pindex) { @@ -2161,6 +2189,7 @@ static const CRPCCommand commands[] = { "blockchain", "getchaintxstats", &getchaintxstats, true, {"nblocks", "blockhash"} }, { "blockchain", "getblockstats", &getblockstats, true, {"hash_or_height", "stats"} }, { "blockchain", "getbestblockhash", &getbestblockhash, true, {} }, + { "blockchain", "getbestchainlock", &getbestchainlock, true, {} }, { "blockchain", "getblockcount", &getblockcount, true, {} }, { "blockchain", "getblock", &getblock, true, {"blockhash","verbosity|verbose"} }, { "blockchain", "getblockhashes", &getblockhashes, true, {"high","low"} }, diff --git a/test/functional/llmq-is-cl-conflicts.py b/test/functional/llmq-is-cl-conflicts.py index 52f9cdfe6f..066628b6f4 100755 --- a/test/functional/llmq-is-cl-conflicts.py +++ b/test/functional/llmq-is-cl-conflicts.py @@ -112,9 +112,7 @@ class LLMQ_IS_CL_Conflicts(DashTestFramework): cl = self.create_chainlock(self.nodes[0].getblockcount() + 1, block.sha256) self.test_node.send_clsig(cl) - # Give the CLSIG some time to propagate. We unfortunately can't check propagation here as "getblock/getblockheader" - # is required to check for CLSIGs, but this requires the block header to be propagated already - time.sleep(1) + self.wait_for_best_chainlock(self.nodes[1], "%064x" % block.sha256) # The block should get accepted now, and at the same time prune the conflicting ISLOCKs submit_result = self.nodes[1].submitblock(ToHex(block)) @@ -223,6 +221,17 @@ class LLMQ_IS_CL_Conflicts(DashTestFramework): time.sleep(0.1) raise AssertionError("wait_for_chainlock timed out") + def wait_for_best_chainlock(self, node, block_hash): + t = time.time() + while time.time() - t < 15: + try: + if node.getbestchainlock()["blockhash"] == block_hash: + return + except: + pass + time.sleep(0.1) + raise AssertionError("wait_for_best_chainlock timed out") + def create_block(self, node, vtx=[]): bt = node.getblocktemplate() height = bt['height']