diff --git a/src/masternodes/mn_rpc.cpp b/src/masternodes/mn_rpc.cpp index 2a5f43fd0a7..54430cd4abc 100644 --- a/src/masternodes/mn_rpc.cpp +++ b/src/masternodes/mn_rpc.cpp @@ -125,10 +125,12 @@ UniValue createmasternode(const JSONRPCRequest& request) CWallet* const pwallet = GetWallet(request); RPCHelpMan{"createmasternode", - "\nCreates (and submits to local node and network) a masternode creation transaction with given metadata, spending the given inputs..\n" - "The first optional argument (may be empty array) is an array of specific UTXOs to spend." + + "\nCreates (and submits to local node and network) a masternode creation transaction with given owner and operator addresses, spending the given inputs..\n" + "The last optional argument (may be empty array) is an array of specific UTXOs to spend." + HelpRequiringPassphrase(pwallet) + "\n", { + {"ownerAddress", RPCArg::Type::STR, RPCArg::Optional::NO, "Any valid address for keeping collateral amount (any P2PKH or P2WKH address) - used as owner key"}, + {"operatorAddress", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Optional (== ownerAddress) masternode operator auth address (P2PKH only, unique)"}, {"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of json objects", { {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", @@ -139,25 +141,13 @@ UniValue createmasternode(const JSONRPCRequest& request) }, }, }, - {"metadata", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", - { - {"operatorAuthAddress", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Masternode operator auth address (P2PKH only, unique)" }, - {"collateralAddress", RPCArg::Type::STR, RPCArg::Optional::NO, "Any valid address for keeping collateral amount (any P2PKH or P2WKH address) - used as owner key"}, - }, - }, }, RPCResult{ "\"hex\" (string) The hex-encoded raw transaction with signature(s)\n" }, RPCExamples{ - HelpExampleCli("createmasternode", "\"[{\\\"txid\\\":\\\"id\\\",\\\"vout\\\":0}]\" " - "\"{\\\"operatorAuthAddress\\\":\\\"address\\\"," - "\\\"collateralAddress\\\":\\\"address\\\"" - "}\"") - + HelpExampleRpc("createmasternode", "\"[{\\\"txid\\\":\\\"id\\\",\\\"vout\\\":0}]\" " - "\"{\\\"operatorAuthAddress\\\":\\\"address\\\"," - "\\\"collateralAddress\\\":\\\"address\\\"" - "}\"") + HelpExampleCli("createmasternode", "ownerAddress operatorAddress \"[{\\\"txid\\\":\\\"id\\\",\\\"vout\\\":0}]\"") + + HelpExampleRpc("createmasternode", "ownerAddress operatorAddress \"[{\\\"txid\\\":\\\"id\\\",\\\"vout\\\":0}]\"") }, }.Check(request); @@ -166,33 +156,25 @@ UniValue createmasternode(const JSONRPCRequest& request) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot create Masternode while still in Initial Block Download"); } - RPCTypeCheck(request.params, { UniValue::VARR, UniValue::VOBJ }, true); - if (request.params[0].isNull() || request.params[1].isNull()) + RPCTypeCheck(request.params, { UniValue::VSTR, UniValue::VSTR, UniValue::VARR }, true); + if (request.params[0].isNull()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters, arguments 1 and 2 must be non-null, and argument 2 expected as object with " - "{\"operatorAuthAddress\",\"collateralAddress\"}"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters, at least argument 1 must be non-null"); } - UniValue metaObj = request.params[1].get_obj(); - RPCTypeCheckObj(metaObj, { - { "operatorAuthAddress", UniValue::VSTR }, - { "collateralAddress", UniValue::VSTR } - }, - true, true); - std::string collateralAddress = metaObj["collateralAddress"].getValStr(); - std::string operatorAuthAddressBase58 = metaObj["operatorAuthAddress"].getValStr(); + std::string ownerAddress = request.params[0].getValStr(); - CTxDestination collateralDest = DecodeDestination(collateralAddress); - if (collateralDest.which() != 1 && collateralDest.which() != 4) + CTxDestination ownerDest = DecodeDestination(ownerAddress); + if (ownerDest.which() != 1 && ownerDest.which() != 4) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "collateralAddress (" + collateralAddress + ") does not refer to a P2PKH or P2WPKH address"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "ownerAddress (" + ownerAddress + ") does not refer to a P2PKH or P2WPKH address"); } - CKeyID ownerAuthKey = collateralDest.which() == 1 ? CKeyID(*boost::get(&collateralDest)) : CKeyID(*boost::get(&collateralDest)); + CKeyID ownerAuthKey = ownerDest.which() == 1 ? CKeyID(*boost::get(&ownerDest)) : CKeyID(*boost::get(&ownerDest)); - CTxDestination operatorDest = operatorAuthAddressBase58 == "" ? collateralDest : DecodeDestination(operatorAuthAddressBase58); + CTxDestination operatorDest = request.params.size() > 1 ? DecodeDestination(request.params[1].getValStr()) : ownerDest; if (operatorDest.which() != 1 && operatorDest.which() != 4) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorAuthAddress (" + operatorAuthAddressBase58 + ") does not refer to a P2PKH or P2WPKH address"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorAddress (" + request.params[1].getValStr() + ") does not refer to a P2PKH or P2WPKH address"); } CKeyID operatorAuthKey = operatorDest.which() == 1 ? CKeyID(*boost::get(&operatorDest)) : CKeyID(*boost::get(&operatorDest)) ; @@ -202,12 +184,12 @@ UniValue createmasternode(const JSONRPCRequest& request) if (pmasternodesview->ExistMasternode(CMasternodesView::AuthIndex::ByOwner, ownerAuthKey) || pmasternodesview->ExistMasternode(CMasternodesView::AuthIndex::ByOperator, ownerAuthKey)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Masternode with collateralAddress == " + collateralAddress + " already exists"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Masternode with ownerAddress == " + ownerAddress + " already exists"); } if (pmasternodesview->ExistMasternode(CMasternodesView::AuthIndex::ByOwner, operatorAuthKey) || pmasternodesview->ExistMasternode(CMasternodesView::AuthIndex::ByOperator, operatorAuthKey)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Masternode with operatorAuthAddress == " + EncodeDestination(operatorDest) + " already exists"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Masternode with operatorAddress == " + EncodeDestination(operatorDest) + " already exists"); } } @@ -220,10 +202,13 @@ UniValue createmasternode(const JSONRPCRequest& request) CMutableTransaction rawTx; - FillInputs(request.params[0].get_array(), rawTx); + if (request.params.size() > 2) + { + FillInputs(request.params[2].get_array(), rawTx); + } rawTx.vout.push_back(CTxOut(EstimateMnCreationFee(), scriptMeta)); - rawTx.vout.push_back(CTxOut(GetMnCollateralAmount(), GetScriptForDestination(collateralDest))); + rawTx.vout.push_back(CTxOut(GetMnCollateralAmount(), GetScriptForDestination(ownerDest))); return fundsignsend(rawTx, request, pwallet); } @@ -235,9 +220,10 @@ UniValue resignmasternode(const JSONRPCRequest& request) RPCHelpMan{"resignmasternode", "\nCreates (and submits to local node and network) a transaction resigning your masternode. Collateral will be unlocked after " + std::to_string(GetMnResignDelay()) + " blocks.\n" - "The first optional argument (may be empty array) is an array of specific UTXOs to spend. One of UTXO's must belong to the MN's owner (collateral) address" + + "The last optional argument (may be empty array) is an array of specific UTXOs to spend. One of UTXO's must belong to the MN's owner (collateral) address" + HelpRequiringPassphrase(pwallet) + "\n", { + {"mn_id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The Masternode's ID"}, {"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of json objects. Provide it if you want to spent specific UTXOs", { {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", @@ -248,14 +234,13 @@ UniValue resignmasternode(const JSONRPCRequest& request) }, }, }, - {"mn_id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The Masternode's ID"}, }, RPCResult{ "\"hex\" (string) The hex-encoded raw transaction with signature(s)\n" }, RPCExamples{ - HelpExampleCli("resignmasternode", "\"[{\\\"txid\\\":\\\"id\\\",\\\"vout\\\":0}]\" \"mn_id\"") - + HelpExampleRpc("resignmasternode", "\"[{\\\"txid\\\":\\\"id\\\",\\\"vout\\\":0}]\" \"mn_id\"") + HelpExampleCli("resignmasternode", "mn_id \"[{\\\"txid\\\":\\\"id\\\",\\\"vout\\\":0}]\"") + + HelpExampleRpc("resignmasternode", "mn_id \"[{\\\"txid\\\":\\\"id\\\",\\\"vout\\\":0}]\"") }, }.Check(request); @@ -263,19 +248,25 @@ UniValue resignmasternode(const JSONRPCRequest& request) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot resign Masternode while still in Initial Block Download"); } - RPCTypeCheck(request.params, { UniValue::VARR, UniValue::VSTR }, true); + RPCTypeCheck(request.params, { UniValue::VSTR, UniValue::VARR }, true); - std::string const nodeIdStr = request.params[1].getValStr(); + std::string const nodeIdStr = request.params[0].getValStr(); uint256 nodeId = uint256S(nodeIdStr); CTxDestination ownerDest; { auto locked_chain = pwallet->chain().lock(); - auto optIDs = pmasternodesview->AmIOwner(); - if (!optIDs) + auto nodePtr = pmasternodesview->ExistMasternode(nodeId); + if (!nodePtr) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("You are not the owner of masternode %s, or it does not exist", nodeIdStr)); + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("The masternode %s does not exist", nodeIdStr)); } - auto nodePtr = pmasternodesview->ExistMasternode(nodeId); + + ownerDest = nodePtr->ownerType == 1 ? CTxDestination(PKHash(nodePtr->ownerAuthAddress)) : CTxDestination(WitnessV0KeyHash(nodePtr->ownerAuthAddress)); + if (!IsMine(*pwallet, ownerDest)) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("You are not the owner of masternode %s", nodeIdStr)); + } + if (nodePtr->banHeight != -1) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Masternode %s was criminal, banned at height %i by tx %s", nodeIdStr, nodePtr->banHeight, nodePtr->banTx.GetHex())); @@ -285,15 +276,13 @@ UniValue resignmasternode(const JSONRPCRequest& request) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Masternode %s was resigned by tx %s; collateral can be spend at block #%d", nodeIdStr, nodePtr->resignTx.GetHex(), nodePtr->resignHeight + GetMnResignDelay())); } - ownerDest = nodePtr->ownerType == 1 ? CTxDestination(PKHash(nodePtr->ownerAuthAddress)) : CTxDestination(WitnessV0KeyHash(nodePtr->ownerAuthAddress)); } CMutableTransaction rawTx; - UniValue inputs = request.params[0].get_array(); - if (inputs.size() > 0) + if (request.params.size() > 1) { - FillInputs(request.params[0].get_array(), rawTx); + FillInputs(request.params[1].get_array(), rawTx); } else { @@ -309,7 +298,7 @@ UniValue resignmasternode(const JSONRPCRequest& request) if (vecOutputs.size() == 0) { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strprintf("Can't find any UTXO's for ownerAuthAddress (%s). Send some coins and try again!", EncodeDestination(ownerDest))); + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strprintf("Can't find any UTXO's for ownerAddress (%s). Send some coins and try again!", EncodeDestination(ownerDest))); } rawTx.vin.push_back(CTxIn(vecOutputs[0].tx->GetHash(), vecOutputs[0].i)); } @@ -448,8 +437,8 @@ UniValue listcriminalproofs(const JSONRPCRequest& request) static const CRPCCommand commands[] = { // category name actor (function) params // ----------------- ------------------------ ----------------------- ---------- - { "masternodes", "createmasternode", &createmasternode, { "inputs", "metadata" } }, - { "masternodes", "resignmasternode", &resignmasternode, { "inputs", "mn_id" } }, + { "masternodes", "createmasternode", &createmasternode, { "ownerAddress", "operatorAddress", "inputs" } }, + { "masternodes", "resignmasternode", &resignmasternode, { "mn_id", "inputs" } }, { "masternodes", "listmasternodes", &listmasternodes, { "list", "verbose" } }, { "masternodes", "listcriminalproofs", &listcriminalproofs, { } }, }; diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index b2f2e58bdd0..358e62daf3e 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -169,10 +169,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "createwallet", 4, "avoid_reuse"}, { "getnodeaddresses", 0, "count"}, { "stop", 0, "wait" }, - { "createmasternode", 0, "inputs" }, - { "createmasternode", 1, "metadata" }, - { "resignmasternode", 0, "inputs" }, - { "resignmasternode", 1, "mn_id" }, + { "createmasternode", 2, "inputs" }, + { "resignmasternode", 1, "inputs" }, { "listmasternodes", 0, "list" }, { "listmasternodes", 1, "verbose" }, diff --git a/test/functional/rpc_mn_basic.py b/test/functional/rpc_mn_basic.py index d2a3f52c500..a7e04d3a991 100755 --- a/test/functional/rpc_mn_basic.py +++ b/test/functional/rpc_mn_basic.py @@ -34,20 +34,18 @@ def run_test(self): # Fail to create: Insufficient funds (not matured coins) try: - idnode0 = self.nodes[0].createmasternode([], { - # "operatorAuthAddress": operator0, - "collateralAddress": collateral0 - }) + idnode0 = self.nodes[0].createmasternode( + collateral0 + ) except JSONRPCException as e: errorString = e.error['message'] assert("Insufficient funds" in errorString) # Create node0 self.nodes[0].generate(1) - idnode0 = self.nodes[0].createmasternode([], { - # "operatorAuthAddress": operator0, - "collateralAddress": collateral0 - }) + idnode0 = self.nodes[0].createmasternode( + collateral0 + ) # Create and sign (only) collateral spending tx spendTx = self.nodes[0].createrawtransaction([{'txid':idnode0, 'vout':1}],[{collateral0:9.999}]) @@ -81,18 +79,9 @@ def run_test(self): # RESIGNING: #======================== - # Fail to resign: Forget to place params in config + # Fail to resign: Have no money on ownerauth address try: - self.nodes[0].resignmasternode([], idnode0) - except JSONRPCException as e: - errorString = e.error['message'] - assert("You are not the owner" in errorString) - - # Restart with new params, but have no money on ownerauth address - self.restart_node(0, extra_args=['-masternode_owner='+collateral0]) - self.nodes[0].generate(1) # to broke "initial block downloading" - try: - self.nodes[0].resignmasternode([], idnode0) + self.nodes[0].resignmasternode(idnode0) except JSONRPCException as e: errorString = e.error['message'] assert("Can't find any UTXO's" in errorString) @@ -100,7 +89,7 @@ def run_test(self): # Funding auth address and successful resign fundingTx = self.nodes[0].sendtoaddress(collateral0, 1) self.nodes[0].generate(1) - resignTx = self.nodes[0].resignmasternode([], idnode0) + resignTx = self.nodes[0].resignmasternode(idnode0) self.nodes[0].generate(1) assert_equal(self.nodes[0].listmasternodes()[idnode0]['state'], "PRE_RESIGNED") self.nodes[0].generate(10)