From efaa704f20237251b19c8d57077a41c0ae737c75 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Tue, 25 Dec 2018 10:22:23 +0300 Subject: [PATCH 01/11] Add an option to use specific address as a source of funds in protx rpc commands --- src/rpc/rpcevo.cpp | 117 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 93 insertions(+), 24 deletions(-) diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index e7d17f22e3aa..c2b1c98fd247 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -12,6 +12,7 @@ #include "validation.h" #ifdef ENABLE_WALLET +#include "wallet/coincontrol.h" #include "wallet/wallet.h" #endif//ENABLE_WALLET @@ -89,32 +90,64 @@ static CBLSSecretKey ParseBLSSecretKey(const std::string& hexKey, const std::str #ifdef ENABLE_WALLET template -static void FundSpecialTx(CMutableTransaction& tx, SpecialTxPayload payload) +static void FundSpecialTx(CMutableTransaction& tx, const SpecialTxPayload& payload, const CTxDestination& fundDest) { + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + CDataStream ds(SER_NETWORK, PROTOCOL_VERSION); ds << payload; tx.vExtraPayload.assign(ds.begin(), ds.end()); static CTxOut dummyTxOut(0, CScript() << OP_RETURN); + std::vector vecSend; bool dummyTxOutAdded = false; + if (tx.vout.empty()) { - // add dummy txout as FundTransaction requires at least one output + // add dummy txout as CreateTransaction requires at least one recipient tx.vout.emplace_back(dummyTxOut); dummyTxOutAdded = true; } + for (const auto& txOut : tx.vout) { + CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false}; + vecSend.push_back(recipient); + } + + CCoinControl coinControl; + CTxDestination nodest = CNoDestination(); + if (fundDest != nodest) { + coinControl.destChange = fundDest; + + std::vector vecOutputs; + pwalletMain->AvailableCoins(vecOutputs); + + for (const auto& out : vecOutputs) { + CTxDestination txDest; + if (ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, txDest) && txDest == fundDest) { + coinControl.Select(COutPoint(out.tx->tx->GetHash(), out.i)); + } + } + } else { + coinControl.fAllowOtherInputs = true; + } + + CWalletTx wtx; + CReserveKey reservekey(pwalletMain); CAmount nFee; - CFeeRate feeRate = CFeeRate(0); int nChangePos = -1; std::string strFailReason; - std::set setSubtractFeeFromOutputs; - if (!pwalletMain->FundTransaction(tx, nFee, false, feeRate, nChangePos, strFailReason, false, false, setSubtractFeeFromOutputs, true, CNoDestination())) { + + if (!pwalletMain->CreateTransaction(vecSend, wtx, reservekey, nFee, nChangePos, strFailReason, &coinControl, false, ALL_COINS, false, tx.vExtraPayload.size())) { throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); } + tx.vin = wtx.tx->vin; + tx.vout = wtx.tx->vout; + if (dummyTxOutAdded && tx.vout.size() > 1) { - // FundTransaction added a change output, so we don't need the dummy txout anymore - // Removing it results in slight overpayment of fees, but we ignore this for now (as it's a very low amount) + // CreateTransaction added a change output, so we don't need the dummy txout anymore. + // Removing it results in slight overpayment of fees, but we ignore this for now (as it's a very low amount). auto it = std::find(tx.vout.begin(), tx.vout.end(), dummyTxOut); assert(it != tx.vout.end()); tx.vout.erase(it); @@ -186,7 +219,7 @@ static std::string SignAndSendSpecialTx(const CMutableTransaction& tx) void protx_register_fund_help() { throw std::runtime_error( - "protx register_fund \"collateralAddress\" \"ipAndPort\" \"ownerKeyAddr\" \"operatorPubKey\" \"votingKeyAddr\" operatorReward \"payoutAddress\"\n" + "protx register_fund \"collateralAddress\" \"ipAndPort\" \"ownerKeyAddr\" \"operatorPubKey\" \"votingKeyAddr\" operatorReward \"payoutAddress\" ( \"fundAddress\" )\n" "\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" @@ -207,7 +240,9 @@ void protx_register_fund_help() " If set to \"0\" or an empty string, ownerAddr will be used.\n" "6. \"operatorReward\" (numeric, required) The fraction in % to share with the operator. The value must be\n" " between 0.00 and 100.00.\n" - "7. \"payoutAddress\" (string, required) The dash address to use for masternode reward payments." + "7. \"payoutAddress\" (string, required) The dash address to use for masternode reward payments.\n" + "8. \"fundAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" + " The private key belonging to this address be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "register_fund \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") ); @@ -216,7 +251,7 @@ void protx_register_fund_help() void protx_register_help() { throw std::runtime_error( - "protx register \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerKeyAddr\" \"operatorPubKey\" \"votingKeyAddr\" operatorReward \"payoutAddress\"\n" + "protx register \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerKeyAddr\" \"operatorPubKey\" \"votingKeyAddr\" operatorReward \"payoutAddress\" ( \"fundAddress\" )\n" "\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. It must also not be used by any other masternode.\n" @@ -232,7 +267,7 @@ void protx_register_help() void protx_register_prepare_help() { throw std::runtime_error( - "protx register_prepare \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerKeyAddr\" \"operatorPubKey\" \"votingKeyAddr\" operatorReward \"payoutAddress\"\n" + "protx register_prepare \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerKeyAddr\" \"operatorPubKey\" \"votingKeyAddr\" operatorReward \"payoutAddress\" ( \"fundAddress\" )\n" "\nCreates an unsigned ProTx and returns it. The ProTx must be signed externally with the collateral\n" "key and then passed to \"protx register_submit\". The prepared transaction will also contain inputs\n" "and outputs to cover fees.\n" @@ -271,11 +306,11 @@ UniValue protx_register(const JSONRPCRequest& request) bool isFundRegister = request.params[0].get_str() == "register_fund"; bool isPrepareRegister = request.params[0].get_str() == "register_prepare"; - if (isFundRegister && (request.fHelp || request.params.size() != 8)) { + if (isFundRegister && (request.fHelp || (request.params.size() != 8 && request.params.size() != 9))) { protx_register_fund_help(); - } else if (isExternalRegister && (request.fHelp || request.params.size() != 9)) { + } else if (isExternalRegister && (request.fHelp || (request.params.size() != 9 && request.params.size() != 10))) { protx_register_help(); - } else if (isPrepareRegister && (request.fHelp || request.params.size() != 9)) { + } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 9 && request.params.size() != 10))) { protx_register_prepare_help(); } @@ -353,7 +388,14 @@ UniValue protx_register(const JSONRPCRequest& request) ptx.vchSig.resize(65); } - FundSpecialTx(tx, ptx); + CBitcoinAddress fundAddress; + if (request.params.size() > paramIdx + 6) { + fundAddress = CBitcoinAddress(request.params[paramIdx + 6].get_str()); + if (!fundAddress.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[paramIdx + 6].get_str()); + } + + FundSpecialTx(tx, ptx, fundAddress.Get()); UpdateSpecialTxInputsHash(tx, ptx); if (isFundRegister) { @@ -435,7 +477,7 @@ UniValue protx_register_submit(const JSONRPCRequest& request) void protx_update_service_help() { throw std::runtime_error( - "protx update_service \"proTxHash\" \"ipAndPort\" \"operatorKey\" (\"operatorPayoutAddress\")\n" + "protx update_service \"proTxHash\" \"ipAndPort\" \"operatorKey\" (\"operatorPayoutAddress\" \"fundAddress\" )\n" "\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" @@ -447,6 +489,8 @@ void protx_update_service_help() " registered operator public key.\n" "4. \"operatorPayoutAddress\" (string, optional) The address used for operator reward payments.\n" " Only allowed when the ProRegTx had a non-zero operatorReward value.\n" + "5. \"fundAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" + " The private key belonging to this address be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "update_service \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" 5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd") ); @@ -454,7 +498,7 @@ void protx_update_service_help() UniValue protx_update_service(const JSONRPCRequest& request) { - if (request.fHelp || (request.params.size() != 4 && request.params.size() != 5)) + if (request.fHelp || (request.params.size() < 4 || request.params.size() > 6)) protx_update_service_help(); CProUpServTx ptx; @@ -488,7 +532,14 @@ UniValue protx_update_service(const JSONRPCRequest& request) tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_UPDATE_SERVICE; - FundSpecialTx(tx, ptx); + CBitcoinAddress fundAddress; + if (request.params.size() > 5) { + fundAddress = CBitcoinAddress(request.params[5].get_str()); + if (!fundAddress.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[5].get_str()); + } + + FundSpecialTx(tx, ptx, fundAddress.Get()); SignSpecialTxPayloadByHash(tx, ptx, keyOperator); SetTxPayload(tx, ptx); @@ -498,7 +549,7 @@ UniValue protx_update_service(const JSONRPCRequest& request) void protx_update_registrar_help() { throw std::runtime_error( - "protx update_registrar \"proTxHash\" \"operatorKeyAddr\" \"votingKeyAddr\" \"payoutAddress\"\n" + "protx update_registrar \"proTxHash\" \"operatorKeyAddr\" \"votingKeyAddr\" \"payoutAddress\" ( \"fundAddress\" )\n" "\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" @@ -512,6 +563,8 @@ void protx_update_registrar_help() " If set to \"0\" or an empty string, the last on-chain voting key of the masternode will be used.\n" "4. \"payoutAddress\" (string, required) The dash address to use for masternode reward payments\n" " If set to \"0\" or an empty string, the last on-chain payout address of the masternode will be used.\n" + "5. \"fundAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" + " The private key belonging to this address be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "update_registrar \"0123456701234567012345670123456701234567012345670123456701234567\" \"982eb34b7c7f614f29e5c665bc3605f1beeef85e3395ca12d3be49d2868ecfea5566f11cedfad30c51b2403f2ad95b67\" \"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwG\"") ); @@ -519,7 +572,7 @@ void protx_update_registrar_help() UniValue protx_update_registrar(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() != 5) { + if (request.fHelp || (request.params.size() != 5 && request.params.size() != 6)) { protx_update_registrar_help(); } @@ -560,7 +613,14 @@ UniValue protx_update_registrar(const JSONRPCRequest& request) // make sure we get anough fees added ptx.vchSig.resize(65); - FundSpecialTx(tx, ptx); + CBitcoinAddress fundAddress; + if (request.params.size() > 5) { + fundAddress = CBitcoinAddress(request.params[5].get_str()); + if (!fundAddress.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[5].get_str()); + } + + FundSpecialTx(tx, ptx, fundAddress.Get()); SignSpecialTxPayloadByHash(tx, ptx, keyOwner); SetTxPayload(tx, ptx); @@ -570,7 +630,7 @@ UniValue protx_update_registrar(const JSONRPCRequest& request) void protx_revoke_help() { throw std::runtime_error( - "protx revoke \"proTxHash\" \"operatorKey\" ( reason )\n" + "protx revoke \"proTxHash\" \"operatorKey\" ( reason \"fundAddress\")\n" "\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" @@ -580,6 +640,8 @@ void protx_revoke_help() "2. \"operatorKey\" (string, required) The operator private key belonging to the\n" " registered operator public key.\n" "3. reason (numeric, optional) The reason for revocation.\n" + "4. \"fundAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" + " The private key belonging to this address be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "revoke \"0123456701234567012345670123456701234567012345670123456701234567\" \"072f36a77261cdd5d64c32d97bac417540eddca1d5612f416feb07ff75a8e240\"") ); @@ -587,7 +649,7 @@ void protx_revoke_help() UniValue protx_revoke(const JSONRPCRequest& request) { - if (request.fHelp || (request.params.size() != 3 && request.params.size() != 4)) { + if (request.fHelp || (request.params.size() < 3 || request.params.size() > 5)) { protx_revoke_help(); } @@ -618,7 +680,14 @@ UniValue protx_revoke(const JSONRPCRequest& request) tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_UPDATE_REVOKE; - FundSpecialTx(tx, ptx); + CBitcoinAddress fundAddress; + if (request.params.size() > 4) { + fundAddress = CBitcoinAddress(request.params[4].get_str()); + if (!fundAddress.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[4].get_str()); + } + + FundSpecialTx(tx, ptx, fundAddress.Get()); SignSpecialTxPayloadByHash(tx, ptx, keyOperator); SetTxPayload(tx, ptx); From abbb7d547420699f98c6f229528d5d2cbd559ede Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 27 Dec 2018 12:07:49 +0300 Subject: [PATCH 02/11] fix typo "must" --- src/rpc/rpcevo.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index c2b1c98fd247..57414c5d9842 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -231,7 +231,7 @@ void protx_register_fund_help() "2. \"ipAndPort\" (string, required) IP and port in the form \"IP:PORT\".\n" " Must be unique on the network. Can be set to 0, which will require a ProUpServTx afterwards.\n" "3. \"ownerKeyAddr\" (string, required) The owner key used for payee updates and proposal voting.\n" - " The private key belonging to this address be known in your wallet. The address must\n" + " The private key belonging to this address must be known in your wallet. The address must\n" " be unused and must differ from the collateralAddress\n" "4. \"operatorPubKey\" (string, required) The operator BLS public key. The private key does not have to be known.\n" " It has to match the private key which is later used when operating the masternode.\n" @@ -242,7 +242,7 @@ void protx_register_fund_help() " between 0.00 and 100.00.\n" "7. \"payoutAddress\" (string, required) The dash address to use for masternode reward payments.\n" "8. \"fundAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" - " The private key belonging to this address be known in your wallet.\n" + " The private key belonging to this address must be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "register_fund \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") ); @@ -490,7 +490,7 @@ void protx_update_service_help() "4. \"operatorPayoutAddress\" (string, optional) The address used for operator reward payments.\n" " Only allowed when the ProRegTx had a non-zero operatorReward value.\n" "5. \"fundAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" - " The private key belonging to this address be known in your wallet.\n" + " The private key belonging to this address must be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "update_service \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" 5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd") ); @@ -564,7 +564,7 @@ void protx_update_registrar_help() "4. \"payoutAddress\" (string, required) The dash address to use for masternode reward payments\n" " If set to \"0\" or an empty string, the last on-chain payout address of the masternode will be used.\n" "5. \"fundAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" - " The private key belonging to this address be known in your wallet.\n" + " The private key belonging to this address must be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "update_registrar \"0123456701234567012345670123456701234567012345670123456701234567\" \"982eb34b7c7f614f29e5c665bc3605f1beeef85e3395ca12d3be49d2868ecfea5566f11cedfad30c51b2403f2ad95b67\" \"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwG\"") ); @@ -641,7 +641,7 @@ void protx_revoke_help() " registered operator public key.\n" "3. reason (numeric, optional) The reason for revocation.\n" "4. \"fundAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" - " The private key belonging to this address be known in your wallet.\n" + " The private key belonging to this address must be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "revoke \"0123456701234567012345670123456701234567012345670123456701234567\" \"072f36a77261cdd5d64c32d97bac417540eddca1d5612f416feb07ff75a8e240\"") ); From bd216856e3718f088a6ce5c40acceeb8ddead409 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 27 Dec 2018 12:10:34 +0300 Subject: [PATCH 03/11] Always use payoutAddress (or operatorPayoutAddress) as a default if no fundAddress was specified --- src/rpc/rpcevo.cpp | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 57414c5d9842..cb48b125f29e 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -95,6 +95,11 @@ static void FundSpecialTx(CMutableTransaction& tx, const SpecialTxPayload& paylo assert(pwalletMain != NULL); LOCK2(cs_main, pwalletMain->cs_wallet); + CTxDestination nodest = CNoDestination(); + if (fundDest == nodest) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "No source of funds specified"); + } + CDataStream ds(SER_NETWORK, PROTOCOL_VERSION); ds << payload; tx.vExtraPayload.assign(ds.begin(), ds.end()); @@ -115,21 +120,16 @@ static void FundSpecialTx(CMutableTransaction& tx, const SpecialTxPayload& paylo } CCoinControl coinControl; - CTxDestination nodest = CNoDestination(); - if (fundDest != nodest) { - coinControl.destChange = fundDest; + coinControl.destChange = fundDest; - std::vector vecOutputs; - pwalletMain->AvailableCoins(vecOutputs); + std::vector vecOutputs; + pwalletMain->AvailableCoins(vecOutputs); - for (const auto& out : vecOutputs) { - CTxDestination txDest; - if (ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, txDest) && txDest == fundDest) { - coinControl.Select(COutPoint(out.tx->tx->GetHash(), out.i)); - } + for (const auto& out : vecOutputs) { + CTxDestination txDest; + if (ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, txDest) && txDest == fundDest) { + coinControl.Select(COutPoint(out.tx->tx->GetHash(), out.i)); } - } else { - coinControl.fAllowOtherInputs = true; } CWalletTx wtx; @@ -388,7 +388,7 @@ UniValue protx_register(const JSONRPCRequest& request) ptx.vchSig.resize(65); } - CBitcoinAddress fundAddress; + CBitcoinAddress fundAddress = payoutAddress; if (request.params.size() > paramIdx + 6) { fundAddress = CBitcoinAddress(request.params[paramIdx + 6].get_str()); if (!fundAddress.IsValid()) @@ -532,7 +532,7 @@ UniValue protx_update_service(const JSONRPCRequest& request) tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_UPDATE_SERVICE; - CBitcoinAddress fundAddress; + CBitcoinAddress fundAddress = payoutAddress; if (request.params.size() > 5) { fundAddress = CBitcoinAddress(request.params[5].get_str()); if (!fundAddress.IsValid()) @@ -613,7 +613,7 @@ UniValue protx_update_registrar(const JSONRPCRequest& request) // make sure we get anough fees added ptx.vchSig.resize(65); - CBitcoinAddress fundAddress; + CBitcoinAddress fundAddress = payoutAddress; if (request.params.size() > 5) { fundAddress = CBitcoinAddress(request.params[5].get_str()); if (!fundAddress.IsValid()) @@ -685,9 +685,18 @@ UniValue protx_revoke(const JSONRPCRequest& request) fundAddress = CBitcoinAddress(request.params[4].get_str()); if (!fundAddress.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[4].get_str()); + FundSpecialTx(tx, ptx, fundAddress.Get()); + } else { + // Using funds in previousely specified operator payout address + if (dmn->pdmnState->scriptOperatorPayout == CScript()) { + // No scriptOperatorPayout was specified, can't revoke + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "scriptOperatorPayout was not specified earlier, can't revoke"); + } + CTxDestination txDest; + ExtractDestination(dmn->pdmnState->scriptOperatorPayout, txDest); + FundSpecialTx(tx, ptx, txDest); } - FundSpecialTx(tx, ptx, fundAddress.Get()); SignSpecialTxPayloadByHash(tx, ptx, keyOperator); SetTxPayload(tx, ptx); From 48ab1be3d20179f47ddb6456a205b2fd641655e6 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 27 Dec 2018 12:37:09 +0300 Subject: [PATCH 04/11] make sure we actually selected some inputs --- src/rpc/rpcevo.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index cb48b125f29e..a731052c736a 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -132,6 +132,10 @@ static void FundSpecialTx(CMutableTransaction& tx, const SpecialTxPayload& paylo } } + if (!coinControl.HasSelected()) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "No funds at specified address"); + } + CWalletTx wtx; CReserveKey reservekey(pwalletMain); CAmount nFee; From 1566b9442de82f888d5230807e87147b1b091dd4 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 27 Dec 2018 12:37:51 +0300 Subject: [PATCH 05/11] fundAddress -> feeSourceAddress --- src/rpc/rpcevo.cpp | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index a731052c736a..33d45bc3190c 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -255,7 +255,7 @@ void protx_register_fund_help() void protx_register_help() { throw std::runtime_error( - "protx register \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerKeyAddr\" \"operatorPubKey\" \"votingKeyAddr\" operatorReward \"payoutAddress\" ( \"fundAddress\" )\n" + "protx register \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerKeyAddr\" \"operatorPubKey\" \"votingKeyAddr\" operatorReward \"payoutAddress\" ( \"feeSourceAddress\" )\n" "\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. It must also not be used by any other masternode.\n" @@ -263,6 +263,7 @@ void protx_register_help() "1. \"collateralHash\" (string, required) The collateral transaction hash.\n" "2. collateralIndex (numeric, required) The collateral transaction output index.\n" "3., 4., 5. ... See help text of \"protx register_fund\"\n" + "feeSourceAddress See help text for \"fundAddress\" of \"protx register_fund\"\n" "\nExamples:\n" + HelpExampleCli("protx", "register \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") ); @@ -271,12 +272,13 @@ void protx_register_help() void protx_register_prepare_help() { throw std::runtime_error( - "protx register_prepare \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerKeyAddr\" \"operatorPubKey\" \"votingKeyAddr\" operatorReward \"payoutAddress\" ( \"fundAddress\" )\n" + "protx register_prepare \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerKeyAddr\" \"operatorPubKey\" \"votingKeyAddr\" operatorReward \"payoutAddress\" ( \"feeSourceAddress\" )\n" "\nCreates an unsigned ProTx and returns it. The ProTx must be signed externally with the collateral\n" "key and then passed to \"protx register_submit\". The prepared transaction will also contain inputs\n" "and outputs to cover fees.\n" "\nArguments:\n" "1., 2., 3., ... See help text of \"protx register\".\n" + "feeSourceAddress See help text for \"fundAddress\" of \"protx register_fund\"\n" "\nResult:\n" "{ (json object)\n" " \"tx\" : (string) The serialized ProTx in hex format.\n" @@ -481,7 +483,7 @@ UniValue protx_register_submit(const JSONRPCRequest& request) void protx_update_service_help() { throw std::runtime_error( - "protx update_service \"proTxHash\" \"ipAndPort\" \"operatorKey\" (\"operatorPayoutAddress\" \"fundAddress\" )\n" + "protx update_service \"proTxHash\" \"ipAndPort\" \"operatorKey\" (\"operatorPayoutAddress\" \"feeSourceAddress\" )\n" "\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" @@ -493,7 +495,7 @@ void protx_update_service_help() " registered operator public key.\n" "4. \"operatorPayoutAddress\" (string, optional) The address used for operator reward payments.\n" " Only allowed when the ProRegTx had a non-zero operatorReward value.\n" - "5. \"fundAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" + "5. \"feeSourceAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" " The private key belonging to this address must be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "update_service \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" 5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd") @@ -536,14 +538,14 @@ UniValue protx_update_service(const JSONRPCRequest& request) tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_UPDATE_SERVICE; - CBitcoinAddress fundAddress = payoutAddress; + CBitcoinAddress feeSourceAddress = payoutAddress; if (request.params.size() > 5) { - fundAddress = CBitcoinAddress(request.params[5].get_str()); - if (!fundAddress.IsValid()) + feeSourceAddress = CBitcoinAddress(request.params[5].get_str()); + if (!feeSourceAddress.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[5].get_str()); } - FundSpecialTx(tx, ptx, fundAddress.Get()); + FundSpecialTx(tx, ptx, feeSourceAddress.Get()); SignSpecialTxPayloadByHash(tx, ptx, keyOperator); SetTxPayload(tx, ptx); @@ -553,7 +555,7 @@ UniValue protx_update_service(const JSONRPCRequest& request) void protx_update_registrar_help() { throw std::runtime_error( - "protx update_registrar \"proTxHash\" \"operatorKeyAddr\" \"votingKeyAddr\" \"payoutAddress\" ( \"fundAddress\" )\n" + "protx update_registrar \"proTxHash\" \"operatorKeyAddr\" \"votingKeyAddr\" \"payoutAddress\" ( \"feeSourceAddress\" )\n" "\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" @@ -567,7 +569,7 @@ void protx_update_registrar_help() " If set to \"0\" or an empty string, the last on-chain voting key of the masternode will be used.\n" "4. \"payoutAddress\" (string, required) The dash address to use for masternode reward payments\n" " If set to \"0\" or an empty string, the last on-chain payout address of the masternode will be used.\n" - "5. \"fundAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" + "5. \"feeSourceAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" " The private key belonging to this address must be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "update_registrar \"0123456701234567012345670123456701234567012345670123456701234567\" \"982eb34b7c7f614f29e5c665bc3605f1beeef85e3395ca12d3be49d2868ecfea5566f11cedfad30c51b2403f2ad95b67\" \"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwG\"") @@ -617,14 +619,14 @@ UniValue protx_update_registrar(const JSONRPCRequest& request) // make sure we get anough fees added ptx.vchSig.resize(65); - CBitcoinAddress fundAddress = payoutAddress; + CBitcoinAddress feeSourceAddress = payoutAddress; if (request.params.size() > 5) { - fundAddress = CBitcoinAddress(request.params[5].get_str()); - if (!fundAddress.IsValid()) + feeSourceAddress = CBitcoinAddress(request.params[5].get_str()); + if (!feeSourceAddress.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[5].get_str()); } - FundSpecialTx(tx, ptx, fundAddress.Get()); + FundSpecialTx(tx, ptx, feeSourceAddress.Get()); SignSpecialTxPayloadByHash(tx, ptx, keyOwner); SetTxPayload(tx, ptx); @@ -634,7 +636,7 @@ UniValue protx_update_registrar(const JSONRPCRequest& request) void protx_revoke_help() { throw std::runtime_error( - "protx revoke \"proTxHash\" \"operatorKey\" ( reason \"fundAddress\")\n" + "protx revoke \"proTxHash\" \"operatorKey\" ( reason \"feeSourceAddress\")\n" "\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" @@ -643,8 +645,8 @@ void protx_revoke_help() "1. \"proTxHash\" (string, required) The hash of the initial ProRegTx.\n" "2. \"operatorKey\" (string, required) The operator private key belonging to the\n" " registered operator public key.\n" - "3. reason (numeric, optional) The reason for revocation.\n" - "4. \"fundAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" + "3. reason (numeric, optional) The reason for revocation.\n" + "4. \"feeSourceAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" " The private key belonging to this address must be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "revoke \"0123456701234567012345670123456701234567012345670123456701234567\" \"072f36a77261cdd5d64c32d97bac417540eddca1d5612f416feb07ff75a8e240\"") @@ -684,12 +686,12 @@ UniValue protx_revoke(const JSONRPCRequest& request) tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_UPDATE_REVOKE; - CBitcoinAddress fundAddress; + CBitcoinAddress feeSourceAddress; if (request.params.size() > 4) { - fundAddress = CBitcoinAddress(request.params[4].get_str()); - if (!fundAddress.IsValid()) + feeSourceAddress = CBitcoinAddress(request.params[4].get_str()); + if (!feeSourceAddress.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[4].get_str()); - FundSpecialTx(tx, ptx, fundAddress.Get()); + FundSpecialTx(tx, ptx, feeSourceAddress.Get()); } else { // Using funds in previousely specified operator payout address if (dmn->pdmnState->scriptOperatorPayout == CScript()) { From e3cec372c7de653c80e00c97baefd9bb57d2c09f Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 27 Dec 2018 12:40:54 +0300 Subject: [PATCH 06/11] Align help text with new logic --- src/rpc/rpcevo.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 33d45bc3190c..e224fa7ead24 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -246,6 +246,7 @@ void protx_register_fund_help() " between 0.00 and 100.00.\n" "7. \"payoutAddress\" (string, required) The dash address to use for masternode reward payments.\n" "8. \"fundAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" + " If not specified, payoutAddress is the one that is going to be used.\n" " The private key belonging to this address must be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "register_fund \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") @@ -496,6 +497,7 @@ void protx_update_service_help() "4. \"operatorPayoutAddress\" (string, optional) The address used for operator reward payments.\n" " Only allowed when the ProRegTx had a non-zero operatorReward value.\n" "5. \"feeSourceAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" + " If not specified, operatorPayoutAddress is the one that is going to be used.\n" " The private key belonging to this address must be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "update_service \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" 5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd") @@ -570,6 +572,7 @@ void protx_update_registrar_help() "4. \"payoutAddress\" (string, required) The dash address to use for masternode reward payments\n" " If set to \"0\" or an empty string, the last on-chain payout address of the masternode will be used.\n" "5. \"feeSourceAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" + " If not specified, payoutAddress is the one that is going to be used.\n" " The private key belonging to this address must be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "update_registrar \"0123456701234567012345670123456701234567012345670123456701234567\" \"982eb34b7c7f614f29e5c665bc3605f1beeef85e3395ca12d3be49d2868ecfea5566f11cedfad30c51b2403f2ad95b67\" \"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwG\"") @@ -647,6 +650,7 @@ void protx_revoke_help() " registered operator public key.\n" "3. reason (numeric, optional) The reason for revocation.\n" "4. \"feeSourceAddress\" (string, optional) If specified wallet will only use coins from this address to fund ProTx.\n" + " If not specified, operatorPayoutAddress specified earlier is the one that is going to be used.\n" " The private key belonging to this address must be known in your wallet.\n" "\nExamples:\n" + HelpExampleCli("protx", "revoke \"0123456701234567012345670123456701234567012345670123456701234567\" \"072f36a77261cdd5d64c32d97bac417540eddca1d5612f416feb07ff75a8e240\"") From 8fdc2a04b1137289f3f4eb37b18cebf095266934 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 27 Dec 2018 13:09:29 +0300 Subject: [PATCH 07/11] fix protx_update_service --- src/rpc/rpcevo.cpp | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index e224fa7ead24..add857a54486 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -519,14 +519,6 @@ UniValue protx_update_service(const JSONRPCRequest& request) CBLSSecretKey keyOperator = ParseBLSSecretKey(request.params[3].get_str(), "operatorKey"); - if (request.params.size() > 4) { - CBitcoinAddress payoutAddress(request.params[4].get_str()); - if (!payoutAddress.IsValid()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid operator payout address: %s", request.params[4].get_str())); - } - ptx.scriptOperatorPayout = GetScriptForDestination(payoutAddress.Get()); - } - auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(ptx.proTxHash); if (!dmn) { throw std::runtime_error(strprintf("masternode with proTxHash %s not found", ptx.proTxHash.ToString())); @@ -540,14 +532,36 @@ UniValue protx_update_service(const JSONRPCRequest& request) tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_UPDATE_SERVICE; - CBitcoinAddress feeSourceAddress = payoutAddress; - if (request.params.size() > 5) { - feeSourceAddress = CBitcoinAddress(request.params[5].get_str()); - if (!feeSourceAddress.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[5].get_str()); + if (request.params.size() >= 5) { + // Using funds from currently specified operator payout address + CBitcoinAddress payoutAddress(request.params[4].get_str()); + if (!payoutAddress.IsValid()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid operator payout address: %s", request.params[4].get_str())); + } + ptx.scriptOperatorPayout = GetScriptForDestination(payoutAddress.Get()); + if (request.params.size() == 5) { + FundSpecialTx(tx, ptx, payoutAddress.Get()); + } else { + // Using funds from currently specified feeSourceAddress + CBitcoinAddress feeSourceAddress = CBitcoinAddress(request.params[5].get_str()); + if (!feeSourceAddress.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[5].get_str()); + FundSpecialTx(tx, ptx, feeSourceAddress.Get()); + } + } else if (dmn->pdmnState->scriptOperatorPayout != CScript()) { + // Using funds from previousely specified operator payout address + CTxDestination txDest; + ExtractDestination(dmn->pdmnState->scriptOperatorPayout, txDest); + FundSpecialTx(tx, ptx, txDest); + } else if (dmn->pdmnState->scriptPayout != CScript()) { + // Using funds from previousely specified masternode payout address + CTxDestination txDest; + ExtractDestination(dmn->pdmnState->scriptPayout, txDest); + FundSpecialTx(tx, ptx, txDest); + } else { + throw JSONRPCError(RPC_INTERNAL_ERROR, "No payout or fee source addresses found, can't update"); } - FundSpecialTx(tx, ptx, feeSourceAddress.Get()); SignSpecialTxPayloadByHash(tx, ptx, keyOperator); SetTxPayload(tx, ptx); From f542d1157ebaecf8c072d0f1584c39b61cca0e36 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 27 Dec 2018 13:16:23 +0300 Subject: [PATCH 08/11] fix protx_revoke --- src/rpc/rpcevo.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index add857a54486..2927603e69e7 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -704,21 +704,23 @@ UniValue protx_revoke(const JSONRPCRequest& request) tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_UPDATE_REVOKE; - CBitcoinAddress feeSourceAddress; if (request.params.size() > 4) { - feeSourceAddress = CBitcoinAddress(request.params[4].get_str()); + CBitcoinAddress feeSourceAddress = CBitcoinAddress(request.params[4].get_str()); if (!feeSourceAddress.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[4].get_str()); FundSpecialTx(tx, ptx, feeSourceAddress.Get()); - } else { - // Using funds in previousely specified operator payout address - if (dmn->pdmnState->scriptOperatorPayout == CScript()) { - // No scriptOperatorPayout was specified, can't revoke - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "scriptOperatorPayout was not specified earlier, can't revoke"); - } + } else if (dmn->pdmnState->scriptOperatorPayout != CScript()) { + // Using funds from previousely specified operator payout address CTxDestination txDest; ExtractDestination(dmn->pdmnState->scriptOperatorPayout, txDest); FundSpecialTx(tx, ptx, txDest); + } else if (dmn->pdmnState->scriptPayout != CScript()) { + // Using funds from previousely specified masternode payout address + CTxDestination txDest; + ExtractDestination(dmn->pdmnState->scriptPayout, txDest); + FundSpecialTx(tx, ptx, txDest); + } else { + throw JSONRPCError(RPC_INTERNAL_ERROR, "No payout or fee source addresses found, can't revoke"); } SignSpecialTxPayloadByHash(tx, ptx, keyOperator); From 6aa13fc7fd8e9a4784abef95b011a60fec758399 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 27 Dec 2018 11:59:55 +0100 Subject: [PATCH 09/11] Add fRequireAllInputs to CCoinControl and use it in FundSpecialTx This avoids adding all inputs even though they are not needed. --- src/rpc/rpcevo.cpp | 1 + src/wallet/coincontrol.h | 5 ++++- src/wallet/wallet.cpp | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 2927603e69e7..1a9a4c167398 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -121,6 +121,7 @@ static void FundSpecialTx(CMutableTransaction& tx, const SpecialTxPayload& paylo CCoinControl coinControl; coinControl.destChange = fundDest; + coinControl.fRequireAllInputs = false; std::vector vecOutputs; pwalletMain->AvailableCoins(vecOutputs); diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index 50469dc725f3..2d06635f6121 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -14,8 +14,10 @@ class CCoinControl CTxDestination destChange; bool fUsePrivateSend; bool fUseInstantSend; - //! If false, allows unselected inputs, but requires all selected inputs be used + //! If false, allows unselected inputs, but requires all selected inputs be used if fAllowOtherInputs is true (default) bool fAllowOtherInputs; + //! If false, only include as many inputs as necessary to fulfill a coin selection request. Only usable together with fAllowOtherInputs + bool fRequireAllInputs; //! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE criteria bool fAllowWatchOnly; //! Minimum absolute fee (not per kilobyte) @@ -36,6 +38,7 @@ class CCoinControl { destChange = CNoDestination(); fAllowOtherInputs = false; + fRequireAllInputs = true; fAllowWatchOnly = false; setSelected.clear(); fUseInstantSend = false; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f1bb0b4ccec4..553a912d7beb 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2889,6 +2889,11 @@ bool CWallet::SelectCoins(const std::vector& vAvailableCoins, const CAm } nValueRet += out.tx->tx->vout[out.i].nValue; setCoinsRet.insert(std::make_pair(out.tx, out.i)); + + if (!coinControl->fRequireAllInputs && nValueRet >= nTargetValue) { + // stop when we added at least one input and enough inputs to have at least nTargetValue funds + return true; + } } return (nValueRet >= nTargetValue); From 6f88fc5496d3439c53546244c191cd644e393363 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 27 Dec 2018 15:31:10 +0100 Subject: [PATCH 10/11] Allow to specify empty operator payout address and simplify fee code in update_service DIP3 tests were failing to call "protx update_service" as it did not accept an empty "operatorPayoutAddress". This also simplifies handling of the feeSourceAddr. --- src/rpc/rpcevo.cpp | 53 +++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 1a9a4c167398..754763e39cdc 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -533,36 +533,41 @@ UniValue protx_update_service(const JSONRPCRequest& request) tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_UPDATE_SERVICE; + // param operatorPayoutAddress if (request.params.size() >= 5) { - // Using funds from currently specified operator payout address - CBitcoinAddress payoutAddress(request.params[4].get_str()); - if (!payoutAddress.IsValid()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid operator payout address: %s", request.params[4].get_str())); - } - ptx.scriptOperatorPayout = GetScriptForDestination(payoutAddress.Get()); - if (request.params.size() == 5) { - FundSpecialTx(tx, ptx, payoutAddress.Get()); + if (request.params[4].get_str().empty() || request.params[4].get_str() == "0") { + ptx.scriptOperatorPayout = dmn->pdmnState->scriptOperatorPayout; } else { - // Using funds from currently specified feeSourceAddress - CBitcoinAddress feeSourceAddress = CBitcoinAddress(request.params[5].get_str()); - if (!feeSourceAddress.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[5].get_str()); - FundSpecialTx(tx, ptx, feeSourceAddress.Get()); + CBitcoinAddress payoutAddress(request.params[4].get_str()); + if (!payoutAddress.IsValid()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid operator payout address: %s", request.params[4].get_str())); + } + ptx.scriptOperatorPayout = GetScriptForDestination(payoutAddress.Get()); } - } else if (dmn->pdmnState->scriptOperatorPayout != CScript()) { - // Using funds from previousely specified operator payout address - CTxDestination txDest; - ExtractDestination(dmn->pdmnState->scriptOperatorPayout, txDest); - FundSpecialTx(tx, ptx, txDest); - } else if (dmn->pdmnState->scriptPayout != CScript()) { - // Using funds from previousely specified masternode payout address - CTxDestination txDest; - ExtractDestination(dmn->pdmnState->scriptPayout, txDest); - FundSpecialTx(tx, ptx, txDest); } else { - throw JSONRPCError(RPC_INTERNAL_ERROR, "No payout or fee source addresses found, can't update"); + ptx.scriptOperatorPayout = dmn->pdmnState->scriptOperatorPayout; + } + + CTxDestination feeSource; + + // param feeSourceAddress + if (request.params.size() >= 6) { + CBitcoinAddress feeSourceAddress = CBitcoinAddress(request.params[5].get_str()); + if (!feeSourceAddress.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[5].get_str()); + feeSource = feeSourceAddress.Get(); + } else { + if (ptx.scriptOperatorPayout != CScript()) { + // use operator reward address as default source for fees + ExtractDestination(ptx.scriptOperatorPayout, feeSource); + } else { + // use payout address as default source for fees + ExtractDestination(dmn->pdmnState->scriptPayout, feeSource); + } } + FundSpecialTx(tx, ptx, feeSource); + SignSpecialTxPayloadByHash(tx, ptx, keyOperator); SetTxPayload(tx, ptx); From 239e7f3c4eb56a2c366fe9a87070ee12f2f2a452 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 27 Dec 2018 15:32:29 +0100 Subject: [PATCH 11/11] Specify source for funds and fees in DIP3 tests --- qa/rpc-tests/dip3-deterministicmns.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/qa/rpc-tests/dip3-deterministicmns.py b/qa/rpc-tests/dip3-deterministicmns.py index 0ee75ae2d1a1..09ed89207582 100755 --- a/qa/rpc-tests/dip3-deterministicmns.py +++ b/qa/rpc-tests/dip3-deterministicmns.py @@ -415,6 +415,7 @@ def create_mn_protx_base(self, node, idx, alias, legacy_mn_key): mn.p2p_port = p2p_port(mn.idx) blsKey = node.bls('generate') + mn.fundsAddr = node.getnewaddress() mn.ownerAddr = node.getnewaddress() mn.operatorAddr = blsKey['public'] mn.votingAddr = mn.ownerAddr @@ -426,9 +427,11 @@ def create_mn_protx_base(self, node, idx, alias, legacy_mn_key): # create a protx MN and also fund it (using collateral inside ProRegTx) def create_mn_protx_fund(self, node, idx, alias, legacy_mn_key=None): mn = self.create_mn_protx_base(node, idx, alias, legacy_mn_key=legacy_mn_key) + node.sendtoaddress(mn.fundsAddr, 1000.001) + mn.collateral_address = node.getnewaddress() - mn.protx_hash = node.protx('register_fund', mn.collateral_address, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.collateral_address) + mn.protx_hash = node.protx('register_fund', mn.collateral_address, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.collateral_address, mn.fundsAddr) mn.collateral_txid = mn.protx_hash mn.collateral_vout = -1 @@ -444,9 +447,11 @@ def create_mn_protx_fund(self, node, idx, alias, legacy_mn_key=None): # create a protx MN which refers to an existing collateral def create_mn_protx(self, node, idx, alias, collateral_txid, collateral_vout, legacy_mn_key=None): mn = self.create_mn_protx_base(node, idx, alias, legacy_mn_key=legacy_mn_key) + node.sendtoaddress(mn.fundsAddr, 0.001) + mn.rewards_address = node.getnewaddress() - mn.protx_hash = node.protx('register', collateral_txid, collateral_vout, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.rewards_address) + mn.protx_hash = node.protx('register', collateral_txid, collateral_vout, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.rewards_address, mn.fundsAddr) mn.collateral_txid = collateral_txid mn.collateral_vout = collateral_vout @@ -476,14 +481,16 @@ def upgrade_mn_protx(self, mn, refund): return mn def update_mn_payee(self, mn, payee): - self.nodes[0].protx('update_registrar', mn.protx_hash, '', '', payee) + self.nodes[0].sendtoaddress(mn.fundsAddr, 0.001) + self.nodes[0].protx('update_registrar', mn.protx_hash, '', '', payee, mn.fundsAddr) self.nodes[0].generate(1) self.sync_all() info = self.nodes[0].protx('info', mn.protx_hash) assert(info['state']['payoutAddress'] == payee) def test_protx_update_service(self, mn): - self.nodes[0].protx('update_service', mn.protx_hash, '127.0.0.2:%d' % mn.p2p_port, mn.blsMnkey) + self.nodes[0].sendtoaddress(mn.fundsAddr, 0.001) + self.nodes[0].protx('update_service', mn.protx_hash, '127.0.0.2:%d' % mn.p2p_port, mn.blsMnkey, "", mn.fundsAddr) self.nodes[0].generate(1) self.sync_all() for node in self.nodes: @@ -493,7 +500,7 @@ def test_protx_update_service(self, mn): assert_equal(mn_list['%s-%d' % (mn.collateral_txid, mn.collateral_vout)]['address'], '127.0.0.2:%d' % mn.p2p_port) # undo - self.nodes[0].protx('update_service', mn.protx_hash, '127.0.0.1:%d' % mn.p2p_port, mn.blsMnkey) + self.nodes[0].protx('update_service', mn.protx_hash, '127.0.0.1:%d' % mn.p2p_port, mn.blsMnkey, "", mn.fundsAddr) self.nodes[0].generate(1) def force_finish_mnsync(self, node): @@ -732,16 +739,20 @@ def wait_for_mnlists_same(self, timeout=30): def test_fail_create_protx(self, node): # Try to create ProTx (should still fail) + fund_address = node.getnewaddress() address = node.getnewaddress() + node.sendtoaddress(fund_address, 1000.001) # +0.001 for fees key = node.getnewaddress() blsKey = node.bls('generate') - assert_raises_jsonrpc(None, "bad-tx-type", node.protx, 'register_fund', address, '127.0.0.1:10000', key, blsKey['public'], key, 0, address) + assert_raises_jsonrpc(None, "bad-tx-type", node.protx, 'register_fund', address, '127.0.0.1:10000', key, blsKey['public'], key, 0, address, fund_address) def test_success_create_protx(self, node): + fund_address = node.getnewaddress() address = node.getnewaddress() + txid = node.sendtoaddress(fund_address, 1000.001) # +0.001 for fees key = node.getnewaddress() blsKey = node.bls('generate') - txid = node.protx('register_fund', address, '127.0.0.1:10000', key, blsKey['public'], key, 0, address) + node.protx('register_fund', address, '127.0.0.1:10000', key, blsKey['public'], key, 0, address, fund_address) rawtx = node.getrawtransaction(txid, 1) self.mine_double_spend(node, rawtx['vin'], address, use_mnmerkleroot_from_tip=True) self.sync_all()