Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rpc): submit chainlock signature if needed RPC #5765

Merged
merged 11 commits into from
Dec 19, 2023
Merged
70 changes: 70 additions & 0 deletions src/rpc/quorums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,75 @@ static UniValue verifyislock(const JSONRPCRequest& request)
return llmq_ctx.sigman->VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, txid, sig, 0) ||
llmq_ctx.sigman->VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, txid, sig, signOffset);
}

static void submitchainlock_help(const JSONRPCRequest& request)
{
RPCHelpMan{"submitchainlock",
"Submit a ChainLock signature if needed\n",
{
{"blockHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash of the ChainLock."},
{"signature", RPCArg::Type::STR, RPCArg::Optional::NO, "The signature of the ChainLock."},
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved
{"blockHeight", RPCArg::Type::NUM, RPCArg::Optional::NO, "The height of the ChainLock.."},
UdjinM6 marked this conversation as resolved.
Show resolved Hide resolved
},
RPCResults{},
RPCExamples{""},
}.Check(request);
}

static UniValue submitchainlock(const JSONRPCRequest& request)
{
submitchainlock_help(request);

const uint256 nBlockHash(ParseHashV(request.params[0], "blockHash"));

const NodeContext& node = EnsureAnyNodeContext(request.context);
const ChainstateManager& chainman = EnsureChainman(node);

const int nBlockHeight = ParseInt32V(request.params[2], "blockHeight");
CBlockIndex* pIndex{nullptr};
{
LOCK(cs_main);
if (nBlockHeight < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid block height");
}
if (nBlockHeight > chainman.ActiveChain().Height()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "future block");
}
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved
pIndex = chainman.ActiveChain()[nBlockHeight];
if (nBlockHash != pIndex->GetBlockHash()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid blockhash");
UdjinM6 marked this conversation as resolved.
Show resolved Hide resolved
}
}

CBLSSignature sig;
if (pIndex) {
bool use_legacy_signature = !llmq::utils::IsV19Active(pIndex);
if (!sig.SetHexStr(request.params[1].get_str(), use_legacy_signature)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid signature format");
}
} else {
if (!sig.SetHexStr(request.params[1].get_str(), false) &&
!sig.SetHexStr(request.params[1].get_str(), true)
) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid signature format");
}
}
UdjinM6 marked this conversation as resolved.
Show resolved Hide resolved

const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
llmq::CChainLockSig clsig = llmq::CChainLockSig(nBlockHeight, nBlockHash, sig);
if (!llmq_ctx.clhandler->VerifyChainLock(clsig)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid signature");
}

if (llmq_ctx.clhandler->HasChainLock(nBlockHeight, nBlockHash)) {
return true;
}

llmq_ctx.clhandler->ProcessNewChainLock(-1, clsig, ::SerializeHash(clsig));
return true;
}


void RegisterQuorumsRPCCommands(CRPCTable &tableRPC)
{
// clang-format off
Expand All @@ -995,6 +1064,7 @@ static const CRPCCommand commands[] =
{ "evo", "quorum", &_quorum, {} },
{ "evo", "verifychainlock", &verifychainlock, {"blockHash", "signature", "blockHeight"} },
{ "evo", "verifyislock", &verifyislock, {"id", "txid", "signature", "maxHeight"} },
{ "evo", "submitchainlock", &submitchainlock, {"blockHash", "signature", "blockHeight"} },
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved
};
// clang-format on
for (const auto& command : commands) {
Expand Down
1 change: 1 addition & 0 deletions src/rpc/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ void CRPCTable::InitPlatformRestrictions()
{"quorum", {"sign", static_cast<uint8_t>(Params().GetConsensus().llmqTypePlatform)}},
{"quorum", {"verify"}},
{"verifyislock", {}},
{"submitchainlock", {}},
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved
};
}

Expand Down
18 changes: 18 additions & 0 deletions test/functional/feature_llmq_chainlocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,24 @@ def run_test(self):
self.wait_for_chainlocked_block_all_nodes(self.nodes[1].getbestblockhash())
self.test_coinbase_best_cl(self.nodes[0])

self.log.info("Isolate node, mine on another, reconnect and submit CL via RPC")
self.isolate_node(0)
self.nodes[1].generate(1)
self.wait_for_chainlocked_block(self.nodes[1], self.nodes[1].getbestblockhash())
self.reconnect_isolated_node(0, 1)
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved
time.sleep(1)
best_0 = self.nodes[0].getbestchainlock()
best_1 = self.nodes[1].getbestchainlock()
assert best_0['blockhash'] != best_1['blockhash']
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved
assert best_0['height'] != best_1['height']
assert best_0['signature'] != best_1['signature']
self.log.info(self.nodes[0].submitchainlock(best_1['blockhash'], best_1['signature'], best_1['height']))
best_0 = self.nodes[0].getbestchainlock()
best_1 = self.nodes[1].getbestchainlock()
assert best_0['blockhash'] == best_1['blockhash']
assert best_0['height'] == best_1['height']
assert best_0['signature'] == best_1['signature']

self.log.info("Isolate node, mine on both parts of the network, and reconnect")
self.isolate_node(0)
bad_tip = self.nodes[0].generate(5)[-1]
Expand Down
3 changes: 2 additions & 1 deletion test/functional/rpc_platform_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ def test_command(method, params, auth, expexted_status, should_not_match=False):
"getblockcount",
"getbestchainlock",
"quorum",
"verifyislock"]
"verifyislock",
"submitchainlock"]
ogabrielides marked this conversation as resolved.
Show resolved Hide resolved

help_output = self.nodes[0].help().split('\n')
nonwhitelisted = set()
Expand Down
Loading