diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -17,21 +17,26 @@ this setting requires downloading the full blockchain again. This mode is incompatible with -txindex and -rescan. -'label' API for wallet ----------------------- - +'label' and 'account' APIs for wallet +------------------------------------- + A new 'label' API has been introduced for the wallet. This is intended as a - replacement for the deprecated 'account' API. - + replacement for the deprecated 'account' API. The 'account' can continue to + be used in v0.20 by starting bitcoind with the '-deprecatedrpc=accounts' + argument, and will be fully removed in v0.21. + The label RPC methods mirror the account functionality, with the following functional differences: - - - Labels can be set on any address, not just receiving addresses. This functionality was previously only available through the GUI. - - Labels can be deleted by reassigning all addresses using the `setlabel` RPC method. - - There isn't support for sending transactions _from_ a label, or for determining which label a transaction was sent from. + + - Labels can be set on any address, not just receiving addresses. This + functionality was previously only available through the GUI. + - Labels can be deleted by reassigning all addresses using the `setlabel` RPC + method. + - There isn't support for sending transactions _from_ a label, or for + determining which label a transaction was sent from. - Labels do not have a balance. - + Here are the changes to RPC methods: - + | Deprecated Method | New Method | Notes | | :---------------------- | :-------------------- | :-----------| | `getaccount` | `getaddressinfo` | `getaddressinfo` returns a json object with address information instead of just the name of the account as a string. | @@ -43,9 +48,12 @@ | `move` | n/a | _no replacement_ | | `sendfrom` | n/a | _no replacement_ | | `setaccount` | `setlabel` | Both methods now: <ul><li>allow assigning labels to any address, instead of raising an error if the address is not receiving address.<li>delete the previous label associated with an address when the final address using that label is reassigned to a different label, instead of making an implicit `getaccountaddress` call to ensure the previous label still has a receiving address. | - + | Changed Method | Notes | | :--------------------- | :------ | - | `addmultisigaddress` | Renamed `account` named parameter to `label`. Still accepts `account` for backward compatibility. | - | `getnewaddress` | Renamed `account` named parameter to `label`. Still accepts `account` for backward compatibility. | - | `listunspent` | Returns new `label` fields, along with `account` fields for backward compatibility. | + | `addmultisigaddress` | Renamed `account` named parameter to `label`. Still accepts `account` for backward compatibility if running with '-deprecatedrpc=accounts'. | + | `getnewaddress` | Renamed `account` named parameter to `label`. Still accepts `account` for backward compatibility. if running with '-deprecatedrpc=accounts' | + | `listunspent` | Returns new `label` fields. `account` field will be returned for backward compatibility if running with '-deprecatedrpc=accounts' | + | `sendmany` | The `account` named parameter has been renamed to `dummy`. If provided, the `dummy` parameter must be set to the empty string, unless running with the `-deprecatedrpc=accounts` argument (in which case functionality is unchanged). | + | `listtransactions` | The `account` named parameter has been renamed to `dummy`. If provided, the `dummy` parameter must be set to the string `*`, unless running with the `-deprecatedrpc=accounts` argument (in which case functionality is unchanged). | + | `getbalance` | `account`, `minconf` and `include_watchonly` parameters are deprecated, and can only be used if running with '-deprecatedrpc=accounts' | diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -240,6 +240,20 @@ return NullUniValue; } + if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && + request.strMethod == "getaccountaddress") { + if (request.fHelp) { + throw std::runtime_error( + "getaccountaddress (Deprecated, will be removed in v0.21. To " + "use this command, start bitcoind with " + "-deprecatedrpc=accounts)"); + } + throw JSONRPCError( + RPC_METHOD_DEPRECATED, + "getaccountaddress is deprecated and will be removed in v0.21. To " + "use this command, start bitcoind with -deprecatedrpc=accounts."); + } + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( @@ -270,7 +284,7 @@ // Parse the label first so we don't generate a key if there's an error std::string label = LabelFromValue(request.params[0]); - bool force = request.strMethod == "getaccountaddress" ? true : false; + bool force = request.strMethod == "getaccountaddress"; if (!request.params[1].isNull()) { force = request.params[1].get_bool(); } @@ -357,6 +371,19 @@ return NullUniValue; } + if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && + request.strMethod == "setaccount") { + if (request.fHelp) { + throw std::runtime_error( + "setaccount (Deprecated, will be removed in v0.21. To use this " + "command, start bitcoind with -deprecatedrpc=accounts)"); + } + throw JSONRPCError( + RPC_METHOD_DEPRECATED, + "setaccount is deprecated and will be removed in v0.21. To use " + "this command, start bitcoind with -deprecatedrpc=accounts."); + } + if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "setlabel \"address\" \"label\"\n" @@ -414,6 +441,18 @@ return NullUniValue; } + if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) { + if (request.fHelp) { + throw std::runtime_error( + "getaccount (Deprecated, will be removed in v0.21. To use this " + "command, start bitcoind with -deprecatedrpc=accounts)"); + } + throw JSONRPCError( + RPC_METHOD_DEPRECATED, + "getaccount is deprecated and will be removed in v0.21. To use " + "this command, start bitcoind with -deprecatedrpc=accounts."); + } + if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getaccount \"address\"\n" @@ -459,6 +498,19 @@ return NullUniValue; } + if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) { + if (request.fHelp) { + throw std::runtime_error( + "getaddressbyaccount (Deprecated, will be removed in v0.21. To " + "use this command, start bitcoind with " + "-deprecatedrpc=accounts)"); + } + throw JSONRPCError(RPC_METHOD_DEPRECATED, + "getaddressesbyaccount is deprecated and will be " + "removed in v0.21. To use this command, start " + "bitcoind with -deprecatedrpc=accounts."); + } + if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getaddressesbyaccount \"account\"\n" @@ -880,6 +932,20 @@ return NullUniValue; } + if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && + request.strMethod == "getreceivedbyaccount") { + if (request.fHelp) { + throw std::runtime_error( + "getreceivedbyaccount (Deprecated, will be removed in v0.21. " + "To use this command, start bitcoind with " + "-deprecatedrpc=accounts)"); + } + throw JSONRPCError(RPC_METHOD_DEPRECATED, + "getreceivedbyaccount is deprecated and will be " + "removed in v0.21. To use this command, start " + "bitcoind with -deprecatedrpc=accounts."); + } + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( @@ -958,7 +1024,11 @@ return NullUniValue; } - if (request.fHelp || request.params.size() > 3) { + if (request.fHelp || + (request.params.size() > 3 && + IsDeprecatedRPCEnabled(gArgs, "accounts")) || + (request.params.size() != 0 && + !IsDeprecatedRPCEnabled(gArgs, "accounts"))) { throw std::runtime_error( "getbalance ( \"account\" minconf include_watchonly )\n" "\nIf account is not specified, returns the server's total " @@ -974,8 +1044,11 @@ "The server total may be different to the balance in the default " "\"\" account.\n" "\nArguments:\n" - "1. \"account\" (string, optional) DEPRECATED. The account " - "string may be given as a\n" + "1. \"account\" (string, optional) DEPRECATED. This argument " + "will be removed in v0.21. \n" + " To use this deprecated argument, start " + "bitcoind with -deprecatedrpc=accounts. The account string may be " + "given as a\n" " specific account name to find the balance " "associated with wallet keys in\n" " a named account, or as the empty string " @@ -996,10 +1069,16 @@ " has resulted in confusing outcomes, so it is " "recommended to avoid passing\n" " this argument.\n" - "2. minconf (numeric, optional, default=1) Only include " - "transactions confirmed at least this many times.\n" - "3. include_watchonly (bool, optional, default=false) Also include " - "balance in watch-only addresses (see 'importaddress')\n" + "2. minconf (numeric, optional, default=1) DEPRECATED. " + "Only valid when an account is specified. This argument will be " + "removed in v0.21. To use this deprecated argument, start bitcoind " + "with -deprecatedrpc=accounts. Only include transactions confirmed " + "at least this many times.\n" + "3. include_watchonly (bool, optional, default=false) DEPRECATED. " + "Only valid when an account is specified. This argument will be " + "removed in v0.21. To use this deprecated argument, start bitcoind " + "with -deprecatedrpc=accounts. Also include balance in watch-only " + "addresses (see 'importaddress')\n" "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + @@ -1018,41 +1097,46 @@ LOCK2(cs_main, pwallet->cs_wallet); - const UniValue &account_value = request.params[0]; - const UniValue &minconf = request.params[1]; - const UniValue &include_watchonly = request.params[2]; + if (IsDeprecatedRPCEnabled(gArgs, "accounts")) { + const UniValue &account_value = request.params[0]; + const UniValue &minconf = request.params[1]; + const UniValue &include_watchonly = request.params[2]; - if (account_value.isNull()) { - if (!minconf.isNull()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - "getbalance minconf option is only currently " - "supported if an account is specified"); - } - if (!include_watchonly.isNull()) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "getbalance include_watchonly option is only currently " - "supported if an account is specified"); + if (account_value.isNull()) { + if (!minconf.isNull()) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "getbalance minconf option is only currently " + "supported if an account is specified"); + } + if (!include_watchonly.isNull()) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "getbalance include_watchonly option is only currently " + "supported if an account is specified"); + } + return ValueFromAmount(pwallet->GetBalance()); } - return ValueFromAmount(pwallet->GetBalance()); - } - const std::string &account_param = account_value.get_str(); - const std::string *account = - account_param != "*" ? &account_param : nullptr; + const std::string &account_param = account_value.get_str(); + const std::string *account = + account_param != "*" ? &account_param : nullptr; - int nMinDepth = 1; - if (!minconf.isNull()) { - nMinDepth = minconf.get_int(); - } + int nMinDepth = 1; + if (!minconf.isNull()) { + nMinDepth = minconf.get_int(); + } - isminefilter filter = ISMINE_SPENDABLE; - if (!include_watchonly.isNull() && include_watchonly.get_bool()) { - filter = filter | ISMINE_WATCH_ONLY; + isminefilter filter = ISMINE_SPENDABLE; + if (!include_watchonly.isNull() && include_watchonly.get_bool()) { + filter = filter | ISMINE_WATCH_ONLY; + } + + return ValueFromAmount( + pwallet->GetLegacyBalance(filter, nMinDepth, account)); } - return ValueFromAmount( - pwallet->GetLegacyBalance(filter, nMinDepth, account)); + return ValueFromAmount(pwallet->GetBalance()); } static UniValue getunconfirmedbalance(const Config &config, @@ -1087,6 +1171,18 @@ return NullUniValue; } + if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) { + if (request.fHelp) { + throw std::runtime_error( + "move (Deprecated, will be removed in v0.21. To use this " + "command, start bitcoind with -deprecatedrpc=accounts)"); + } + throw JSONRPCError( + RPC_METHOD_DEPRECATED, + "move is deprecated and will be removed in v0.21. To use this " + "command, start bitcoind with -deprecatedrpc=accounts."); + } + if (request.fHelp || request.params.size() < 3 || request.params.size() > 5) { throw std::runtime_error( @@ -1269,11 +1365,87 @@ return NullUniValue; } - if (request.fHelp || request.params.size() < 2 || - request.params.size() > 5) { - throw std::runtime_error( - "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf " - "\"comment\" [\"address\",...] )\n" + std::string help_text; + if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) { + help_text = + "sendmany \"\" {\"address\":amount,...} ( minconf \"comment\" " + "[\"address\",...] replaceable conf_target \"estimate_mode\")\n" + "\nSend multiple times. Amounts are double-precision floating " + "point numbers.\n" + "Note that the \"fromaccount\" argument has been removed in " + "v0.20. " + "To use this RPC with a \"fromaccount\" argument, restart\n" + "bitcoind with -deprecatedrpc=accounts\n" + + HelpRequiringPassphrase(pwallet) + + "\n" + "\nArguments:\n" + "1. \"dummy\" (string, required) Must be set to \"\" " + "for backwards compatibility.\n" + "2. \"amounts\" (string, required) A json object with " + "addresses and amounts\n" + " {\n" + " \"address\":amount (numeric or string) The bitcoin " + "address is the key, the numeric amount (can be string) in " + + CURRENCY_UNIT + + " is the value\n" + " ,...\n" + " }\n" + "3. minconf (numeric, optional, default=1) Only " + "use the balance confirmed at least this many times.\n" + "4. \"comment\" (string, optional) A comment\n" + "5. subtractfeefrom (array, optional) A json array with " + "addresses.\n" + " The fee will be equally deducted from " + "the amount of each selected address.\n" + " Those recipients will receive less " + "bitcoins than you enter in their corresponding amount field.\n" + " If no addresses are specified here, " + "the sender pays the fee.\n" + " [\n" + " \"address\" (string) Subtract fee from this " + "address\n" + " ,...\n" + " ]\n" + "\nResult:\n" + "\"txid\" (string) The transaction id for the " + "send. Only 1 transaction is created regardless of \n" + " the number of addresses.\n" + "\nExamples:\n" + "\nSend two amounts to two different addresses:\n" + + HelpExampleCli( + "sendmany", + "\"\" " + "\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01," + "\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + + "\nSend two amounts to two different addresses setting the " + "confirmation and comment:\n" + + HelpExampleCli("sendmany", + "\"\" " + "\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01," + "\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" " + "6 \"testing\"") + + "\nSend two amounts to two different addresses, subtract fee " + "from " + "amount:\n" + + HelpExampleCli( + "sendmany", + "\"\" " + "\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01," + "\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" " + "\"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\"," + "\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("sendmany", + "\"\", " + "{\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01," + "\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, " + "6, \"testing\""); + } else { + help_text = + "sendmany \"\" \"fromaccount\" {\"address\":amount,...} ( " + "minconf " + "\"comment\" [\"address\",...] replaceable conf_target " + "\"estimate_mode\")\n" "\nSend multiple times. Amounts are double-precision floating " "point numbers." + HelpRequiringPassphrase(pwallet) + @@ -1339,7 +1511,12 @@ "\"\", " "\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01," "\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"," - " 6, \"testing\"")); + " 6, \"testing\""); + } + + if (request.fHelp || request.params.size() < 2 || + request.params.size() > 8) { + throw std::runtime_error(help_text); } // Make sure the results are valid at least up to the most recent block @@ -1354,6 +1531,11 @@ "Error: Peer-to-peer functionality missing or disabled"); } + if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && + !request.params[0].get_str().empty()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Dummy value must be set to \"\""); + } std::string strAccount = LabelFromValue(request.params[0]); UniValue sendTo = request.params[1].get_obj(); int nMinDepth = 1; @@ -1781,6 +1963,20 @@ return NullUniValue; } + if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && + request.strMethod == "listreceivedbyaccount") { + if (request.fHelp) { + throw std::runtime_error( + "listreceivedbyaccount (Deprecated, will be removed in v0.21. " + "To use this command, start bitcoind with " + "-deprecatedrpc=accounts)"); + } + throw JSONRPCError(RPC_METHOD_DEPRECATED, + "listreceivedbyaccount is deprecated and will be " + "removed in v0.21. To use this command, start " + "bitcoind with -deprecatedrpc=accounts."); + } + if (request.fHelp || request.params.size() > 3) { throw std::runtime_error( "listreceivedbylabel ( minconf include_empty include_watchonly)\n" @@ -1866,7 +2062,9 @@ (::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) { entry.pushKV("involvesWatchonly", true); } - entry.pushKV("account", strSentAccount); + if (IsDeprecatedRPCEnabled(gArgs, "accounts")) { + entry.pushKV("account", strSentAccount); + } MaybePushAddress(entry, s.destination); entry.pushKV("category", "send"); entry.pushKV("amount", ValueFromAmount(-s.amount)); @@ -1897,7 +2095,9 @@ (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) { entry.pushKV("involvesWatchonly", true); } - entry.pushKV("account", account); + if (IsDeprecatedRPCEnabled(gArgs, "accounts")) { + entry.pushKV("account", account); + } MaybePushAddress(entry, r.destination); if (wtx.IsCoinBase()) { if (wtx.GetDepthInMainChain() < 1) { @@ -1934,7 +2134,9 @@ entry.pushKV("category", "move"); entry.pushKV("time", acentry.nTime); entry.pushKV("amount", ValueFromAmount(acentry.nCreditDebit)); - entry.pushKV("otheraccount", acentry.strOtherAccount); + if (IsDeprecatedRPCEnabled(gArgs, "accounts")) { + entry.pushKV("otheraccount", acentry.strOtherAccount); + } entry.pushKV("comment", acentry.strComment); ret.push_back(entry); } @@ -1948,8 +2150,84 @@ return NullUniValue; } - if (request.fHelp || request.params.size() > 4) { - throw std::runtime_error( + std::string help_text{}; + if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) { + help_text = + "listtransactions (dummy count skip include_watchonly)\n" + "\nReturns up to 'count' most recent transactions skipping the " + "first 'from' transactions for account 'account'.\n" + "Note that the \"account\" argument and \"otheraccount\" return " + "value have been removed in v0.20. To use this RPC with an " + "\"account\" argument, restart\n" + "bitcoind with -deprecatedrpc=accounts\n" + "\nArguments:\n" + "1. \"dummy\" (string, optional) If set, should be \"*\" for " + "backwards compatibility.\n" + "2. count (numeric, optional, default=10) The number of " + "transactions to return\n" + "3. skip (numeric, optional, default=0) The number of " + "transactions to skip\n" + "4. include_watchonly (bool, optional, default=false) Include " + "transactions to watch-only addresses (see 'importaddress')\n" + "\nResult:\n" + "[\n" + " {\n" + " \"address\":\"address\", (string) The bitcoin address " + "of " + "the transaction.\n" + " \"category\":\"send|receive\", (string) The transaction " + "category.\n" + " \"amount\": x.xxx, (numeric) The amount in " + + CURRENCY_UNIT + + ". This is negative for the 'send' category, and is positive\n" + " for the 'receive' " + "category,\n" + " \"label\": \"label\", (string) A comment for the " + "address/transaction, if any\n" + " \"vout\": n, (numeric) the vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee " + "in " + + CURRENCY_UNIT + + ". This is negative and only available for the \n" + " 'send' category of " + "transactions.\n" + " \"confirmations\": n, (numeric) The number of " + "confirmations for the transaction. Negative confirmations " + "indicate the\n" + " transaction conflicts " + "with the block chain\n" + " \"trusted\": xxx, (bool) Whether we consider the " + "outputs of this unconfirmed transaction safe to spend.\n" + " \"blockhash\": \"hashvalue\", (string) The block hash " + "containing the transaction.\n" + " \"blockindex\": n, (numeric) The index of the " + "transaction in the block that includes it.\n" + " \"blocktime\": xxx, (numeric) The block time in " + "seconds since epoch (1 Jan 1970 GMT).\n" + " \"txid\": \"transactionid\", (string) The transaction id.\n" + " \"time\": xxx, (numeric) The transaction time in " + "seconds since epoch (midnight Jan 1 1970 GMT).\n" + " \"timereceived\": xxx, (numeric) The time received in " + "seconds since epoch (midnight Jan 1 1970 GMT).\n" + " \"comment\": \"...\", (string) If a comment is " + "associated with the transaction.\n" + " \"abandoned\": xxx (bool) 'true' if the transaction " + "has been abandoned (inputs are respendable). Only available for " + "the \n" + " 'send' category of " + "transactions.\n" + " }\n" + "]\n" + + "\nExamples:\n" + "\nList the most recent 10 transactions in the systems\n" + + HelpExampleCli("listtransactions", "") + + "\nList transactions 100 to 120\n" + + HelpExampleCli("listtransactions", "\"*\" 20 100") + + "\nAs a json rpc call\n" + + HelpExampleRpc("listtransactions", "\"*\", 20, 100"); + } else { + help_text = "listtransactions ( \"account\" count skip include_watchonly)\n" "\nReturns up to 'count' most recent transactions skipping the " "first 'from' transactions for account 'account'.\n" @@ -2026,7 +2304,8 @@ "'receive' category of transactions.\n" " \"comment\": \"...\", (string) If a comment is " "associated with the transaction.\n" - " \"otheraccount\": \"accountname\", (string) DEPRECATED. For " + " \"otheraccount\": \"accountname\", (string) DEPRECATED. This " + "field will be removed in v0.21. For " "the 'move' category of transactions, the account the funds came \n" " from (for receiving " "funds, positive amounts), or went to (for sending funds,\n" @@ -2045,7 +2324,10 @@ "\nList transactions 100 to 120\n" + HelpExampleCli("listtransactions", "\"*\" 20 100") + "\nAs a json rpc call\n" + - HelpExampleRpc("listtransactions", "\"*\", 20, 100")); + HelpExampleRpc("listtransactions", "\"*\", 20, 100"); + } + if (request.fHelp || request.params.size() > 4) { + throw std::runtime_error(help_text); } // Make sure the results are valid at least up to the most recent block @@ -2057,6 +2339,10 @@ std::string strAccount = "*"; if (!request.params[0].isNull()) { strAccount = request.params[0].get_str(); + if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && strAccount != "*") { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Dummy value must be set to \"*\""); + } } int nCount = 10; @@ -2091,9 +2377,11 @@ if (pwtx != nullptr) { ListTransactions(pwallet, *pwtx, strAccount, 0, true, ret, filter); } - CAccountingEntry *const pacentry = (*it).second.second; - if (pacentry != nullptr) { - AcentryToJSON(*pacentry, strAccount, ret); + if (IsDeprecatedRPCEnabled(gArgs, "accounts")) { + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != nullptr) { + AcentryToJSON(*pacentry, strAccount, ret); + } } if ((int)ret.size() >= (nCount + nFrom)) { @@ -2143,6 +2431,19 @@ return NullUniValue; } + if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) { + if (request.fHelp) { + throw std::runtime_error("listaccounts (Deprecated, will be " + "removed in v0.21. To use " + "this command, start bitcoind with " + "-deprecatedrpc=accounts)"); + } + throw JSONRPCError( + RPC_METHOD_DEPRECATED, + "listaccounts is deprecated and will be removed in v0.21. To " + "use this command, start bitcoind with -deprecatedrpc=accounts."); + } + if (request.fHelp || request.params.size() > 2) { throw std::runtime_error( "listaccounts ( minconf include_watchonly)\n" @@ -2276,7 +2577,9 @@ "\nResult:\n" "{\n" " \"transactions\": [\n" - " \"account\":\"accountname\", (string) DEPRECATED. The " + " \"account\":\"accountname\", (string) DEPRECATED. This " + "field will be removed in v0.21. To see this deprecated field, " + "start bitcoind with -deprecatedrpc=accounts. The " "account name associated with the transaction. Will be \"\" for " "the default account.\n" " \"address\":\"address\", (string) The bitcoin address of " @@ -2498,8 +2801,10 @@ " \"details\" : [\n" " {\n" " \"account\" : \"accountname\", (string) DEPRECATED. " - "The account name involved in the transaction, can be \"\" for the " - "default account.\n" + "This field will be removed in a v0.21. To see this deprecated " + "field, start bitcoind with -deprecatedrpc=accounts. The account " + "name involved in the transaction, can be \"\" for the default " + "account.\n" " \"address\" : \"address\", (string) The bitcoin " "address involved in the transaction\n" " \"category\" : \"send|receive\", (string) The category, " @@ -3663,8 +3968,10 @@ " \"address\" : \"address\", (string) the bitcoin address\n" " \"label\" : \"label\", (string) The associated label, " "or \"\" for the default label\n" - " \"account\" : \"account\", (string) DEPRECATED. Backwards " - "compatible alias for label.\n" + " \"account\" : \"account\", (string) DEPRECATED. This field " + "will be removed in v0.21. To see this deprecated field, start " + "bitcoind with -deprecatedrpc=accounts. The associated account, or " + "\"\" for the default account\n" " \"scriptPubKey\" : \"key\", (string) the script key\n" " \"amount\" : x.xxx, (numeric) the transaction output " "amount in " + @@ -3801,7 +4108,10 @@ if (pwallet->mapAddressBook.count(address)) { entry.pushKV("label", pwallet->mapAddressBook[address].name); - entry.pushKV("account", pwallet->mapAddressBook[address].name); + if (IsDeprecatedRPCEnabled(gArgs, "accounts")) { + entry.pushKV("account", + pwallet->mapAddressBook[address].name); + } } if (scriptPubKey.IsPayToScriptHash()) { @@ -4223,9 +4533,9 @@ WalletRescanReserver reserver(pwallet); if (!reserver.reserve()) { - throw JSONRPCError( - RPC_WALLET_ERROR, - "Wallet is currently rescanning. Abort existing rescan or wait."); + throw JSONRPCError(RPC_WALLET_ERROR, + "Wallet is currently rescanning. Abort existing " + "rescan or wait."); } CBlockIndex *pindexStart = nullptr; @@ -4456,7 +4766,11 @@ "(\"ismine\", \"iswatchonly\", \"account\").\n" " \"iscompressed\" : true|false, (boolean) If the address is " "compressed\n" - " \"account\" : \"account\" (string) The account " + " \"label\" : \"label\" (string) The label associated " + "with the address, \"\" is the default account\n" + " \"account\" : \"account\" (string) DEPRECATED. This " + "field will be removed in v0.21. To see this deprecated field, " + "start bitcoind with -deprecatedrpc=accounts. The account " "associated with the address, \"\" is the default account\n" " \"timestamp\" : timestamp, (number, optional) The creation " "time of the key if available in seconds since epoch (Jan 1 1970 " @@ -4507,7 +4821,10 @@ UniValue detail = DescribeWalletAddress(pwallet, dest); ret.pushKVs(detail); if (pwallet->mapAddressBook.count(dest)) { - ret.pushKV("account", pwallet->mapAddressBook[dest].name); + ret.pushKV("label", pwallet->mapAddressBook[dest].name); + if (IsDeprecatedRPCEnabled(gArgs, "accounts")) { + ret.pushKV("account", pwallet->mapAddressBook[dest].name); + } } const CKeyMetadata *meta = nullptr; CKeyID key_id = GetKeyForDestination(*pwallet, dest); @@ -4762,11 +5079,9 @@ { "wallet", "keypoolrefill", keypoolrefill, {"newsize"} }, { "wallet", "listaddressgroupings", listaddressgroupings, {} }, { "wallet", "listlockunspent", listlockunspent, {} }, - { "wallet", "listreceivedbylabel", listreceivedbylabel, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "listreceivedbyaccount", listreceivedbylabel, {"minconf","include_empty","include_watchonly"} }, { "wallet", "listreceivedbyaddress", listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} }, { "wallet", "listsinceblock", listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, - { "wallet", "listtransactions", listtransactions, {"account","count","skip","include_watchonly"} }, + { "wallet", "listtransactions", listtransactions, {"account|dummy","count","skip","include_watchonly"} }, { "wallet", "listunspent", listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, { "wallet", "listwallets", listwallets, {} }, { "wallet", "loadwallet", loadwallet, {"filename"} }, @@ -4774,7 +5089,7 @@ { "wallet", "rescanblockchain", rescanblockchain, {"start_height", "stop_height"} }, { "wallet", "sethdseed", sethdseed, {"newkeypool","seed"} }, { "wallet", "sendfrom", sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, - { "wallet", "sendmany", sendmany, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} }, + { "wallet", "sendmany", sendmany, {"fromaccount|dummy","amounts","minconf","comment","subtractfeefrom"} }, { "wallet", "sendtoaddress", sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount"} }, { "wallet", "settxfee", settxfee, {"amount"} }, { "wallet", "signmessage", signmessage, {"address","message"} }, diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py --- a/test/functional/rpc_deprecated.py +++ b/test/functional/rpc_deprecated.py @@ -4,6 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test deprecation of RPC calls.""" from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_raises_rpc_error class DeprecatedRpcTest(BitcoinTestFramework): @@ -11,7 +12,7 @@ self.num_nodes = 2 self.setup_clean_chain = True self.extra_args = [ - [], []] + [], ["-deprecatedrpc=accounts"]] def run_test(self): # This test should be used to verify correct behaviour of deprecated @@ -20,7 +21,95 @@ # self.log.info("Make sure that -deprecatedrpc=createmultisig allows it to take addresses") # assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, [self.nodes[0].getnewaddress()]) # self.nodes[1].createmultisig(1, [self.nodes[1].getnewaddress()]) - self.log.info("No tested deprecated RPC methods") + + self.log.info("Test accounts deprecation") + # The following account RPC methods are deprecated: + # - getaccount + # - getaccountaddress + # - getaddressesbyaccount + # - getreceivedbyaccount + # - listaccouts + # - listreceivedbyaccount + # - move + # - setaccount + # + # The following 'label' RPC methods are usable both with and without the + # -deprecatedrpc=accounts switch enabled. + # - getlabeladdress + # - getaddressesbylabel + # - getreceivedbylabel + # - listlabels + # - listreceivedbylabel + # - setlabel + # + address0 = self.nodes[0].getnewaddress() + self.nodes[0].generatetoaddress(101, address0) + address1 = self.nodes[1].getnewaddress() + self.nodes[1].generatetoaddress(101, address1) + + self.log.info("- getaccount") + assert_raises_rpc_error(-32, "getaccount is deprecated", + self.nodes[0].getaccount, address0) + self.nodes[1].getaccount(address1) + + self.log.info("- setaccount") + assert_raises_rpc_error(-32, "setaccount is deprecated", + self.nodes[0].setaccount, address0, "label0") + self.nodes[1].setaccount(address1, "label1") + + self.log.info("- setlabel") + self.nodes[0].setlabel(address0, "label0") + self.nodes[1].setlabel(address1, "label1") + + self.log.info("- getaccountaddress") + assert_raises_rpc_error(-32, "getaccountaddress is deprecated", + self.nodes[0].getaccountaddress, "label0") + self.nodes[1].getaccountaddress("label1") + + self.log.info("- getlabeladdress") + self.nodes[0].getlabeladdress("label0") + self.nodes[1].getlabeladdress("label1") + + self.log.info("- getaddressesbyaccount") + assert_raises_rpc_error(-32, "getaddressesbyaccount is deprecated", + self.nodes[0].getaddressesbyaccount, "label0") + self.nodes[1].getaddressesbyaccount("label1") + + self.log.info("- getaddressesbylabel") + self.nodes[0].getaddressesbylabel("label0") + self.nodes[1].getaddressesbylabel("label1") + + self.log.info("- getreceivedbyaccount") + assert_raises_rpc_error(-32, "getreceivedbyaccount is deprecated", + self.nodes[0].getreceivedbyaccount, "label0") + self.nodes[1].getreceivedbyaccount("label1") + + self.log.info("- getreceivedbylabel") + self.nodes[0].getreceivedbylabel("label0") + self.nodes[1].getreceivedbylabel("label1") + + self.log.info("- listaccounts") + assert_raises_rpc_error(-32, "listaccounts is deprecated", + self.nodes[0].listaccounts) + self.nodes[1].listaccounts() + + self.log.info("- listlabels") + self.nodes[0].listlabels() + self.nodes[1].listlabels() + + self.log.info("- listreceivedbyaccount") + assert_raises_rpc_error(-32, "listreceivedbyaccount is deprecated", + self.nodes[0].listreceivedbyaccount) + self.nodes[1].listreceivedbyaccount() + + self.log.info("- listreceivedbylabel") + self.nodes[0].listreceivedbylabel() + self.nodes[1].listreceivedbylabel() + + self.log.info("- move") + assert_raises_rpc_error(-32, "move is deprecated", + self.nodes[0].move, "label0", "label0b", 10) + self.nodes[1].move("label1", "label1b", 10) if __name__ == '__main__': diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -25,9 +25,10 @@ def set_test_params(self): self.num_nodes = 4 self.setup_clean_chain = True + self.extra_args = [['-deprecatedrpc=accounts']] * 4 def setup_network(self): - self.add_nodes(4) + self.add_nodes(4, self.extra_args) self.start_node(0) self.start_node(1) self.start_node(2) @@ -415,9 +416,12 @@ self.log.info("check " + m) self.stop_nodes() # set lower ancestor limit for later - self.start_node(0, [m, "-limitancestorcount=" + str(chainlimit)]) - self.start_node(1, [m, "-limitancestorcount=" + str(chainlimit)]) - self.start_node(2, [m, "-limitancestorcount=" + str(chainlimit)]) + self.start_node(0, [m, "-deprecatedrpc=accounts", + "-limitancestorcount="+str(chainlimit)]) + self.start_node(1, [m, "-deprecatedrpc=accounts", + "-limitancestorcount="+str(chainlimit)]) + self.start_node(2, [m, "-deprecatedrpc=accounts", + "-limitancestorcount="+str(chainlimit)]) if m == '-reindex': # reindex will leave rpc warm up "early"; Wait for it to finish wait_until(lambda: [block_count] * 3 == @@ -475,7 +479,7 @@ # Double chain limit but require combining inputs, so we pass SelectCoinsMinConf self.stop_node(0) self.start_node(0, extra_args=[ - "-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)]) + "-deprecatedrpc=accounts", "-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)]) # wait for loadmempool timeout = 10 diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py --- a/test/functional/wallet_import_rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -131,7 +131,8 @@ self.num_nodes = 2 + len(IMPORT_NODES) def setup_network(self): - extra_args = [[] for _ in range(self.num_nodes)] + extra_args = [['-deprecatedrpc=accounts'] + for _ in range(self.num_nodes)] for i, import_node in enumerate(IMPORT_NODES, 2): if import_node.prune: extra_args[i] += ["-prune=1"] diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py --- a/test/functional/wallet_importprunedfunds.py +++ b/test/functional/wallet_importprunedfunds.py @@ -14,6 +14,7 @@ def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 + self.extra_args = [['-deprecatedrpc=accounts']] * 2 def run_test(self): self.log.info("Mining blocks...") diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py --- a/test/functional/wallet_keypool_topup.py +++ b/test/functional/wallet_keypool_topup.py @@ -25,7 +25,8 @@ def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 - self.extra_args = [[], ['-keypool=100']] + self.extra_args = [['-deprecatedrpc=accounts'], + ['-deprecatedrpc=accounts', '-keypool=100']] def run_test(self): wallet_path = os.path.join( diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py --- a/test/functional/wallet_labels.py +++ b/test/functional/wallet_labels.py @@ -6,26 +6,39 @@ RPCs tested are: - getlabeladdress - - getaddressesbyaccount + - getaddressesbyaccount/getaddressesbylabel - listaddressgroupings - setlabel - sendfrom (with account arguments) - move (with account arguments) + +Run the test twice - once using the accounts API and once using the labels API. +The accounts API test can be removed in V0.21. """ from collections import defaultdict from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal +from test_framework.util import assert_equal, assert_raises_rpc_error class WalletLabelsTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 1 - self.extra_args = [[]] + self.num_nodes = 2 + self.extra_args = [['-deprecatedrpc=accounts'], []] + + def setup_network(self): + """Don't connect nodes.""" + self.setup_nodes() def run_test(self): - node = self.nodes[0] + """Run the test twice - once using the accounts API and once using the labels API.""" + self.log.info("Test accounts API") + self._run_subtest(True, self.nodes[0]) + self.log.info("Test labels API") + self._run_subtest(False, self.nodes[1]) + + def _run_subtest(self, accounts_api, node): # Check that there's no UTXO on any of the nodes assert_equal(len(node.listunspent()), 0) @@ -74,7 +87,8 @@ # Create labels and make sure subsequent label API calls # recognize the label/address associations. - labels = [Label(name) for name in ("a", "b", "c", "d", "e")] + labels = [Label(name, accounts_api) + for name in ("a", "b", "c", "d", "e")] for label in labels: label.add_receive_address( node.getlabeladdress(label=label.name, force=True)) @@ -99,21 +113,23 @@ # Check that sendfrom label reduces listaccounts balances. for i, label in enumerate(labels): - to_label = labels[(i+1) % len(labels)] + to_label = labels[(i + 1) % len(labels)] node.sendfrom(label.name, to_label.receive_address, amount_to_send) node.generate(1) for label in labels: label.add_receive_address(node.getlabeladdress(label.name)) label.verify(node) assert_equal(node.getreceivedbylabel(label.name), 2) - node.move(label.name, "", node.getbalance(label.name)) + if accounts_api: + node.move(label.name, "", node.getbalance(label.name)) label.verify(node) node.generate(101) expected_account_balances = {"": 5200 + fee} for label in labels: expected_account_balances[label.name] = 0 - assert_equal(node.listaccounts(), expected_account_balances) - assert_equal(node.getbalance(""), 5200 + fee) + if accounts_api: + assert_equal(node.listaccounts(), expected_account_balances) + assert_equal(node.getbalance(""), 5200 + fee) # Check that setlabel can assign a label to a new unused address. for label in labels: @@ -121,7 +137,11 @@ node.setlabel(address, label.name) label.add_address(address) label.verify(node) - assert(address not in node.getaddressesbyaccount("")) + if accounts_api: + assert(address not in node.getaddressesbyaccount("")) + else: + assert_raises_rpc_error(-11, "No addresses with label", + node.getaddressesbylabel, "") # Check that addmultisigaddress can assign labels. for label in labels: @@ -135,8 +155,9 @@ label.verify(node) node.sendfrom("", multisig_address, 50) node.generate(101) - for label in labels: - assert_equal(node.getbalance(label.name), 50) + if accounts_api: + for label in labels: + assert_equal(node.getbalance(label.name), 50) # Check that setlabel can change the label of an address from a # different label. @@ -156,9 +177,10 @@ class Label: - def __init__(self, name): + def __init__(self, name, accounts_api): # Label name self.name = name + self.accounts_api = accounts_api # Current receiving address associated with this label. self.receive_address = None # List of all addresses assigned with this label @@ -184,13 +206,17 @@ node.getaddressinfo(address)['labels'][0], {"name": self.name, "purpose": self.purpose[address]}) - assert_equal(node.getaccount(address), self.name) + if self.accounts_api: + assert_equal(node.getaccount(address), self.name) + else: + assert_equal(node.getaddressinfo(address)['label'], self.name) assert_equal( node.getaddressesbylabel(self.name), {address: {"purpose": self.purpose[address]} for address in self.addresses}) - assert_equal( - set(node.getaddressesbyaccount(self.name)), set(self.addresses)) + if self.accounts_api: + assert_equal(set(node.getaddressesbyaccount( + self.name)), set(self.addresses)) def change_label(node, address, old_label, new_label): diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py --- a/test/functional/wallet_listreceivedby.py +++ b/test/functional/wallet_listreceivedby.py @@ -18,6 +18,7 @@ class ReceivedByTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + self.extra_args = [['-deprecatedrpc=accounts']] * 2 def run_test(self): # Generate block to get out of IBD diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py --- a/test/functional/wallet_listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py @@ -11,7 +11,9 @@ def set_test_params(self): self.num_nodes = 4 self.setup_clean_chain = True - self.extra_args = [["-noparkdeepreorg"], ["-noparkdeepreorg"], [], []] + self.extra_args = [["-noparkdeepreorg", "-deprecatedrpc=accounts"], + ["-noparkdeepreorg", "-deprecatedrpc=accounts"], + ["-deprecatedrpc=accounts"], ["-deprecatedrpc=accounts"]] def run_test(self): self.nodes[2].generate(101) diff --git a/test/functional/rpc_listtransactions.py b/test/functional/wallet_listtransactions.py rename from test/functional/rpc_listtransactions.py rename to test/functional/wallet_listtransactions.py --- a/test/functional/rpc_listtransactions.py +++ b/test/functional/wallet_listtransactions.py @@ -13,6 +13,7 @@ class ListTransactionsTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + self.extra_args = [['-deprecatedrpc=accounts']] * 2 def run_test(self): # Leave IBD diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py --- a/test/functional/wallet_txn_clone.py +++ b/test/functional/wallet_txn_clone.py @@ -16,7 +16,9 @@ class TxnMallTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 - self.extra_args = [["-noparkdeepreorg"], ["-noparkdeepreorg"], [], []] + self.extra_args = [["-noparkdeepreorg", "-deprecatedrpc=accounts"], + ["-noparkdeepreorg", "-deprecatedrpc=accounts"], + ["-deprecatedrpc=accounts"], ["-deprecatedrpc=accounts"]] def add_options(self, parser): parser.add_argument("--mineblock", dest="mine_block", default=False, action="store_true", diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py --- a/test/functional/wallet_txn_doublespend.py +++ b/test/functional/wallet_txn_doublespend.py @@ -18,7 +18,9 @@ class TxnMallTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 - self.extra_args = [["-noparkdeepreorg"], ["-noparkdeepreorg"], [], []] + self.extra_args = [["-noparkdeepreorg", "-deprecatedrpc=accounts"], + ["-noparkdeepreorg", "-deprecatedrpc=accounts"], + ["-deprecatedrpc=accounts"], ["-deprecatedrpc=accounts"]] def add_options(self, parser): parser.add_argument("--mineblock", dest="mine_block", default=False, action="store_true",