diff options
Diffstat (limited to 'src/wallet/rpcwallet.cpp')
-rw-r--r-- | src/wallet/rpcwallet.cpp | 147 |
1 files changed, 86 insertions, 61 deletions
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 78ea5eac92..0f55ef20ae 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -161,7 +161,11 @@ static UniValue getnewaddress(const JSONRPCRequest& request) + HelpExampleRpc("getnewaddress", "") ); - LOCK2(cs_main, pwallet->cs_wallet); + if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); + } + + LOCK(pwallet->cs_wallet); // Parse the label first so we don't generate a key if there's an error std::string label; @@ -268,7 +272,11 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request) + HelpExampleRpc("getrawchangeaddress", "") ); - LOCK2(cs_main, pwallet->cs_wallet); + if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); + } + + LOCK(pwallet->cs_wallet); if (!pwallet->IsLocked()) { pwallet->TopUpKeyPool(); @@ -323,7 +331,7 @@ static UniValue setlabel(const JSONRPCRequest& request) + HelpExampleRpc("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"") ); - LOCK2(cs_main, pwallet->cs_wallet); + LOCK(pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str()); if (!IsValidDestination(dest)) { @@ -1843,7 +1851,7 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const { if (wtx.GetDepthInMainChain() < 1) entry.pushKV("category", "orphan"); - else if (wtx.GetBlocksToMaturity() > 0) + else if (wtx.IsImmatureCoinBase()) entry.pushKV("category", "immature"); else entry.pushKV("category", "generate"); @@ -2139,7 +2147,7 @@ static UniValue listaccounts(const JSONRPCRequest& request) std::list<COutputEntry> listReceived; std::list<COutputEntry> listSent; int nDepth = wtx.GetDepthInMainChain(); - if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) + if (wtx.IsImmatureCoinBase() || nDepth < 0) continue; wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); mapAccountBalances[strSentAccount] -= nFee; @@ -2506,6 +2514,10 @@ static UniValue keypoolrefill(const JSONRPCRequest& request) + HelpExampleRpc("keypoolrefill", "") ); + if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); + } + LOCK2(cs_main, pwallet->cs_wallet); // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool @@ -2970,8 +2982,16 @@ static UniValue settxfee(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); CAmount nAmount = AmountFromValue(request.params[0]); + CFeeRate tx_fee_rate(nAmount, 1000); + if (tx_fee_rate == 0) { + // automatic selection + } else if (tx_fee_rate < ::minRelayTxFee) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than min relay tx fee (%s)", ::minRelayTxFee.ToString())); + } else if (tx_fee_rate < pwallet->m_min_fee) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than wallet min fee (%s)", pwallet->m_min_fee.ToString())); + } - pwallet->m_pay_tx_fee = CFeeRate(nAmount, 1000); + pwallet->m_pay_tx_fee = tx_fee_rate; return true; } @@ -2990,19 +3010,20 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) "Returns an object containing various wallet state info.\n" "\nResult:\n" "{\n" - " \"walletname\": xxxxx, (string) the wallet name\n" - " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n" - " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n" - " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n" - " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" - " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n" - " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n" - " \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n" - " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" - " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" - " \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n" - " \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias for hdseedid retained for backwards-compatibility. Will be removed in V0.18.\n" + " \"walletname\": xxxxx, (string) the wallet name\n" + " \"walletversion\": xxxxx, (numeric) the wallet version\n" + " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n" + " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" + " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n" + " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n" + " \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n" + " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" + " \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n" + " \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias for hdseedid retained for backwards-compatibility. Will be removed in V0.18.\n" + " \"private_keys_enabled\": true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n" "}\n" "\nExamples:\n" + HelpExampleCli("getwalletinfo", "") @@ -3038,6 +3059,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) obj.pushKV("hdseedid", seed_id.GetHex()); obj.pushKV("hdmasterkeyid", seed_id.GetHex()); } + obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); return obj; } @@ -3128,12 +3150,13 @@ static UniValue loadwallet(const JSONRPCRequest& request) static UniValue createwallet(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() != 1) { + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( - "createwallet \"wallet_name\"\n" + "createwallet \"wallet_name\" ( disable_private_keys )\n" "\nCreates and loads a new wallet.\n" "\nArguments:\n" - "1. \"wallet_name\" (string, required) The name for the new wallet. If this is a path, the wallet will be created at the path location.\n" + "1. \"wallet_name\" (string, required) The name for the new wallet. If this is a path, the wallet will be created at the path location.\n" + "2. disable_private_keys (boolean, optional, default: false) Disable the possibility of private keys (only watchonlys are possible in this mode).\n" "\nResult:\n" "{\n" " \"name\" : <wallet_name>, (string) The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path.\n" @@ -3148,6 +3171,11 @@ static UniValue createwallet(const JSONRPCRequest& request) std::string error; std::string warning; + bool disable_privatekeys = false; + if (!request.params[1].isNull()) { + disable_privatekeys = request.params[1].get_bool(); + } + fs::path wallet_path = fs::absolute(wallet_name, GetWalletDir()); if (fs::symlink_status(wallet_path).type() != fs::file_not_found) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + wallet_name + " already exists."); @@ -3158,7 +3186,7 @@ static UniValue createwallet(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error); } - std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_name, fs::absolute(wallet_name, GetWalletDir())); + std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_name, fs::absolute(wallet_name, GetWalletDir()), (disable_privatekeys ? (uint64_t)WALLET_FLAG_DISABLE_PRIVATE_KEYS : 0)); if (!wallet) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed."); } @@ -4001,9 +4029,8 @@ public: void ProcessSubScript(const CScript& subscript, UniValue& obj, bool include_addresses = false) const { // Always present: script type and redeemscript - txnouttype which_type; std::vector<std::vector<unsigned char>> solutions_data; - Solver(subscript, which_type, solutions_data); + txnouttype which_type = Solver(subscript, solutions_data); obj.pushKV("script", GetTxnOutputType(which_type)); obj.pushKV("hex", HexStr(subscript.begin(), subscript.end())); @@ -4433,7 +4460,9 @@ bool ParseHDKeypath(std::string keypath_str, std::vector<uint32_t>& keypath) return false; } uint32_t number; - ParseUInt32(item, &number); + if (!ParseUInt32(item, &number)) { + return false; + } path |= number; keypath.push_back(path); @@ -4482,10 +4511,11 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C // If we don't know about this input, skip it and let someone else deal with it const uint256& txhash = txin.prevout.hash; - const auto& it = pwallet->mapWallet.find(txhash); + const auto it = pwallet->mapWallet.find(txhash); if (it != pwallet->mapWallet.end()) { const CWalletTx& wtx = it->second; CTxOut utxo = wtx.tx->vout[txin.prevout.n]; + // Update both UTXOs from the wallet. input.non_witness_utxo = wtx.tx; input.witness_utxo = utxo; } @@ -4502,11 +4532,13 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C complete &= SignPSBTInput(PublicOnlySigningProvider(pwallet), *psbtx.tx, input, sigdata, i, sighash_type); } - // Drop the unnecessary UTXO - if (sigdata.witness) { - input.non_witness_utxo = nullptr; - } else { - input.witness_utxo.SetNull(); + if (it != pwallet->mapWallet.end()) { + // Drop the unnecessary UTXO if we added both from the wallet. + if (sigdata.witness) { + input.non_witness_utxo = nullptr; + } else { + input.witness_utxo.SetNull(); + } } // Get public key paths @@ -4522,11 +4554,6 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C const CTxOut& out = txConst->vout.at(i); PSBTOutput& psbt_out = psbtx.outputs.at(i); - // Dummy tx so we can use ProduceSignature to get stuff out - CMutableTransaction dummy_tx; - dummy_tx.vin.push_back(CTxIn()); - dummy_tx.vout.push_back(CTxOut()); - // Fill a SignatureData with output info SignatureData sigdata; psbt_out.FillSignatureData(sigdata); @@ -4608,8 +4635,8 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request) UniValue result(UniValue::VOBJ); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << psbtx; - result.push_back(Pair("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()))); - result.push_back(Pair("complete", complete)); + result.pushKV("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size())); + result.pushKV("complete", complete); return result; } @@ -4623,7 +4650,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 2 || request.params.size() > 6) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) throw std::runtime_error( "walletcreatefundedpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable ) ( options bip32derivs )\n" "\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n" @@ -4650,9 +4677,8 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) " accepted as second parameter.\n" " ]\n" "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" - "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n" " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n" - "5. options (object, optional)\n" + "4. options (object, optional)\n" " {\n" " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n" " \"changePosition\" (numeric, optional, default random) The index of the change output\n" @@ -4674,7 +4700,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) " \"ECONOMICAL\"\n" " \"CONSERVATIVE\"\n" " }\n" - "6. bip32derivs (boolean, optiona, default=false) If true, includes the BIP 32 derivation paths for public keys if we know them\n" + "5. bip32derivs (boolean, optiona, default=false) If true, includes the BIP 32 derivation paths for public keys if we know them\n" "\nResult:\n" "{\n" " \"psbt\": \"value\", (string) The resulting raw transaction (base64-encoded string)\n" @@ -4690,15 +4716,15 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) UniValue::VARR, UniValueType(), // ARR or OBJ, checked later UniValue::VNUM, - UniValue::VBOOL, - UniValue::VOBJ + UniValue::VOBJ, + UniValue::VBOOL }, true ); CAmount fee; int change_position; - CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]); - FundTransaction(pwallet, rawTx, fee, change_position, request.params[4]); + CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]["replaceable"]); + FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]); // Make a blank psbt PartiallySignedTransaction psbtx; @@ -4715,7 +4741,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) const CTransaction txConst(*psbtx.tx); // Fill transaction with out data but don't sign - bool bip32derivs = request.params[5].isNull() ? false : request.params[5].get_bool(); + bool bip32derivs = request.params[4].isNull() ? false : request.params[5].get_bool(); FillPSBT(pwallet, psbtx, &txConst, 1, false, bip32derivs); // Serialize the PSBT @@ -4729,24 +4755,23 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) return result; } -extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp -extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp -extern UniValue importprivkey(const JSONRPCRequest& request); -extern UniValue importaddress(const JSONRPCRequest& request); -extern UniValue importpubkey(const JSONRPCRequest& request); -extern UniValue dumpwallet(const JSONRPCRequest& request); -extern UniValue importwallet(const JSONRPCRequest& request); -extern UniValue importprunedfunds(const JSONRPCRequest& request); -extern UniValue removeprunedfunds(const JSONRPCRequest& request); -extern UniValue importmulti(const JSONRPCRequest& request); -extern UniValue rescanblockchain(const JSONRPCRequest& request); +UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp +UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp +UniValue importprivkey(const JSONRPCRequest& request); +UniValue importaddress(const JSONRPCRequest& request); +UniValue importpubkey(const JSONRPCRequest& request); +UniValue dumpwallet(const JSONRPCRequest& request); +UniValue importwallet(const JSONRPCRequest& request); +UniValue importprunedfunds(const JSONRPCRequest& request); +UniValue removeprunedfunds(const JSONRPCRequest& request); +UniValue importmulti(const JSONRPCRequest& request); static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} }, { "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} }, - { "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","replaceable","options","bip32derivs"} }, + { "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} }, { "hidden", "resendwallettransactions", &resendwallettransactions, {} }, { "wallet", "abandontransaction", &abandontransaction, {"txid"} }, { "wallet", "abortrescan", &abortrescan, {} }, @@ -4754,7 +4779,7 @@ static const CRPCCommand commands[] = { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, { "wallet", "backupwallet", &backupwallet, {"destination"} }, { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, - { "wallet", "createwallet", &createwallet, {"wallet_name"} }, + { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys"} }, { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, |