rpc|llmq|test: Implement verifychainlock (#4119)

* llmq: Make CLSIG_REQUESTID_PREFIX available outside of quorums_chainlocks.cpp

* rpc: Implement `verifychainlock`

* test: Add rpc_verifychainlock.py

* rpc: Fix help text

Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>

Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
dustinface 2021-04-26 13:51:15 +02:00 committed by pasta
parent 5f3c580b4d
commit 97e75f0147
5 changed files with 112 additions and 1 deletions

View File

@ -17,7 +17,7 @@
namespace llmq
{
static const std::string CLSIG_REQUESTID_PREFIX = "clsig";
const std::string CLSIG_REQUESTID_PREFIX = "clsig";
CChainLocksHandler* chainLocksHandler;

View File

@ -22,6 +22,8 @@ class CScheduler;
namespace llmq
{
extern const std::string CLSIG_REQUESTID_PREFIX;
class CChainLockSig
{
public:

View File

@ -15,6 +15,10 @@
#include <llmq/quorums_signing.h>
#include <llmq/quorums_signing_shares.h>
namespace llmq {
extern const std::string CLSIG_REQUESTID_PREFIX;
}
void quorum_list_help()
{
throw std::runtime_error(
@ -652,6 +656,48 @@ UniValue quorum(const JSONRPCRequest& request)
}
}
void verifychainlock_help()
{
throw std::runtime_error(
"verifychainlock \"blockHash\" \"signature\" ( blockHeight )\n"
"Test if a quorum signature is valid for a ChainLock.\n"
"\nArguments:\n"
"1. \"blockHash\" (string, required) The block hash of the ChainLock.\n"
"2. \"signature\" (string, required) The signature of the ChainLock.\n"
"3. blockHeight (integer, optional) The height of the ChainLock. There will be an internal lookup of \"blockHash\" if this is not provided.\n"
);
}
UniValue verifychainlock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) {
verifychainlock_help();
}
const uint256 nBlockHash = ParseHashV(request.params[0], "blockHash");
CBLSSignature chainLockSig;
if (!chainLockSig.SetHexStr(request.params[1].get_str())) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid signature format");
}
int nBlockHeight;
if (request.params[2].isNull()) {
LOCK(cs_main);
const CBlockIndex* pIndex = LookupBlockIndex(nBlockHash);
if (pIndex == nullptr) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "blockHash not found");
}
nBlockHeight = pIndex->nHeight;
} else {
nBlockHeight = ParseInt32V(request.params[2], "blockHeight");
}
const auto llmqType = Params().GetConsensus().llmqTypeChainLocks;
const uint256 nRequestId = ::SerializeHash(std::make_pair(llmq::CLSIG_REQUESTID_PREFIX, nBlockHeight));
return llmq::CSigningManager::VerifyRecoveredSig(llmqType, nBlockHeight, nRequestId, nBlockHash, chainLockSig);
}
void verifyislock_help()
{
throw std::runtime_error(
@ -713,6 +759,7 @@ static const CRPCCommand commands[] =
{ // category name actor (function)
// --------------------- ------------------------ -----------------------
{ "evo", "quorum", &quorum, {} },
{ "evo", "verifychainlock", &verifychainlock, {"blockHash", "signature", "blockHeight"} },
{ "evo", "verifyislock", &verifyislock, {"id", "txid", "signature", "maxHeight"} },
};

View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
# Copyright (c) 2021 The Dash Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from test_framework.test_framework import DashTestFramework
from test_framework.util import assert_raises_rpc_error
'''
rpc_verifychainlock.py
Test verifychainlock rpc
'''
class RPCVerifyChainLockTest(DashTestFramework):
def set_test_params(self):
# -whitelist is needed to avoid the trickling logic on node0
self.set_dash_test_params(5, 3, [["-whitelist=127.0.0.1"], [], [], [], []], fast_dip3_enforcement=True)
self.set_dash_llmq_test_params(3, 2)
def run_test(self):
node0 = self.nodes[0]
node1 = self.nodes[1]
self.activate_dip8()
self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 0)
self.wait_for_sporks_same()
self.mine_quorum()
self.wait_for_chainlocked_block(node0, node0.generate(1)[0])
chainlock = node0.getbestchainlock()
block_hash = chainlock["blockhash"]
height = chainlock["height"]
chainlock_signature = chainlock["signature"]
# No "blockHeight"
assert node0.verifychainlock(block_hash, chainlock_signature)
# Invalid "blockHeight"
assert not node0.verifychainlock(block_hash, chainlock_signature, 0)
# Valid "blockHeight"
assert node0.verifychainlock(block_hash, chainlock_signature, height)
# Invalid "blockHash"
assert not node0.verifychainlock(node0.getblockhash(0), chainlock_signature, height)
# Isolate node1, mine a block on node0 and wait for its ChainLock
node1.setnetworkactive(False)
node0.generate(1)
self.wait_for_chainlocked_block(node0, node0.generate(1)[0])
chainlock = node0.getbestchainlock()
assert chainlock != node1.getbestchainlock()
block_hash = chainlock["blockhash"]
height = chainlock["height"]
chainlock_signature = chainlock["signature"]
# Now it should verify on node0 without "blockHeight" but fail on node1
assert node0.verifychainlock(block_hash, chainlock_signature)
assert_raises_rpc_error(-32603, "blockHash not found", node1.verifychainlock, block_hash, chainlock_signature)
# But they should still both verify with the height provided
assert node0.verifychainlock(block_hash, chainlock_signature, height)
assert node1.verifychainlock(block_hash, chainlock_signature, height)
if __name__ == '__main__':
RPCVerifyChainLockTest().main()

View File

@ -162,6 +162,7 @@ BASE_SCRIPTS= [
'rpc_masternode.py',
'rpc_mnauth.py',
'rpc_verifyislock.py',
'rpc_verifychainlock.py',
'p2p_fingerprint.py',
'rpc_platform_filter.py',
'feature_dip0020_activation.py',