From 2a837f8243364d611564115ee0d078ddf98b5384 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 13 Jun 2024 21:16:15 +0700 Subject: [PATCH] refactor: use new composite for RPC 'protx NNN' --- src/rpc/evo.cpp | 640 +++++++++++++++++++++++++++++++----------------- 1 file changed, 414 insertions(+), 226 deletions(-) diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index f94f7037e7..543fad3e3a 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -343,13 +343,26 @@ static std::string SignAndSendSpecialTx(const JSONRPCRequest& request, CChainsta return sendrawtransaction().HandleRequest(sendRequest).get_str(); } -static void protx_register_fund_help(const JSONRPCRequest& request, bool legacy) +// forward declaration +static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, + CChainstateHelper& chain_helper, + const ChainstateManager& chainman, + const bool specific_legacy_bls_scheme, + const bool isExternalRegister, + const bool isFundRegister, + const bool isPrepareRegister, + const MnType mnType); + +static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& request, CChainstateHelper& chain_helper, CDeterministicMNManager& dmnman, const ChainstateManager& chainman, const MnType mnType); + + +static RPCHelpMan protx_register_fund_wrapper(const bool legacy) { std::string rpc_name = legacy ? "register_fund_legacy" : "register_fund"; std::string rpc_full_name = std::string("protx ").append(rpc_name); std::string pubkey_operator = legacy ? "\"0532646990082f4fd639f90387b1551f2c7c39d37392cb9055a06a7e85c1d23692db8f87f827886310bccc1e29db9aee\"" : "\"8532646990082f4fd639f90387b1551f2c7c39d37392cb9055a06a7e85c1d23692db8f87f827886310bccc1e29db9aee\""; std::string rpc_example = rpc_name.append(" \"" + EXAMPLE_ADDRESS[0] + "\" \"1.2.3.4:1234\" \"" + EXAMPLE_ADDRESS[1] + "\" ").append(pubkey_operator).append(" \"" + EXAMPLE_ADDRESS[1] + "\" 0 \"" + EXAMPLE_ADDRESS[0] + "\""); - RPCHelpMan{rpc_full_name, + return RPCHelpMan{rpc_full_name, "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move 1000 Dash\n" "to the address specified by collateralAddress and will then function as the collateral of your\n" "masternode.\n" @@ -376,16 +389,37 @@ static void protx_register_fund_help(const JSONRPCRequest& request, bool legacy) RPCExamples{ HelpExampleCli("protx", rpc_example) }, - }.Check(request); + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.chain_helper); + CChainstateHelper& chain_helper = *node.chain_helper; + + const bool isExternalRegister = false; + const bool isFundRegister = true; + const bool isPrepareRegister = false; + return protx_register_common_wrapper(request, chain_helper, chainman, legacy, isExternalRegister, isFundRegister, isPrepareRegister, MnType::Regular); +}, + }; } -static void protx_register_help(const JSONRPCRequest& request, bool legacy) +static RPCHelpMan protx_register_fund() { + return protx_register_fund_wrapper(false); +} + +static RPCHelpMan protx_register_fund_legacy() { + return protx_register_fund_wrapper(true); +} + +static RPCHelpMan protx_register_wrapper(bool legacy) { std::string rpc_name = legacy ? "register_legacy" : "register"; std::string rpc_full_name = std::string("protx ").append(rpc_name); std::string pubkey_operator = legacy ? "\"0532646990082f4fd639f90387b1551f2c7c39d37392cb9055a06a7e85c1d23692db8f87f827886310bccc1e29db9aee\"" : "\"8532646990082f4fd639f90387b1551f2c7c39d37392cb9055a06a7e85c1d23692db8f87f827886310bccc1e29db9aee\""; std::string rpc_example = rpc_name.append(" \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"" + EXAMPLE_ADDRESS[1] + "\" ").append(pubkey_operator).append(" \"" + EXAMPLE_ADDRESS[1] + "\" 0 \"" + EXAMPLE_ADDRESS[0] + "\""); - RPCHelpMan{rpc_full_name, + return RPCHelpMan{rpc_full_name, "\nSame as \"protx register_fund\", but with an externally referenced collateral.\n" "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent\n" "transaction output spendable by this wallet. It must also not be used by any other masternode.\n" @@ -409,18 +443,42 @@ static void protx_register_help(const JSONRPCRequest& request, bool legacy) RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, }, RPCExamples{ - HelpExampleCli("protx", rpc_example) + HelpExampleCli("protx", rpc_example), }, - }.Check(request); + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.chain_helper); + CChainstateHelper& chain_helper = *node.chain_helper; + + const bool isExternalRegister = true; + const bool isFundRegister = false; + const bool isPrepareRegister = false; + + return protx_register_common_wrapper(request, chain_helper, chainman, legacy, isExternalRegister, isFundRegister, isPrepareRegister, MnType::Regular); +}, + }; } -static void protx_register_prepare_help(const JSONRPCRequest& request, bool legacy) +static RPCHelpMan protx_register() +{ + return protx_register_wrapper(false); +} + +static RPCHelpMan protx_register_legacy() +{ + return protx_register_wrapper(true); +} + +static RPCHelpMan protx_register_prepare_wrapper(const bool legacy) { std::string rpc_name = legacy ? "register_prepare_legacy" : "register_prepare"; std::string rpc_full_name = std::string("protx ").append(rpc_name); std::string pubkey_operator = legacy ? "\"0532646990082f4fd639f90387b1551f2c7c39d37392cb9055a06a7e85c1d23692db8f87f827886310bccc1e29db9aee\"" : "\"8532646990082f4fd639f90387b1551f2c7c39d37392cb9055a06a7e85c1d23692db8f87f827886310bccc1e29db9aee\""; std::string rpc_example = rpc_name.append(" \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"" + EXAMPLE_ADDRESS[1] + "\" ").append(pubkey_operator).append(" \"" + EXAMPLE_ADDRESS[1] + "\" 0 \"" + EXAMPLE_ADDRESS[0] + "\""); - RPCHelpMan{rpc_full_name, + return RPCHelpMan{rpc_full_name, "\nCreates an unsigned ProTx and a message that must be signed externally\n" "with the private key that corresponds to collateralAddress to prove collateral ownership.\n" "The prepared transaction will also contain inputs and outputs to cover fees.\n", @@ -445,33 +503,37 @@ static void protx_register_prepare_help(const JSONRPCRequest& request, bool lega RPCExamples{ HelpExampleCli("protx", rpc_example) }, - }.Check(request); + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.chain_helper); + CChainstateHelper& chain_helper = *node.chain_helper; + + const bool isExternalRegister = false; + const bool isFundRegister = false; + const bool isPrepareRegister = true; + return protx_register_common_wrapper(request, chain_helper, chainman, legacy, isExternalRegister, isFundRegister, isPrepareRegister, MnType::Regular); +}, + }; } -static void protx_register_submit_help(const JSONRPCRequest& request) +static RPCHelpMan protx_register_prepare() { - RPCHelpMan{"protx register_submit", - "\nCombines the unsigned ProTx and a signature of the signMessage, signs all inputs\n" - "which were added to cover fees and submits the resulting transaction to the network.\n" - "Note: See \"help protx register_prepare\" for more info about creating a ProTx and a message to sign.\n" - + HELP_REQUIRING_PASSPHRASE, - { - {"tx", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The serialized unsigned ProTx in hex format."}, - {"sig", RPCArg::Type::STR, RPCArg::Optional::NO, "The signature signed with the collateral key. Must be in base64 format."}, - }, - RPCResult{ - RPCResult::Type::STR_HEX, "txid", "The transaction id" - }, - RPCExamples{ - HelpExampleCli("protx", "register_submit \"tx\" \"sig\"") - }, - }.Check(request); + return protx_register_prepare_wrapper(false); } -static void protx_register_fund_evo_help(const JSONRPCRequest& request) +static RPCHelpMan protx_register_prepare_legacy() { - RPCHelpMan{ - "protx register_fund_evo", + return protx_register_prepare_wrapper(true); +} + +static RPCHelpMan protx_register_fund_evo_wrapper(bool use_hpmn_suffix) +{ + const std::string command_name{use_hpmn_suffix ? "protx register_fund_hpmn" : "protx register_fund_evo"}; + return RPCHelpMan{ + command_name, "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move 4000 Dash\n" "to the address specified by collateralAddress and will then function as the collateral of your\n" "EvoNode.\n" @@ -500,13 +562,41 @@ static void protx_register_fund_evo_help(const JSONRPCRequest& request) }, RPCExamples{ HelpExampleCli("protx", "register_fund_evo \"" + EXAMPLE_ADDRESS[0] + "\" \"1.2.3.4:1234\" \"" + EXAMPLE_ADDRESS[1] + "\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"" + EXAMPLE_ADDRESS[1] + "\" 0 \"" + EXAMPLE_ADDRESS[0] + "\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822")}, - }.Check(request); + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + if (use_hpmn_suffix && !IsDeprecatedRPCEnabled("hpmn")) { + throw JSONRPCError(RPC_METHOD_DEPRECATED, "*_hpmn methods are deprecated. Use the related *_evo methods or set -deprecatedrpc=hpmn to enable them"); + } + + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.chain_helper); + CChainstateHelper& chain_helper = *node.chain_helper; + + const bool isExternalRegister = false; + const bool isFundRegister = true; + const bool isPrepareRegister = false; + return protx_register_common_wrapper(request, chain_helper, chainman, false, isExternalRegister, isFundRegister, isPrepareRegister, MnType::Evo); +}, + }; } -static void protx_register_evo_help(const JSONRPCRequest& request) +static RPCHelpMan protx_register_fund_evo() { - RPCHelpMan{ - "protx register_evo", + return protx_register_fund_evo_wrapper(false); +} + +static RPCHelpMan protx_register_fund_hpmn() +{ + return protx_register_fund_evo_wrapper(true); +} + +static RPCHelpMan protx_register_evo_wrapper(bool use_hpmn_suffix) +{ + const std::string command_name{use_hpmn_suffix ? "protx register_hpmn" : "protx register_evo"}; + return RPCHelpMan{ + command_name, "\nSame as \"protx register_fund_evo\", but with an externally referenced collateral.\n" "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent\n" "transaction output spendable by this wallet. It must also not be used by any other masternode.\n" + @@ -534,13 +624,42 @@ static void protx_register_evo_help(const JSONRPCRequest& request) }, RPCExamples{ HelpExampleCli("protx", "register_evo \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"" + EXAMPLE_ADDRESS[1] + "\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"" + EXAMPLE_ADDRESS[1] + "\" 0 \"" + EXAMPLE_ADDRESS[0] + "\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822")}, - }.Check(request); + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + if (use_hpmn_suffix && !IsDeprecatedRPCEnabled("hpmn")) { + throw JSONRPCError(RPC_METHOD_DEPRECATED, "*_hpmn methods are deprecated. Use the related *_evo methods or set -deprecatedrpc=hpmn to enable them"); + } + + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.chain_helper); + CChainstateHelper& chain_helper = *node.chain_helper; + + const bool isExternalRegister = true; + const bool isFundRegister = false; + const bool isPrepareRegister = false; + + return protx_register_common_wrapper(request, chain_helper, chainman, false, isExternalRegister, isFundRegister, isPrepareRegister, MnType::Evo); +}, + }; } -static void protx_register_prepare_evo_help(const JSONRPCRequest& request) +static RPCHelpMan protx_register_evo() { - RPCHelpMan{ - "protx register_prepare_evo", + return protx_register_evo_wrapper(false); +} + +static RPCHelpMan protx_register_hpmn() +{ + return protx_register_evo_wrapper(true); +} + +static RPCHelpMan protx_register_prepare_evo_wrapper(bool use_hpmn_suffix) +{ + const std::string command_name{use_hpmn_suffix ? "protx register_prepare_hpmn" : "protx register_prepare_evo"}; + return RPCHelpMan{ + command_name, "\nCreates an unsigned ProTx and a message that must be signed externally\n" "with the private key that corresponds to collateralAddress to prove collateral ownership.\n" "The prepared transaction will also contain inputs and outputs to cover fees.\n", @@ -565,7 +684,34 @@ static void protx_register_prepare_evo_help(const JSONRPCRequest& request) {RPCResult::Type::STR_HEX, "signMessage", "The string message that needs to be signed with the collateral key"}, }}, RPCExamples{HelpExampleCli("protx", "register_prepare_evo \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"" + EXAMPLE_ADDRESS[1] + "\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"" + EXAMPLE_ADDRESS[1] + "\" 0 \"" + EXAMPLE_ADDRESS[0] + "\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822")}, - }.Check(request); + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + if (use_hpmn_suffix && !IsDeprecatedRPCEnabled("hpmn")) { + throw JSONRPCError(RPC_METHOD_DEPRECATED, "*_hpmn methods are deprecated. Use the related *_evo methods or set -deprecatedrpc=hpmn to enable them"); + } + + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.chain_helper); + CChainstateHelper& chain_helper = *node.chain_helper; + + const bool isExternalRegister = false; + const bool isFundRegister = false; + const bool isPrepareRegister = true; + return protx_register_common_wrapper(request, chain_helper, chainman, false, isExternalRegister, isFundRegister, isPrepareRegister, MnType::Evo); +}, + }; +} + +static RPCHelpMan protx_register_prepare_evo() +{ + return protx_register_prepare_evo_wrapper(false); +} + +static RPCHelpMan protx_register_prepare_hpmn() +{ + return protx_register_prepare_evo_wrapper(true); } static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, @@ -578,23 +724,6 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, const MnType mnType) { const bool isEvoRequested = mnType == MnType::Evo; - if (isEvoRequested) { - if (isFundRegister && (request.fHelp || (request.params.size() < 10 || request.params.size() > 12))) { - protx_register_fund_evo_help(request); - } else if (isExternalRegister && (request.fHelp || (request.params.size() < 11 || request.params.size() > 13))) { - protx_register_evo_help(request); - } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 11 && request.params.size() != 12))) { - protx_register_prepare_evo_help(request); - } - } else { - if (isFundRegister && (request.fHelp || (request.params.size() < 7 || request.params.size() > 9))) { - protx_register_fund_help(request, specific_legacy_bls_scheme); - } else if (isExternalRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) { - protx_register_help(request, specific_legacy_bls_scheme); - } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 8 && request.params.size() != 9))) { - protx_register_prepare_help(request, specific_legacy_bls_scheme); - } - } std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); if (!wallet) return NullUniValue; @@ -826,9 +955,30 @@ static UniValue protx_register_legacy(const JSONRPCRequest& request, CChainstate return protx_register_common_wrapper(request, chain_helper, chainman, true, isExternalRegister, isFundRegister, isPrepareRegister, MnType::Regular); } -static UniValue protx_register_submit(const JSONRPCRequest& request, CChainstateHelper& chain_helper, const ChainstateManager& chainman) +static RPCHelpMan protx_register_submit() { - protx_register_submit_help(request); + return RPCHelpMan{"protx register_submit", + "\nCombines the unsigned ProTx and a signature of the signMessage, signs all inputs\n" + "which were added to cover fees and submits the resulting transaction to the network.\n" + "Note: See \"help protx register_prepare\" for more info about creating a ProTx and a message to sign.\n" + + HELP_REQUIRING_PASSPHRASE, + { + {"tx", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The serialized unsigned ProTx in hex format."}, + {"sig", RPCArg::Type::STR, RPCArg::Optional::NO, "The signature signed with the collateral key. Must be in base64 format."}, + }, + RPCResult{ + RPCResult::Type::STR_HEX, "txid", "The transaction id" + }, + RPCExamples{ + HelpExampleCli("protx", "register_submit \"tx\" \"sig\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.chain_helper); + CChainstateHelper& chain_helper = *node.chain_helper; std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); if (!wallet) return NullUniValue; @@ -860,11 +1010,13 @@ static UniValue protx_register_submit(const JSONRPCRequest& request, CChainstate SetTxPayload(tx, ptx); return SignAndSendSpecialTx(request, chain_helper, chainman, tx); +}, + }; } -static void protx_update_service_help(const JSONRPCRequest& request) +static RPCHelpMan protx_update_service() { - RPCHelpMan{"protx update_service", + return RPCHelpMan{"protx update_service", "\nCreates and sends a ProUpServTx to the network. This will update the IP address\n" "of a masternode.\n" "If this is done for a masternode that got PoSe-banned, the ProUpServTx will also revive this masternode.\n" @@ -882,13 +1034,27 @@ static void protx_update_service_help(const JSONRPCRequest& request) RPCExamples{ HelpExampleCli("protx", "update_service \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" 5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd") }, - }.Check(request); + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.dmnman); + CDeterministicMNManager& dmnman = *node.dmnman; + + CHECK_NONFATAL(node.chain_helper); + CChainstateHelper& chain_helper = *node.chain_helper; + + return protx_update_service_common_wrapper(request, chain_helper, dmnman, chainman, MnType::Regular); +}, + }; } -static void protx_update_service_evo_help(const JSONRPCRequest& request) +static RPCHelpMan protx_update_service_evo_wrapper(bool use_hpmn_suffix) { - RPCHelpMan{ - "protx update_service_evo", + const std::string command_name{use_hpmn_suffix ? "protx update_service_hpmn" : "protx update_service_evo"}; + return RPCHelpMan{ + command_name, "\nCreates and sends a ProUpServTx to the network. This will update the IP address and the Platform fields\n" "of an EvoNode.\n" "If this is done for an EvoNode that got PoSe-banned, the ProUpServTx will also revive this EvoNode.\n" + @@ -907,24 +1073,39 @@ static void protx_update_service_evo_help(const JSONRPCRequest& request) RPCResult::Type::STR_HEX, "txid", "The transaction id"}, RPCExamples{ HelpExampleCli("protx", "update_service_evo \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" \"5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822")}, - }.Check(request); + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + if (use_hpmn_suffix && !IsDeprecatedRPCEnabled("hpmn")) { + throw JSONRPCError(RPC_METHOD_DEPRECATED, "*_hpmn methods are deprecated. Use the related *_evo methods or set -deprecatedrpc=hpmn to enable them"); + } + + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.dmnman); + CDeterministicMNManager& dmnman = *node.dmnman; + + CHECK_NONFATAL(node.chain_helper); + CChainstateHelper& chain_helper = *node.chain_helper; + + return protx_update_service_common_wrapper(request, chain_helper, dmnman, chainman, MnType::Evo); +}, + }; +} + +static RPCHelpMan protx_update_service_evo() +{ + return protx_update_service_evo_wrapper(false); +} + +static RPCHelpMan protx_update_service_hpmn() +{ + return protx_update_service_evo_wrapper(true); } static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& request, CChainstateHelper& chain_helper, CDeterministicMNManager& dmnman, const ChainstateManager& chainman, const MnType mnType) { - if (request.strMethod.find("_hpmn") != std::string::npos) { - if (!IsDeprecatedRPCEnabled("hpmn")) { - throw JSONRPCError(RPC_METHOD_DEPRECATED, "*_hpmn methods are deprecated. Use the related *_evo methods or set -deprecatedrpc=hpmn to enable them"); - } - } - const bool isEvoRequested = mnType == MnType::Evo; - if (isEvoRequested) { - protx_update_service_evo_help(request); - } else { - protx_update_service_help(request); - } - std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); if (!wallet) return NullUniValue; @@ -1025,20 +1206,20 @@ static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& reques return SignAndSendSpecialTx(request, chain_helper, chainman, tx); } -static void protx_update_registrar_help(const JSONRPCRequest& request, bool legacy) +static RPCHelpMan protx_update_registrar_wrapper(bool specific_legacy_bls_scheme) { - std::string rpc_name = legacy ? "update_registrar_legacy" : "update_registrar"; + std::string rpc_name = specific_legacy_bls_scheme ? "update_registrar_legacy" : "update_registrar"; std::string rpc_full_name = std::string("protx ").append(rpc_name); - std::string pubkey_operator = legacy ? "\"0532646990082f4fd639f90387b1551f2c7c39d37392cb9055a06a7e85c1d23692db8f87f827886310bccc1e29db9aee\"" : "\"8532646990082f4fd639f90387b1551f2c7c39d37392cb9055a06a7e85c1d23692db8f87f827886310bccc1e29db9aee\""; + std::string pubkey_operator = specific_legacy_bls_scheme ? "\"0532646990082f4fd639f90387b1551f2c7c39d37392cb9055a06a7e85c1d23692db8f87f827886310bccc1e29db9aee\"" : "\"8532646990082f4fd639f90387b1551f2c7c39d37392cb9055a06a7e85c1d23692db8f87f827886310bccc1e29db9aee\""; std::string rpc_example = rpc_name.append(" \"0123456701234567012345670123456701234567012345670123456701234567\" ").append(pubkey_operator).append(" \"" + EXAMPLE_ADDRESS[1] + "\""); - RPCHelpMan{rpc_full_name, + return RPCHelpMan{rpc_full_name, "\nCreates and sends a ProUpRegTx to the network. This will update the operator key, voting key and payout\n" "address of the masternode specified by \"proTxHash\".\n" "The owner key of the masternode must be known to your wallet.\n" + HELP_REQUIRING_PASSPHRASE, { GetRpcArg("proTxHash"), - legacy ? GetRpcArg("operatorPubKey_update_legacy") : GetRpcArg("operatorPubKey_update"), + specific_legacy_bls_scheme ? GetRpcArg("operatorPubKey_update_legacy") : GetRpcArg("operatorPubKey_update"), GetRpcArg("votingAddress_update"), GetRpcArg("payoutAddress_update"), GetRpcArg("feeSourceAddress"), @@ -1049,12 +1230,16 @@ static void protx_update_registrar_help(const JSONRPCRequest& request, bool lega RPCExamples{ HelpExampleCli("protx", rpc_example) }, - }.Check(request); -} - -static UniValue protx_update_registrar_wrapper(const JSONRPCRequest& request, CChainstateHelper& chain_helper, CDeterministicMNManager& dmnman, const ChainstateManager& chainman, const bool specific_legacy_bls_scheme) + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - protx_update_registrar_help(request, specific_legacy_bls_scheme); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.dmnman); + CDeterministicMNManager& dmnman = *node.dmnman; + + CHECK_NONFATAL(node.chain_helper); + CChainstateHelper& chain_helper = *node.chain_helper; std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); if (!wallet) return NullUniValue; @@ -1126,21 +1311,23 @@ static UniValue protx_update_registrar_wrapper(const JSONRPCRequest& request, CC SetTxPayload(tx, ptx); return SignAndSendSpecialTx(request, chain_helper, chainman, tx); +}, + }; } -static UniValue protx_update_registrar(const JSONRPCRequest& request, CChainstateHelper& chain_helper, CDeterministicMNManager& dmnman, const ChainstateManager& chainman) +static RPCHelpMan protx_update_registrar() { - return protx_update_registrar_wrapper(request, chain_helper, dmnman, chainman, false); + return protx_update_registrar_wrapper(false); } -static UniValue protx_update_registrar_legacy(const JSONRPCRequest& request, CChainstateHelper& chain_helper, CDeterministicMNManager& dmnman, const ChainstateManager& chainman) +static RPCHelpMan protx_update_registrar_legacy() { - return protx_update_registrar_wrapper(request, chain_helper, dmnman, chainman, true); + return protx_update_registrar_wrapper(true); } -static void protx_revoke_help(const JSONRPCRequest& request) +static RPCHelpMan protx_revoke() { - RPCHelpMan{"protx revoke", + return RPCHelpMan{"protx revoke", "\nCreates and sends a ProUpRevTx to the network. This will revoke the operator key of the masternode and\n" "put it into the PoSe-banned state. It will also set the service field of the masternode\n" "to zero. Use this in case your operator key got compromised or you want to stop providing your service\n" @@ -1158,12 +1345,17 @@ static void protx_revoke_help(const JSONRPCRequest& request) RPCExamples{ HelpExampleCli("protx", "revoke \"0123456701234567012345670123456701234567012345670123456701234567\" \"072f36a77261cdd5d64c32d97bac417540eddca1d5612f416feb07ff75a8e240\"") }, - }.Check(request); -} - -static UniValue protx_revoke(const JSONRPCRequest& request, CChainstateHelper& chain_helper, CDeterministicMNManager& dmnman, const ChainstateManager& chainman) + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - protx_revoke_help(request); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.dmnman); + CDeterministicMNManager& dmnman = *node.dmnman; + + CHECK_NONFATAL(node.chain_helper); + CChainstateHelper& chain_helper = *node.chain_helper; + std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); if (!wallet) return NullUniValue; @@ -1222,34 +1414,12 @@ static UniValue protx_revoke(const JSONRPCRequest& request, CChainstateHelper& c SetTxPayload(tx, ptx); return SignAndSendSpecialTx(request, chain_helper, chainman, tx); +}, + }; } #endif//ENABLE_WALLET -static void protx_list_help(const JSONRPCRequest& request) -{ - RPCHelpMan{"protx list", - "\nLists all ProTxs in your wallet or on-chain, depending on the given type.\n", - { - {"type", RPCArg::Type::STR, /* default */ "registered", - "\nAvailable types:\n" - " registered - List all ProTx which are registered at the given chain height.\n" - " This will also include ProTx which failed PoSe verification.\n" - " valid - List only ProTx which are active/valid at the given chain height.\n" - " evo - List only ProTx corresponding to EvoNodes at the given chain height.\n" -#ifdef ENABLE_WALLET - " wallet - List only ProTx which are found in your wallet at the given chain height.\n" - " This will also include ProTx which failed PoSe verification.\n" -#endif - }, - {"detailed", RPCArg::Type::BOOL, /* default */ "false", "If not specified, only the hashes of the ProTx will be returned."}, - {"height", RPCArg::Type::NUM, /* default */ "current chain-tip", ""}, - }, - RPCResults{}, - RPCExamples{""}, - }.Check(request); -} - #ifdef ENABLE_WALLET static bool CheckWalletOwnsScript(const CWallet* const pwallet, const CScript& script) { if (!pwallet) { @@ -1317,10 +1487,37 @@ static UniValue BuildDMNListEntry(const CWallet* const pwallet, const CDetermini return o; } -static UniValue protx_list(const JSONRPCRequest& request, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, - const ChainstateManager& chainman) +static RPCHelpMan protx_list() { - protx_list_help(request); + return RPCHelpMan{"protx list", + "\nLists all ProTxs in your wallet or on-chain, depending on the given type.\n", + { + {"type", RPCArg::Type::STR, /* default */ "registered", + "\nAvailable types:\n" + " registered - List all ProTx which are registered at the given chain height.\n" + " This will also include ProTx which failed PoSe verification.\n" + " valid - List only ProTx which are active/valid at the given chain height.\n" + " evo - List only ProTx corresponding to EvoNodes at the given chain height.\n" +#ifdef ENABLE_WALLET + " wallet - List only ProTx which are found in your wallet at the given chain height.\n" + " This will also include ProTx which failed PoSe verification.\n" +#endif + }, + {"detailed", RPCArg::Type::BOOL, /* default */ "false", "If not specified, only the hashes of the ProTx will be returned."}, + {"height", RPCArg::Type::NUM, /* default */ "current chain-tip", ""}, + }, + RPCResults{}, + RPCExamples{""}, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.dmnman); + CDeterministicMNManager& dmnman = *node.dmnman; + + CHECK_NONFATAL(node.mn_metaman); + CMasternodeMetaMan& mn_metaman = *node.mn_metaman; std::shared_ptr wallet{nullptr}; #ifdef ENABLE_WALLET @@ -1348,7 +1545,7 @@ static UniValue protx_list(const JSONRPCRequest& request, CDeterministicMNManage #ifdef ENABLE_WALLET if (request.params.size() > 4) { - protx_list_help(request); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Too many arguments"); } bool detailed = !request.params[1].isNull() ? ParseBoolV(request.params[1], "detailed") : false; @@ -1379,7 +1576,7 @@ static UniValue protx_list(const JSONRPCRequest& request, CDeterministicMNManage #endif } else if (type == "valid" || type == "registered" || type == "evo") { if (request.params.size() > 3) { - protx_list_help(request); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Too many arguments"); } bool detailed = !request.params[1].isNull() ? ParseBoolV(request.params[1], "detailed") : false; @@ -1406,11 +1603,13 @@ static UniValue protx_list(const JSONRPCRequest& request, CDeterministicMNManage } return ret; +}, + }; } -static void protx_info_help(const JSONRPCRequest& request) +static RPCHelpMan protx_info() { - RPCHelpMan{"protx info", + return RPCHelpMan{"protx info", "\nReturns detailed information about a deterministic masternode.\n", { GetRpcArg("proTxHash"), @@ -1425,13 +1624,16 @@ static void protx_info_help(const JSONRPCRequest& request) RPCExamples{ HelpExampleCli("protx", "info \"0123456701234567012345670123456701234567012345670123456701234567\"") }, - }.Check(request); -} - -static UniValue protx_info(const JSONRPCRequest& request, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, - const ChainstateManager& chainman) + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - protx_info_help(request); + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.dmnman); + CDeterministicMNManager& dmnman = *node.dmnman; + + CHECK_NONFATAL(node.mn_metaman); + CMasternodeMetaMan& mn_metaman = *node.mn_metaman; std::shared_ptr wallet{nullptr}; #ifdef ENABLE_WALLET @@ -1467,20 +1669,8 @@ static UniValue protx_info(const JSONRPCRequest& request, CDeterministicMNManage throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s not found", proTxHash.ToString())); } return BuildDMNListEntry(wallet.get(), *dmn, mn_metaman, true, chainman, pindex); -} - -static void protx_diff_help(const JSONRPCRequest& request) -{ - RPCHelpMan{"protx diff", - "\nCalculates a diff between two deterministic masternode lists. The result also contains proof data.\n", - { - {"baseBlock", RPCArg::Type::NUM, RPCArg::Optional::NO, "The starting block height."}, - {"block", RPCArg::Type::NUM, RPCArg::Optional::NO, "The ending block height."}, - {"extended", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Show additional fields."}, - }, - RPCResults{}, - RPCExamples{""}, - }.Check(request); +}, + }; } static uint256 ParseBlock(const UniValue& v, const ChainstateManager& chainman, const std::string& strName) EXCLUSIVE_LOCKS_REQUIRED(cs_main) @@ -1497,10 +1687,27 @@ static uint256 ParseBlock(const UniValue& v, const ChainstateManager& chainman, } } -static UniValue protx_diff(const JSONRPCRequest& request, CDeterministicMNManager& dmnman, const ChainstateManager& chainman, - const LLMQContext& llmq_ctx) +static RPCHelpMan protx_diff() { - protx_diff_help(request); + return RPCHelpMan{"protx diff", + "\nCalculates a diff between two deterministic masternode lists. The result also contains proof data.\n", + { + {"baseBlock", RPCArg::Type::NUM, RPCArg::Optional::NO, "The starting block height."}, + {"block", RPCArg::Type::NUM, RPCArg::Optional::NO, "The ending block height."}, + {"extended", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Show additional fields."}, + }, + RPCResults{}, + RPCExamples{""}, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.dmnman); + CDeterministicMNManager& dmnman = *node.dmnman; + + CHECK_NONFATAL(node.llmq_ctx); + const LLMQContext& llmq_ctx = *node.llmq_ctx; LOCK(cs_main); uint256 baseBlockHash = ParseBlock(request.params[0], chainman, "baseBlock"); @@ -1518,19 +1725,8 @@ static UniValue protx_diff(const JSONRPCRequest& request, CDeterministicMNManage } return mnListDiff.ToJson(extended); -} - -static void protx_listdiff_help(const JSONRPCRequest& request) -{ - RPCHelpMan{"protx listdiff", - "\nCalculate a full MN list diff between two masternode lists.\n", - { - {"baseBlock", RPCArg::Type::NUM, RPCArg::Optional::NO, "The starting block height."}, - {"block", RPCArg::Type::NUM, RPCArg::Optional::NO, "The ending block height."}, - }, - RPCResults{}, - RPCExamples{""}, - }.Check(request); +}, + }; } static const CBlockIndex* ParseBlockIndex(const UniValue& v, const ChainstateManager& chainman, const std::string& strName) EXCLUSIVE_LOCKS_REQUIRED(cs_main) @@ -1551,9 +1747,23 @@ static const CBlockIndex* ParseBlockIndex(const UniValue& v, const ChainstateMan } } -static UniValue protx_listdiff(const JSONRPCRequest& request, CDeterministicMNManager& dmnman, const ChainstateManager& chainman) +static RPCHelpMan protx_listdiff() { - protx_listdiff_help(request); + return RPCHelpMan{"protx listdiff", + "\nCalculate a full MN list diff between two masternode lists.\n", + { + {"baseBlock", RPCArg::Type::NUM, RPCArg::Optional::NO, "The starting block height."}, + {"block", RPCArg::Type::NUM, RPCArg::Optional::NO, "The ending block height."}, + }, + RPCResults{}, + RPCExamples{""}, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const NodeContext& node = EnsureAnyNodeContext(request.context); + const ChainstateManager& chainman = EnsureChainman(node); + + CHECK_NONFATAL(node.dmnman); + CDeterministicMNManager& dmnman = *node.dmnman; LOCK(cs_main); UniValue ret(UniValue::VOBJ); @@ -1600,11 +1810,13 @@ static UniValue protx_listdiff(const JSONRPCRequest& request, CDeterministicMNMa ret.pushKV("updatedMNs", jupdatedMNs); return ret; +}, + }; } -[[ noreturn ]] static void protx_help() +static RPCHelpMan protx_help() { - RPCHelpMan{ + return RPCHelpMan{ "protx", "Set of commands to execute ProTx related actions.\n" "To get help on individual commands, use \"help protx command\".\n" @@ -1637,60 +1849,11 @@ static UniValue protx_listdiff(const JSONRPCRequest& request, CDeterministicMNMa }, RPCResults{}, RPCExamples{""}, - }.Throw(); -} - -static UniValue protx(const JSONRPCRequest& request) + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - const JSONRPCRequest new_request{request.strMethod == "protx" ? request.squashed() : request}; - const std::string command{new_request.strMethod}; - - const NodeContext& node = EnsureAnyNodeContext(request.context); - const ChainstateManager& chainman = EnsureChainman(node); - - CHECK_NONFATAL(node.dmnman); - CDeterministicMNManager& dmnman = *node.dmnman; - - CHECK_NONFATAL(node.mn_metaman); - CMasternodeMetaMan& mn_metaman = *node.mn_metaman; - -#ifdef ENABLE_WALLET - CHECK_NONFATAL(node.chain_helper); - CChainstateHelper& chain_helper = *node.chain_helper; - - if (command == "protxregister" || command == "protxregister_fund" || command == "protxregister_prepare") { - return protx_register(new_request, chain_helper, chainman); - } else if (command == "protxregister_evo" || command == "protxregister_fund_evo" || command == "protxregister_prepare_evo" || command == "protxregister_hpmn" || command == "protxregister_fund_hpmn" || command == "protxregister_prepare_hpmn") { - return protx_register_evo(new_request, chain_helper, chainman); - } else if (command == "protxregister_legacy" || command == "protxregister_fund_legacy" || command == "protxregister_prepare_legacy") { - return protx_register_legacy(new_request, chain_helper, chainman); - } else if (command == "protxregister_submit") { - return protx_register_submit(new_request, chain_helper, chainman); - } else if (command == "protxupdate_service") { - return protx_update_service_common_wrapper(new_request, chain_helper, dmnman, chainman, MnType::Regular); - } else if (command == "protxupdate_service_evo" || command == "protxupdate_service_hpmn") { - return protx_update_service_common_wrapper(new_request, chain_helper, dmnman, chainman, MnType::Evo); - } else if (command == "protxupdate_registrar") { - return protx_update_registrar(new_request, chain_helper, dmnman, chainman); - } else if (command == "protxupdate_registrar_legacy") { - return protx_update_registrar_legacy(new_request, chain_helper, dmnman, chainman); - } else if (command == "protxrevoke") { - return protx_revoke(new_request, chain_helper, dmnman, chainman); - } else -#endif - if (command == "protxlist") { - return protx_list(new_request, dmnman, mn_metaman, chainman); - } else if (command == "protxinfo") { - return protx_info(new_request, dmnman, mn_metaman, chainman); - } else if (command == "protxdiff") { - CHECK_NONFATAL(node.llmq_ctx); - return protx_diff(new_request, dmnman, chainman, *node.llmq_ctx); - } else if (command == "protxlistdiff") { - return protx_listdiff(new_request, dmnman, chainman); - } - else { - protx_help(); - } + throw JSONRPCError(RPC_INVALID_PARAMETER, "Must be a valid command"); +}, + }; } static RPCHelpMan bls_generate() @@ -1795,10 +1958,35 @@ void RegisterEvoRPCCommands(CRPCTable &tableRPC) static const CRPCCommand commands[] = { // category name actor (function) // --------------------- ------------------------ ----------------------- - { "evo", "bls", &bls_help, {"command"} }, - { "evo", "bls", "generate", &bls_generate, {"legacy"} }, - { "evo", "bls", "fromsecret", &bls_fromsecret, {"secret", "legacy"} }, - { "evo", "protx", &protx, {} }, + { "evo", "bls", &bls_help, {"command"} }, + { "evo", "bls", "generate", &bls_generate, {"legacy"} }, + { "evo", "bls", "fromsecret", &bls_fromsecret, {"secret", "legacy"} }, + { "evo", "protx", &protx_help, {"command"} }, +#ifdef ENABLE_WALLET + { "evo", "protx", "register", &protx_register, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "feeSourceAddress", "submit"} }, + { "evo", "protx", "register_evo", &protx_register_evo, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "feeSourceAddress", "submit"} }, + { "evo", "protx", "register_hpmn", &protx_register_hpmn, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "feeSourceAddress", "submit"} }, + { "evo", "protx", "register_legacy", &protx_register_legacy, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "feeSourceAddress", "submit"} }, + { "evo", "protx", "register_fund", &protx_register_fund, {"collateralAddress", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "fundAddress", "submit"} }, + { "evo", "protx", "register_fund_legacy", &protx_register_fund_legacy, {"collateralAddress", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "fundAddress", "submit"} }, + { "evo", "protx", "register_fund_evo", &protx_register_fund_evo, {"collateralAddress", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "fundAddress", "submit"} }, + { "evo", "protx", "register_fund_hpmn", &protx_register_fund_hpmn, {"collateralAddress", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "fundAddress", "submit"} }, + { "evo", "protx", "register_prepare", &protx_register_prepare, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "feeSourceAddress"} }, + { "evo", "protx", "register_prepare_evo", &protx_register_prepare_evo, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "feeSourceAddress"} }, + { "evo", "protx", "register_prepare_hpmn", &protx_register_prepare_hpmn, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "feeSourceAddress"} }, + { "evo", "protx", "register_prepare_legacy", &protx_register_prepare_legacy, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "feeSourceAddress"} }, + { "evo", "protx", "update_service", &protx_update_service, {"proTxHash", "ipAndPort", "operatorKey", "operatorPayoutAddress", "feeSourceAddress"} }, + { "evo", "protx", "update_service_evo", &protx_update_service_evo, {"proTxHash", "ipAndPort", "operatorKey", "platformNodeID", "platformP2PPort", "platformHTTPPort", "operatorPayoutAddress", "feeSourceAddress"} }, + { "evo", "protx", "update_service_hpmn", &protx_update_service_hpmn, {"proTxHash", "ipAndPort", "operatorKey", "platformNodeID", "platformP2PPort", "platformHTTPPort", "operatorPayoutAddress", "feeSourceAddress"} }, + { "evo", "protx", "register_submit", &protx_register_submit, {"tx", "sig"} }, + { "evo", "protx", "update_registrar", &protx_update_registrar, {"proTxHash", "operatorPubKey", "votingAddress", "payoutAddress", "feeSourceAddress"} }, + { "evo", "protx", "update_registrar_legacy", &protx_update_registrar_legacy, {"proTxHash", "operatorPubKey", "votingAddress", "payoutAddress", "feeSourceAddress"} }, + { "evo", "protx", "revoke", &protx_revoke, {"proTxHash", "operatorKey", "reason", "feeSourceAddress"} }, +#endif + { "evo", "protx", "list", &protx_list, {"type", "detailed", "height"} }, + { "evo", "protx", "info", &protx_info, {"proTxHash", "blockHash"} }, + { "evo", "protx", "diff", &protx_diff, {"baseBlock", "block", "extended"} }, + { "evo", "protx", "listdiff", &protx_listdiff, {"baseBlock", "block"} }, }; // clang-format on for (const auto& command : commands) {