Merge #11536: Rename account to label where appropriate

d2527bd Rename wallet_accounts.py test (Russell Yanofsky)
045eeb8 Rename account to label where appropriate (Russell Yanofsky)

Pull request description:

  Rename account to label where appropriate

  This change only updates strings and adds RPC aliases, but should simplify the implementation of address labels in https://github.com/bitcoin/bitcoin/pull/7729, by getting renaming out of the way and letting that change focus on semantics.

  The difference between accounts and labels is that labels apply only to addresses, while accounts apply to both addresses and transactions (transactions have "from" and "to" accounts). The code associating accounts with transactions is clumsy and unreliable so we would like get rid of it.

  ---

  There is a rebased version of #7729 atop this PR at https://github.com/ryanofsky/bitcoin/commits/pr/label, see https://github.com/bitcoin/bitcoin/pull/7729#issuecomment-338417139.

Tree-SHA512: b3f934e612922d6290f50137f8ba71ddfaea4485713c7d97e89400a8b73b09b254f9186dffa462c77f5847721f5af9852b5572ade5443d8ee95dd150b3edb7ff
This commit is contained in:
Wladimir J. van der Laan 2018-03-22 21:14:34 +01:00
commit cead84b72d
No known key found for this signature in database
GPG Key ID: 1E4AED62986CD25D
12 changed files with 378 additions and 356 deletions

View File

@ -63,6 +63,16 @@ RPC changes
- The `createrawtransaction` RPC will now accept an array or dictionary (kept for compatibility) for the `outputs` parameter. This means the order of transaction outputs can be specified by the client.
- The `fundrawtransaction` RPC will reject the previously deprecated `reserveChangeKey` option.
- Wallet `getnewaddress` and `addmultisigaddress` RPC `account` named
parameters have been renamed to `label` with no change in behavior.
- Wallet `getlabeladdress`, `getreceivedbylabel`, `listreceivedbylabel`, and
`setlabel` RPCs have been added to replace `getaccountaddress`,
`getreceivedbyaccount`, `listreceivedbyaccount`, and `setaccount` RPCs,
which are now deprecated. There is no change in behavior between the
new RPCs and deprecated RPCs.
- Wallet `listreceivedbylabel`, `listreceivedbyaccount` and `listunspent` RPCs
add `label` fields to returned JSON objects that previously only had
`account` fields.
External wallet files
---------------------

View File

@ -639,8 +639,6 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, const SendCoinsRecipient& r
payment.add_transactions(transaction.data(), transaction.size());
// Create a new refund address, or re-use:
QString account = tr("Refund from %1").arg(recipient.authenticatedMerchant);
std::string strAccount = account.toStdString();
CPubKey newKey;
if (wallet->GetKeyFromPool(newKey)) {
// BIP70 requests encode the scriptPubKey directly, so we are not restricted to address
@ -651,7 +649,8 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, const SendCoinsRecipient& r
const OutputType change_type = wallet->m_default_change_type != OutputType::NONE ? wallet->m_default_change_type : wallet->m_default_address_type;
wallet->LearnRelatedScripts(newKey, change_type);
CTxDestination dest = GetDestinationForKey(newKey, change_type);
wallet->SetAddressBook(dest, strAccount, "refund");
std::string label = tr("Refund from %1").arg(recipient.authenticatedMerchant).toStdString();
wallet->SetAddressBook(dest, label, "refund");
CScript s = GetScriptForDestination(dest);
payments::Output* refund_to = payment.add_refund_to();

View File

@ -40,6 +40,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "settxfee", 0, "amount" },
{ "getreceivedbyaddress", 1, "minconf" },
{ "getreceivedbyaccount", 1, "minconf" },
{ "getreceivedbylabel", 1, "minconf" },
{ "listreceivedbyaddress", 0, "minconf" },
{ "listreceivedbyaddress", 1, "include_empty" },
{ "listreceivedbyaddress", 2, "include_watchonly" },
@ -47,6 +48,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listreceivedbyaccount", 0, "minconf" },
{ "listreceivedbyaccount", 1, "include_empty" },
{ "listreceivedbyaccount", 2, "include_watchonly" },
{ "listreceivedbylabel", 0, "minconf" },
{ "listreceivedbylabel", 1, "include_empty" },
{ "listreceivedbylabel", 2, "include_watchonly" },
{ "getbalance", 1, "minconf" },
{ "getbalance", 2, "include_watchonly" },
{ "getblockhash", 0, "height" },

View File

@ -76,7 +76,7 @@ enum RPCErrorCode
//! Wallet errors
RPC_WALLET_ERROR = -4, //!< Unspecified problem with wallet (key not found etc.)
RPC_WALLET_INSUFFICIENT_FUNDS = -6, //!< Not enough funds in wallet or account
RPC_WALLET_INVALID_ACCOUNT_NAME = -11, //!< Invalid account name
RPC_WALLET_INVALID_LABEL_NAME = -11, //!< Invalid label name
RPC_WALLET_KEYPOOL_RAN_OUT = -12, //!< Keypool ran out, call keypoolrefill first
RPC_WALLET_UNLOCK_NEEDED = -13, //!< Enter the wallet passphrase with walletpassphrase first
RPC_WALLET_PASSPHRASE_INCORRECT = -14, //!< The wallet passphrase entered was incorrect
@ -85,6 +85,9 @@ enum RPCErrorCode
RPC_WALLET_ALREADY_UNLOCKED = -17, //!< Wallet is already unlocked
RPC_WALLET_NOT_FOUND = -18, //!< Invalid wallet specified
RPC_WALLET_NOT_SPECIFIED = -19, //!< No wallet specified (error when there are multiple wallets loaded)
//! Backwards compatible aliases
RPC_WALLET_INVALID_ACCOUNT_NAME = RPC_WALLET_INVALID_LABEL_NAME,
};
UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);

View File

@ -124,12 +124,12 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
entry.pushKV(item.first, item.second);
}
std::string AccountFromValue(const UniValue& value)
std::string LabelFromValue(const UniValue& value)
{
std::string strAccount = value.get_str();
if (strAccount == "*")
throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name");
return strAccount;
std::string label = value.get_str();
if (label == "*")
throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
return label;
}
UniValue getnewaddress(const JSONRPCRequest& request)
@ -141,12 +141,12 @@ UniValue getnewaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
"getnewaddress ( \"account\" \"address_type\" )\n"
"getnewaddress ( \"label\" \"address_type\" )\n"
"\nReturns a new Bitcoin address for receiving payments.\n"
"If 'account' is specified (DEPRECATED), it is added to the address book \n"
"so payments received with the address will be credited to 'account'.\n"
"If 'label' is specified, it is added to the address book \n"
"so payments received with the address will be associated with 'label'.\n"
"\nArguments:\n"
"1. \"account\" (string, optional) DEPRECATED. The account name for the address to be linked to. If not provided, the default account \"\" is used. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created if there is no account by the given name.\n"
"1. \"label\" (string, optional) The label name for the address to be linked to. If not provided, the default label \"\" is used. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name.\n"
"2. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -addresstype.\n"
"\nResult:\n"
"\"address\" (string) The new bitcoin address\n"
@ -157,10 +157,10 @@ UniValue getnewaddress(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
// Parse the account first so we don't generate a key if there's an error
std::string strAccount;
// Parse the label first so we don't generate a key if there's an error
std::string label;
if (!request.params[0].isNull())
strAccount = AccountFromValue(request.params[0]);
label = LabelFromValue(request.params[0]);
OutputType output_type = pwallet->m_default_address_type;
if (!request.params[1].isNull()) {
@ -182,23 +182,23 @@ UniValue getnewaddress(const JSONRPCRequest& request)
pwallet->LearnRelatedScripts(newKey, output_type);
CTxDestination dest = GetDestinationForKey(newKey, output_type);
pwallet->SetAddressBook(dest, strAccount, "receive");
pwallet->SetAddressBook(dest, label, "receive");
return EncodeDestination(dest);
}
CTxDestination GetAccountDestination(CWallet* const pwallet, std::string strAccount, bool bForceNew=false)
CTxDestination GetLabelDestination(CWallet* const pwallet, const std::string& label, bool bForceNew=false)
{
CTxDestination dest;
if (!pwallet->GetAccountDestination(dest, strAccount, bForceNew)) {
if (!pwallet->GetLabelDestination(dest, label, bForceNew)) {
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
}
return dest;
}
UniValue getaccountaddress(const JSONRPCRequest& request)
UniValue getlabeladdress(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@ -207,27 +207,27 @@ UniValue getaccountaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"getaccountaddress \"account\"\n"
"\nDEPRECATED. Returns the current Bitcoin address for receiving payments to this account.\n"
"getlabeladdress \"label\"\n"
"\nReturns the current Bitcoin address for receiving payments to this label.\n"
"\nArguments:\n"
"1. \"account\" (string, required) The account name for the address. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created and a new address created if there is no account by the given name.\n"
"1. \"label\" (string, required) The label name for the address. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created and a new address created if there is no label by the given name.\n"
"\nResult:\n"
"\"address\" (string) The account bitcoin address\n"
"\"address\" (string) The label bitcoin address\n"
"\nExamples:\n"
+ HelpExampleCli("getaccountaddress", "")
+ HelpExampleCli("getaccountaddress", "\"\"")
+ HelpExampleCli("getaccountaddress", "\"myaccount\"")
+ HelpExampleRpc("getaccountaddress", "\"myaccount\"")
+ HelpExampleCli("getlabeladdress", "")
+ HelpExampleCli("getlabeladdress", "\"\"")
+ HelpExampleCli("getlabeladdress", "\"mylabel\"")
+ HelpExampleRpc("getlabeladdress", "\"mylabel\"")
);
LOCK2(cs_main, pwallet->cs_wallet);
// Parse the account first so we don't generate a key if there's an error
std::string strAccount = AccountFromValue(request.params[0]);
// Parse the label first so we don't generate a key if there's an error
std::string label = LabelFromValue(request.params[0]);
UniValue ret(UniValue::VSTR);
ret = EncodeDestination(GetAccountDestination(pwallet, strAccount));
ret = EncodeDestination(GetLabelDestination(pwallet, label));
return ret;
}
@ -281,7 +281,7 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request)
}
UniValue setaccount(const JSONRPCRequest& request)
UniValue setlabel(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@ -290,14 +290,14 @@ UniValue setaccount(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
"setaccount \"address\" \"account\"\n"
"\nDEPRECATED. Sets the account associated with the given address.\n"
"setlabel \"address\" \"label\"\n"
"\nSets the label associated with the given address.\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to be associated with an account.\n"
"2. \"account\" (string, required) The account to assign the address to.\n"
"1. \"address\" (string, required) The bitcoin address to be associated with a label.\n"
"2. \"label\" (string, required) The label to assign the address to.\n"
"\nExamples:\n"
+ HelpExampleCli("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"")
+ HelpExampleRpc("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"")
+ HelpExampleCli("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"")
+ HelpExampleRpc("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"")
);
LOCK2(cs_main, pwallet->cs_wallet);
@ -307,23 +307,23 @@ UniValue setaccount(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
}
std::string strAccount;
std::string label;
if (!request.params[1].isNull())
strAccount = AccountFromValue(request.params[1]);
label = LabelFromValue(request.params[1]);
// Only add the account if the address is yours.
// Only add the label if the address is yours.
if (IsMine(*pwallet, dest)) {
// Detect when changing the account of an address that is the 'unused current key' of another account:
// Detect when changing the label of an address that is the 'unused current key' of another label:
if (pwallet->mapAddressBook.count(dest)) {
std::string strOldAccount = pwallet->mapAddressBook[dest].name;
if (dest == GetAccountDestination(pwallet, strOldAccount)) {
GetAccountDestination(pwallet, strOldAccount, true);
std::string old_label = pwallet->mapAddressBook[dest].name;
if (dest == GetLabelDestination(pwallet, old_label)) {
GetLabelDestination(pwallet, old_label, true);
}
}
pwallet->SetAddressBook(dest, strAccount, "receive");
pwallet->SetAddressBook(dest, label, "receive");
}
else
throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address");
throw JSONRPCError(RPC_MISC_ERROR, "setlabel can only be used with own address");
return NullUniValue;
}
@ -390,7 +390,7 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount = AccountFromValue(request.params[0]);
std::string strAccount = LabelFromValue(request.params[0]);
// Find all addresses that have the given account
UniValue ret(UniValue::VARR);
@ -552,7 +552,7 @@ UniValue listaddressgroupings(const JSONRPCRequest& request)
" [\n"
" \"address\", (string) The bitcoin address\n"
" amount, (numeric) The amount in " + CURRENCY_UNIT + "\n"
" \"account\" (string, optional) DEPRECATED. The account\n"
" \"label\" (string, optional) The label\n"
" ]\n"
" ,...\n"
" ]\n"
@ -720,7 +720,7 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request)
}
UniValue getreceivedbyaccount(const JSONRPCRequest& request)
UniValue getreceivedbylabel(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@ -729,22 +729,22 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
"getreceivedbyaccount \"account\" ( minconf )\n"
"\nDEPRECATED. Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.\n"
"getreceivedbylabel \"label\" ( minconf )\n"
"\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n"
"\nArguments:\n"
"1. \"account\" (string, required) The selected account, may be the default account using \"\".\n"
"1. \"label\" (string, required) The selected label, may be the default label using \"\".\n"
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this label.\n"
"\nExamples:\n"
"\nAmount received by the default account with at least 1 confirmation\n"
+ HelpExampleCli("getreceivedbyaccount", "\"\"") +
"\nAmount received at the tabby account including unconfirmed amounts with zero confirmations\n"
+ HelpExampleCli("getreceivedbyaccount", "\"tabby\" 0") +
"\nAmount received by the default label with at least 1 confirmation\n"
+ HelpExampleCli("getreceivedbylabel", "\"\"") +
"\nAmount received at the tabby label including unconfirmed amounts with zero confirmations\n"
+ HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
"\nThe amount with at least 6 confirmations\n"
+ HelpExampleCli("getreceivedbyaccount", "\"tabby\" 6") +
+ HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6")
+ HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")
);
ObserveSafeMode();
@ -760,9 +760,9 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request)
if (!request.params[1].isNull())
nMinDepth = request.params[1].get_int();
// Get the set of pub keys assigned to account
std::string strAccount = AccountFromValue(request.params[0]);
std::set<CTxDestination> setAddress = pwallet->GetAccountAddresses(strAccount);
// Get the set of pub keys assigned to label
std::string label = LabelFromValue(request.params[0]);
std::set<CTxDestination> setAddress = pwallet->GetLabelAddresses(label);
// Tally
CAmount nAmount = 0;
@ -920,8 +920,8 @@ UniValue movecmd(const JSONRPCRequest& request)
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
std::string strFrom = AccountFromValue(request.params[0]);
std::string strTo = AccountFromValue(request.params[1]);
std::string strFrom = LabelFromValue(request.params[0]);
std::string strTo = LabelFromValue(request.params[1]);
CAmount nAmount = AmountFromValue(request.params[2]);
if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
@ -984,7 +984,7 @@ UniValue sendfrom(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount = AccountFromValue(request.params[0]);
std::string strAccount = LabelFromValue(request.params[0]);
CTxDestination dest = DecodeDestination(request.params[1].get_str());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
@ -1076,7 +1076,7 @@ UniValue sendmany(const JSONRPCRequest& request)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
std::string strAccount = AccountFromValue(request.params[0]);
std::string strAccount = LabelFromValue(request.params[0]);
UniValue sendTo = request.params[1].get_obj();
int nMinDepth = 1;
if (!request.params[2].isNull())
@ -1171,12 +1171,12 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
}
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
std::string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" \"address_type\" )\n"
std::string msg = "addmultisigaddress nrequired [\"key\",...] ( \"label\" \"address_type\" )\n"
"\nAdd a nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
"This functionality is only intended for use with non-watchonly addresses.\n"
"See `importaddress` for watchonly p2sh address support.\n"
"If 'account' is specified (DEPRECATED), assign address to that account.\n"
"If 'label' is specified, assign address to that label.\n"
"\nArguments:\n"
"1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n"
@ -1185,7 +1185,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
" \"address\" (string) bitcoin address or hex-encoded public key\n"
" ...,\n"
" ]\n"
"3. \"account\" (string, optional) DEPRECATED. An account to assign the addresses to.\n"
"3. \"label\" (string, optional) A label to assign the addresses to.\n"
"4. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -addresstype.\n"
"\nResult:\n"
@ -1204,9 +1204,9 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount;
std::string label;
if (!request.params[2].isNull())
strAccount = AccountFromValue(request.params[2]);
label = LabelFromValue(request.params[2]);
int required = request.params[0].get_int();
@ -1233,7 +1233,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
CScript inner = CreateMultisigRedeemscript(required, pubkeys);
pwallet->AddCScript(inner);
CTxDestination dest = pwallet->AddAndGetDestinationForScript(inner, output_type);
pwallet->SetAddressBook(dest, strAccount, "send");
pwallet->SetAddressBook(dest, label, "send");
UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(dest));
@ -1385,14 +1385,14 @@ struct tallyitem
}
};
UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByAccounts)
UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool by_label)
{
// Minimum confirmations
int nMinDepth = 1;
if (!params[0].isNull())
nMinDepth = params[0].get_int();
// Whether to include empty accounts
// Whether to include empty labels
bool fIncludeEmpty = false;
if (!params[1].isNull())
fIncludeEmpty = params[1].get_bool();
@ -1404,7 +1404,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
bool has_filtered_address = false;
CTxDestination filtered_address = CNoDestination();
if (!fByAccounts && params.size() > 3) {
if (!by_label && params.size() > 3) {
if (!IsValidDestinationString(params[3].get_str())) {
throw JSONRPCError(RPC_WALLET_ERROR, "address_filter parameter was invalid");
}
@ -1449,7 +1449,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
// Reply
UniValue ret(UniValue::VARR);
std::map<std::string, tallyitem> mapAccountTally;
std::map<std::string, tallyitem> label_tally;
// Create mapAddressBook iterator
// If we aren't filtering, go from begin() to end()
@ -1466,7 +1466,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
for (auto item_it = start; item_it != end; ++item_it)
{
const CTxDestination& address = item_it->first;
const std::string& strAccount = item_it->second.name;
const std::string& label = item_it->second.name;
auto it = mapTally.find(address);
if (it == mapTally.end() && !fIncludeEmpty)
continue;
@ -1481,9 +1481,9 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
fIsWatchonly = (*it).second.fIsWatchonly;
}
if (fByAccounts)
if (by_label)
{
tallyitem& _item = mapAccountTally[strAccount];
tallyitem& _item = label_tally[label];
_item.nAmount += nAmount;
_item.nConf = std::min(_item.nConf, nConf);
_item.fIsWatchonly = fIsWatchonly;
@ -1494,11 +1494,10 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
if(fIsWatchonly)
obj.pushKV("involvesWatchonly", true);
obj.pushKV("address", EncodeDestination(address));
obj.pushKV("account", strAccount);
obj.pushKV("account", label);
obj.pushKV("amount", ValueFromAmount(nAmount));
obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
if (!fByAccounts)
obj.pushKV("label", strAccount);
obj.pushKV("label", label);
UniValue transactions(UniValue::VARR);
if (it != mapTally.end())
{
@ -1512,9 +1511,9 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
}
}
if (fByAccounts)
if (by_label)
{
for (const auto& entry : mapAccountTally)
for (const auto& entry : label_tally)
{
CAmount nAmount = entry.second.nAmount;
int nConf = entry.second.nConf;
@ -1524,6 +1523,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
obj.pushKV("account", entry.first);
obj.pushKV("amount", ValueFromAmount(nAmount));
obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
obj.pushKV("label", entry.first);
ret.push_back(obj);
}
}
@ -1552,10 +1552,10 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request)
" {\n"
" \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n"
" \"address\" : \"receivingaddress\", (string) The receiving address\n"
" \"account\" : \"accountname\", (string) DEPRECATED. The account of the receiving address. The default account is \"\".\n"
" \"account\" : \"accountname\", (string) DEPRECATED. Backwards compatible alias for label.\n"
" \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n"
" \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n"
" \"label\" : \"label\", (string) A comment for the address/transaction, if any\n"
" \"label\" : \"label\", (string) The label of the receiving address. The default label is \"\".\n"
" \"txids\": [\n"
" n, (numeric) The ids of transactions received with the address \n"
" ...\n"
@ -1582,7 +1582,7 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request)
return ListReceived(pwallet, request.params, false);
}
UniValue listreceivedbyaccount(const JSONRPCRequest& request)
UniValue listreceivedbylabel(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
@ -1591,29 +1591,29 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 3)
throw std::runtime_error(
"listreceivedbyaccount ( minconf include_empty include_watchonly)\n"
"\nDEPRECATED. List balances by account.\n"
"listreceivedbylabel ( minconf include_empty include_watchonly)\n"
"\nList received transactions by label.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
"2. include_empty (bool, optional, default=false) Whether to include accounts that haven't received any payments.\n"
"2. include_empty (bool, optional, default=false) Whether to include labels that haven't received any payments.\n"
"3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n"
"\nResult:\n"
"[\n"
" {\n"
" \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n"
" \"account\" : \"accountname\", (string) The account name of the receiving account\n"
" \"amount\" : x.xxx, (numeric) The total amount received by addresses with this account\n"
" \"account\" : \"accountname\", (string) DEPRECATED. Backwards compatible alias for label.\n"
" \"amount\" : x.xxx, (numeric) The total amount received by addresses with this label\n"
" \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n"
" \"label\" : \"label\" (string) A comment for the address/transaction, if any\n"
" \"label\" : \"label\" (string) The label of the receiving address. The default label is \"\".\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples:\n"
+ HelpExampleCli("listreceivedbyaccount", "")
+ HelpExampleCli("listreceivedbyaccount", "6 true")
+ HelpExampleRpc("listreceivedbyaccount", "6, true, true")
+ HelpExampleCli("listreceivedbylabel", "")
+ HelpExampleCli("listreceivedbylabel", "6 true")
+ HelpExampleRpc("listreceivedbylabel", "6, true, true")
);
ObserveSafeMode();
@ -2927,7 +2927,8 @@ UniValue listunspent(const JSONRPCRequest& request)
" \"txid\" : \"txid\", (string) the transaction id \n"
" \"vout\" : n, (numeric) the vout value\n"
" \"address\" : \"address\", (string) the bitcoin address\n"
" \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n"
" \"label\" : \"label\", (string) The associated label, or \"\" for the default label\n"
" \"account\" : \"account\", (string) DEPRECATED. Backwards compatible alias for label.\n"
" \"scriptPubKey\" : \"key\", (string) the script key\n"
" \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n"
" \"confirmations\" : n, (numeric) The number of confirmations\n"
@ -3031,6 +3032,7 @@ UniValue listunspent(const JSONRPCRequest& request)
entry.pushKV("address", EncodeDestination(address));
if (pwallet->mapAddressBook.count(address)) {
entry.pushKV("label", pwallet->mapAddressBook[address].name);
entry.pushKV("account", pwallet->mapAddressBook[address].name);
}
@ -3826,21 +3828,23 @@ static const CRPCCommand commands[] =
{ "hidden", "resendwallettransactions", &resendwallettransactions, {} },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
{ "wallet", "abortrescan", &abortrescan, {} },
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account","address_type"} },
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label|account","address_type"} },
{ "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
{ "wallet", "getaccountaddress", &getaccountaddress, {"account"} },
{ "wallet", "getlabeladdress", &getlabeladdress, {"label"} },
{ "wallet", "getaccountaddress", &getlabeladdress, {"account"} },
{ "wallet", "getaccount", &getaccount, {"address"} },
{ "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} },
{ "wallet", "getaddressinfo", &getaddressinfo, {"address"} },
{ "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} },
{ "wallet", "getnewaddress", &getnewaddress, {"account","address_type"} },
{ "wallet", "getnewaddress", &getnewaddress, {"label|account","address_type"} },
{ "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
{ "wallet", "getreceivedbyaccount", &getreceivedbyaccount, {"account","minconf"} },
{ "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf"} },
{ "wallet", "getreceivedbyaccount", &getreceivedbylabel, {"account","minconf"} },
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
{ "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} },
{ "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} },
@ -3855,7 +3859,8 @@ static const CRPCCommand commands[] =
{ "wallet", "listaccounts", &listaccounts, {"minconf","include_watchonly"} },
{ "wallet", "listaddressgroupings", &listaddressgroupings, {} },
{ "wallet", "listlockunspent", &listlockunspent, {} },
{ "wallet", "listreceivedbyaccount", &listreceivedbyaccount, {"minconf","include_empty","include_watchonly"} },
{ "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"} },
@ -3866,7 +3871,8 @@ static const CRPCCommand commands[] =
{ "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
{ "wallet", "sendmany", &sendmany, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} },
{ "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} },
{ "wallet", "setaccount", &setaccount, {"address","account"} },
{ "wallet", "setlabel", &setlabel, {"address","label"} },
{ "wallet", "setaccount", &setlabel, {"address","account"} },
{ "wallet", "settxfee", &settxfee, {"amount"} },
{ "wallet", "signmessage", &signmessage, {"address","message"} },
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },

View File

@ -809,12 +809,12 @@ bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmoun
return true;
}
bool CWallet::GetAccountDestination(CTxDestination &dest, std::string strAccount, bool bForceNew)
bool CWallet::GetLabelDestination(CTxDestination &dest, const std::string& label, bool bForceNew)
{
CWalletDB walletdb(*dbw);
CAccount account;
walletdb.ReadAccount(strAccount, account);
walletdb.ReadAccount(label, account);
if (!bForceNew) {
if (!account.vchPubKey.IsValid())
@ -840,8 +840,8 @@ bool CWallet::GetAccountDestination(CTxDestination &dest, std::string strAccount
LearnRelatedScripts(account.vchPubKey, m_default_address_type);
dest = GetDestinationForKey(account.vchPubKey, m_default_address_type);
SetAddressBook(dest, strAccount, "receive");
walletdb.WriteAccount(strAccount, account);
SetAddressBook(dest, label, "receive");
walletdb.WriteAccount(label, account);
} else {
dest = GetDestinationForKey(account.vchPubKey, m_default_address_type);
}
@ -2220,7 +2220,7 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, cons
for (const CTxOut& out : wtx.tx->vout) {
if (outgoing && IsChange(out)) {
debit -= out.nValue;
} else if (IsMine(out) & filter && depth >= minDepth && (!account || *account == GetAccountName(out.scriptPubKey))) {
} else if (IsMine(out) & filter && depth >= minDepth && (!account || *account == GetLabelName(out.scriptPubKey))) {
balance += out.nValue;
}
}
@ -3252,7 +3252,7 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
return CWalletDB(*dbw).EraseName(EncodeDestination(address));
}
const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const
const std::string& CWallet::GetLabelName(const CScript& scriptPubKey) const
{
CTxDestination address;
if (ExtractDestination(scriptPubKey, address) && !scriptPubKey.IsUnspendable()) {
@ -3262,9 +3262,9 @@ const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const
}
}
// A scriptPubKey that doesn't have an entry in the address book is
// associated with the default account ("").
const static std::string DEFAULT_ACCOUNT_NAME;
return DEFAULT_ACCOUNT_NAME;
// associated with the default label ("").
const static std::string DEFAULT_LABEL_NAME;
return DEFAULT_LABEL_NAME;
}
/**
@ -3620,7 +3620,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
return ret;
}
std::set<CTxDestination> CWallet::GetAccountAddresses(const std::string& strAccount) const
std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) const
{
LOCK(cs_wallet);
std::set<CTxDestination> result;
@ -3628,7 +3628,7 @@ std::set<CTxDestination> CWallet::GetAccountAddresses(const std::string& strAcco
{
const CTxDestination& address = item.first;
const std::string& strName = item.second.name;
if (strName == strAccount)
if (strName == label)
result.insert(address);
}
return result;

View File

@ -928,7 +928,7 @@ public:
int64_t IncOrderPosNext(CWalletDB *pwalletdb = nullptr);
DBErrors ReorderTransactions();
bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = "");
bool GetAccountDestination(CTxDestination &dest, std::string strAccount, bool bForceNew = false);
bool GetLabelDestination(CTxDestination &dest, const std::string& label, bool bForceNew = false);
void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
@ -1006,7 +1006,7 @@ public:
std::set< std::set<CTxDestination> > GetAddressGroupings();
std::map<CTxDestination, CAmount> GetAddressBalances();
std::set<CTxDestination> GetAccountAddresses(const std::string& strAccount) const;
std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
isminetype IsMine(const CTxIn& txin) const;
/**
@ -1036,7 +1036,7 @@ public:
bool DelAddressBook(const CTxDestination& address);
const std::string& GetAccountName(const CScript& scriptPubKey) const;
const std::string& GetLabelName(const CScript& scriptPubKey) const;
void Inventory(const uint256 &hash) override
{

View File

@ -67,7 +67,7 @@ BASE_SCRIPTS= [
'feature_segwit.py',
# vv Tests less than 2m vv
'wallet_basic.py',
'wallet_accounts.py',
'wallet_labels.py',
'p2p_segwit.py',
'wallet_dump.py',
'rpc_listtransactions.py',

View File

@ -1,206 +0,0 @@
#!/usr/bin/env python3
# Copyright (c) 2016-2017 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test account RPCs.
RPCs tested are:
- getaccountaddress
- getaddressesbyaccount
- listaddressgroupings
- setaccount
- sendfrom (with account arguments)
- move (with account arguments)
"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
class WalletAccountsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
self.extra_args = [[]]
def run_test(self):
node = self.nodes[0]
# Check that there's no UTXO on any of the nodes
assert_equal(len(node.listunspent()), 0)
# Note each time we call generate, all generated coins go into
# the same address, so we call twice to get two addresses w/50 each
node.generate(1)
node.generate(101)
assert_equal(node.getbalance(), 100)
# there should be 2 address groups
# each with 1 address with a balance of 50 Bitcoins
address_groups = node.listaddressgroupings()
assert_equal(len(address_groups), 2)
# the addresses aren't linked now, but will be after we send to the
# common address
linked_addresses = set()
for address_group in address_groups:
assert_equal(len(address_group), 1)
assert_equal(len(address_group[0]), 2)
assert_equal(address_group[0][1], 50)
linked_addresses.add(address_group[0][0])
# send 50 from each address to a third address not in this wallet
# There's some fee that will come back to us when the miner reward
# matures.
common_address = "msf4WtN1YQKXvNtvdFYt9JBnUD2FB41kjr"
txid = node.sendmany(
fromaccount="",
amounts={common_address: 100},
subtractfeefrom=[common_address],
minconf=1,
)
tx_details = node.gettransaction(txid)
fee = -tx_details['details'][0]['fee']
# there should be 1 address group, with the previously
# unlinked addresses now linked (they both have 0 balance)
address_groups = node.listaddressgroupings()
assert_equal(len(address_groups), 1)
assert_equal(len(address_groups[0]), 2)
assert_equal(set([a[0] for a in address_groups[0]]), linked_addresses)
assert_equal([a[1] for a in address_groups[0]], [0, 0])
node.generate(1)
# we want to reset so that the "" account has what's expected.
# otherwise we're off by exactly the fee amount as that's mined
# and matures in the next 100 blocks
node.sendfrom("", common_address, fee)
amount_to_send = 1.0
# Create accounts and make sure subsequent account API calls
# recognize the account/address associations.
accounts = [Account(name) for name in ("a", "b", "c", "d", "e")]
for account in accounts:
account.add_receive_address(node.getaccountaddress(account.name))
account.verify(node)
# Send a transaction to each account, and make sure this forces
# getaccountaddress to generate a new receiving address.
for account in accounts:
node.sendtoaddress(account.receive_address, amount_to_send)
account.add_receive_address(node.getaccountaddress(account.name))
account.verify(node)
# Check the amounts received.
node.generate(1)
for account in accounts:
assert_equal(
node.getreceivedbyaddress(account.addresses[0]), amount_to_send)
assert_equal(node.getreceivedbyaccount(account.name), amount_to_send)
# Check that sendfrom account reduces listaccounts balances.
for i, account in enumerate(accounts):
to_account = accounts[(i+1) % len(accounts)]
node.sendfrom(account.name, to_account.receive_address, amount_to_send)
node.generate(1)
for account in accounts:
account.add_receive_address(node.getaccountaddress(account.name))
account.verify(node)
assert_equal(node.getreceivedbyaccount(account.name), 2)
node.move(account.name, "", node.getbalance(account.name))
account.verify(node)
node.generate(101)
expected_account_balances = {"": 5200}
for account in accounts:
expected_account_balances[account.name] = 0
assert_equal(node.listaccounts(), expected_account_balances)
assert_equal(node.getbalance(""), 5200)
# Check that setaccount can assign an account to a new unused address.
for account in accounts:
address = node.getaccountaddress("")
node.setaccount(address, account.name)
account.add_address(address)
account.verify(node)
assert(address not in node.getaddressesbyaccount(""))
# Check that addmultisigaddress can assign accounts.
for account in accounts:
addresses = []
for x in range(10):
addresses.append(node.getnewaddress())
multisig_address = node.addmultisigaddress(5, addresses, account.name)['address']
account.add_address(multisig_address)
account.verify(node)
node.sendfrom("", multisig_address, 50)
node.generate(101)
for account in accounts:
assert_equal(node.getbalance(account.name), 50)
# Check that setaccount can change the account of an address from a
# different account.
change_account(node, accounts[0].addresses[0], accounts[0], accounts[1])
# Check that setaccount can change the account of an address which
# is the receiving address of a different account.
change_account(node, accounts[0].receive_address, accounts[0], accounts[1])
# Check that setaccount can set the account of an address already
# in the account. This is a no-op.
change_account(node, accounts[2].addresses[0], accounts[2], accounts[2])
# Check that setaccount can set the account of an address which is
# already the receiving address of the account. It would probably make
# sense for this to be a no-op, but right now it resets the receiving
# address, causing getaccountaddress to return a brand new address.
change_account(node, accounts[2].receive_address, accounts[2], accounts[2])
class Account:
def __init__(self, name):
# Account name
self.name = name
# Current receiving address associated with this account.
self.receive_address = None
# List of all addresses assigned with this account
self.addresses = []
def add_address(self, address):
assert_equal(address not in self.addresses, True)
self.addresses.append(address)
def add_receive_address(self, address):
self.add_address(address)
self.receive_address = address
def verify(self, node):
if self.receive_address is not None:
assert self.receive_address in self.addresses
assert_equal(node.getaccountaddress(self.name), self.receive_address)
for address in self.addresses:
assert_equal(node.getaccount(address), self.name)
assert_equal(
set(node.getaddressesbyaccount(self.name)), set(self.addresses))
def change_account(node, address, old_account, new_account):
assert_equal(address in old_account.addresses, True)
node.setaccount(address, new_account.name)
old_account.addresses.remove(address)
new_account.add_address(address)
# Calling setaccount on an address which was previously the receiving
# address of a different account should reset the receiving address of
# the old account, causing getaccountaddress to return a brand new
# address.
if address == old_account.receive_address:
new_address = node.getaccountaddress(old_account.name)
assert_equal(new_address not in old_account.addresses, True)
assert_equal(new_address not in new_account.addresses, True)
old_account.add_receive_address(new_address)
old_account.verify(node)
new_account.verify(node)
if __name__ == '__main__':
WalletAccountsTest().main()

View File

@ -78,7 +78,7 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
if txid is not None:
tx, = [tx for tx in txs if tx["txid"] == txid]
assert_equal(tx["account"], self.label)
assert_equal(tx["label"], self.label)
assert_equal(tx["address"], self.address["address"])
assert_equal(tx["amount"], amount)
assert_equal(tx["category"], "receive")

206
test/functional/wallet_labels.py Executable file
View File

@ -0,0 +1,206 @@
#!/usr/bin/env python3
# Copyright (c) 2016-2017 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test label RPCs.
RPCs tested are:
- getlabeladdress
- getaddressesbyaccount
- listaddressgroupings
- setlabel
- sendfrom (with account arguments)
- move (with account arguments)
"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
class WalletLabelsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
self.extra_args = [[]]
def run_test(self):
node = self.nodes[0]
# Check that there's no UTXO on any of the nodes
assert_equal(len(node.listunspent()), 0)
# Note each time we call generate, all generated coins go into
# the same address, so we call twice to get two addresses w/50 each
node.generate(1)
node.generate(101)
assert_equal(node.getbalance(), 100)
# there should be 2 address groups
# each with 1 address with a balance of 50 Bitcoins
address_groups = node.listaddressgroupings()
assert_equal(len(address_groups), 2)
# the addresses aren't linked now, but will be after we send to the
# common address
linked_addresses = set()
for address_group in address_groups:
assert_equal(len(address_group), 1)
assert_equal(len(address_group[0]), 2)
assert_equal(address_group[0][1], 50)
linked_addresses.add(address_group[0][0])
# send 50 from each address to a third address not in this wallet
# There's some fee that will come back to us when the miner reward
# matures.
common_address = "msf4WtN1YQKXvNtvdFYt9JBnUD2FB41kjr"
txid = node.sendmany(
fromaccount="",
amounts={common_address: 100},
subtractfeefrom=[common_address],
minconf=1,
)
tx_details = node.gettransaction(txid)
fee = -tx_details['details'][0]['fee']
# there should be 1 address group, with the previously
# unlinked addresses now linked (they both have 0 balance)
address_groups = node.listaddressgroupings()
assert_equal(len(address_groups), 1)
assert_equal(len(address_groups[0]), 2)
assert_equal(set([a[0] for a in address_groups[0]]), linked_addresses)
assert_equal([a[1] for a in address_groups[0]], [0, 0])
node.generate(1)
# we want to reset so that the "" label has what's expected.
# otherwise we're off by exactly the fee amount as that's mined
# and matures in the next 100 blocks
node.sendfrom("", common_address, fee)
amount_to_send = 1.0
# 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")]
for label in labels:
label.add_receive_address(node.getlabeladdress(label.name))
label.verify(node)
# Send a transaction to each label, and make sure this forces
# getlabeladdress to generate a new receiving address.
for label in labels:
node.sendtoaddress(label.receive_address, amount_to_send)
label.add_receive_address(node.getlabeladdress(label.name))
label.verify(node)
# Check the amounts received.
node.generate(1)
for label in labels:
assert_equal(
node.getreceivedbyaddress(label.addresses[0]), amount_to_send)
assert_equal(node.getreceivedbylabel(label.name), amount_to_send)
# Check that sendfrom label reduces listaccounts balances.
for i, label in enumerate(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))
label.verify(node)
node.generate(101)
expected_account_balances = {"": 5200}
for label in labels:
expected_account_balances[label.name] = 0
assert_equal(node.listaccounts(), expected_account_balances)
assert_equal(node.getbalance(""), 5200)
# Check that setlabel can assign a label to a new unused address.
for label in labels:
address = node.getlabeladdress("")
node.setlabel(address, label.name)
label.add_address(address)
label.verify(node)
assert(address not in node.getaddressesbyaccount(""))
# Check that addmultisigaddress can assign labels.
for label in labels:
addresses = []
for x in range(10):
addresses.append(node.getnewaddress())
multisig_address = node.addmultisigaddress(5, addresses, label.name)['address']
label.add_address(multisig_address)
label.verify(node)
node.sendfrom("", multisig_address, 50)
node.generate(101)
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.
change_label(node, labels[0].addresses[0], labels[0], labels[1])
# Check that setlabel can change the label of an address which
# is the receiving address of a different label.
change_label(node, labels[0].receive_address, labels[0], labels[1])
# Check that setlabel can set the label of an address already
# in the label. This is a no-op.
change_label(node, labels[2].addresses[0], labels[2], labels[2])
# Check that setlabel can set the label of an address which is
# already the receiving address of the label. It would probably make
# sense for this to be a no-op, but right now it resets the receiving
# address, causing getlabeladdress to return a brand new address.
change_label(node, labels[2].receive_address, labels[2], labels[2])
class Label:
def __init__(self, name):
# Label name
self.name = name
# Current receiving address associated with this label.
self.receive_address = None
# List of all addresses assigned with this label
self.addresses = []
def add_address(self, address):
assert_equal(address not in self.addresses, True)
self.addresses.append(address)
def add_receive_address(self, address):
self.add_address(address)
self.receive_address = address
def verify(self, node):
if self.receive_address is not None:
assert self.receive_address in self.addresses
assert_equal(node.getlabeladdress(self.name), self.receive_address)
for address in self.addresses:
assert_equal(node.getaccount(address), self.name)
assert_equal(
set(node.getaddressesbyaccount(self.name)), set(self.addresses))
def change_label(node, address, old_label, new_label):
assert_equal(address in old_label.addresses, True)
node.setlabel(address, new_label.name)
old_label.addresses.remove(address)
new_label.add_address(address)
# Calling setlabel on an address which was previously the receiving
# address of a different label should reset the receiving address of
# the old label, causing getlabeladdress to return a brand new
# address.
if address == old_label.receive_address:
new_address = node.getlabeladdress(old_label.name)
assert_equal(new_address not in old_label.addresses, True)
assert_equal(new_address not in new_label.addresses, True)
old_label.add_receive_address(new_address)
old_label.verify(node)
new_label.verify(node)
if __name__ == '__main__':
WalletLabelsTest().main()

View File

@ -36,11 +36,11 @@ class ReceivedByTest(BitcoinTestFramework):
self.sync_all()
assert_array_result(self.nodes[1].listreceivedbyaddress(),
{"address": addr},
{"address": addr, "account": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]})
{"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]})
# With min confidence < 10
assert_array_result(self.nodes[1].listreceivedbyaddress(5),
{"address": addr},
{"address": addr, "account": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]})
{"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]})
# With min confidence > 10, should not find Tx
assert_array_result(self.nodes[1].listreceivedbyaddress(11), {"address": addr}, {}, True)
@ -48,11 +48,11 @@ class ReceivedByTest(BitcoinTestFramework):
empty_addr = self.nodes[1].getnewaddress()
assert_array_result(self.nodes[1].listreceivedbyaddress(0, True),
{"address": empty_addr},
{"address": empty_addr, "account": "", "amount": 0, "confirmations": 0, "txids": []})
{"address": empty_addr, "label": "", "amount": 0, "confirmations": 0, "txids": []})
#Test Address filtering
#Only on addr
expected = {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}
expected = {"address":addr, "label":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}
res = self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True, address_filter=addr)
assert_array_result(res, {"address":addr}, expected)
assert_equal(len(res), 1)
@ -66,12 +66,12 @@ class ReceivedByTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
#Same test as above should still pass
expected = {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":11, "txids":[txid,]}
expected = {"address":addr, "label":"", "amount":Decimal("0.1"), "confirmations":11, "txids":[txid,]}
res = self.nodes[1].listreceivedbyaddress(0, True, True, addr)
assert_array_result(res, {"address":addr}, expected)
assert_equal(len(res), 1)
#Same test as above but with other_addr should still pass
expected = {"address":other_addr, "account":"", "amount":Decimal("0.1"), "confirmations":1, "txids":[txid2,]}
expected = {"address":other_addr, "label":"", "amount":Decimal("0.1"), "confirmations":1, "txids":[txid2,]}
res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr)
assert_array_result(res, {"address":other_addr}, expected)
assert_equal(len(res), 1)
@ -108,46 +108,46 @@ class ReceivedByTest(BitcoinTestFramework):
# Trying to getreceivedby for an address the wallet doesn't own should return an error
assert_raises_rpc_error(-4, "Address not found in wallet", self.nodes[0].getreceivedbyaddress, addr)
self.log.info("listreceivedbyaccount + getreceivedbyaccount Test")
self.log.info("listreceivedbylabel + getreceivedbylabel Test")
# set pre-state
addrArr = self.nodes[1].getnewaddress()
account = self.nodes[1].getaccount(addrArr)
received_by_account_json = [r for r in self.nodes[1].listreceivedbyaccount() if r["account"] == account][0]
balance_by_account = self.nodes[1].getreceivedbyaccount(account)
label = self.nodes[1].getaccount(addrArr)
received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel() if r["label"] == label][0]
balance_by_label = self.nodes[1].getreceivedbylabel(label)
txid = self.nodes[0].sendtoaddress(addr, 0.1)
self.sync_all()
# listreceivedbyaccount should return received_by_account_json because of 0 confirmations
assert_array_result(self.nodes[1].listreceivedbyaccount(),
{"account": account},
received_by_account_json)
# listreceivedbylabel should return received_by_label_json because of 0 confirmations
assert_array_result(self.nodes[1].listreceivedbylabel(),
{"label": label},
received_by_label_json)
# getreceivedbyaddress should return same balance because of 0 confirmations
balance = self.nodes[1].getreceivedbyaccount(account)
assert_equal(balance, balance_by_account)
balance = self.nodes[1].getreceivedbylabel(label)
assert_equal(balance, balance_by_label)
self.nodes[1].generate(10)
self.sync_all()
# listreceivedbyaccount should return updated account balance
assert_array_result(self.nodes[1].listreceivedbyaccount(),
{"account": account},
{"account": received_by_account_json["account"], "amount": (received_by_account_json["amount"] + Decimal("0.1"))})
# listreceivedbylabel should return updated received list
assert_array_result(self.nodes[1].listreceivedbylabel(),
{"label": label},
{"label": received_by_label_json["label"], "amount": (received_by_label_json["amount"] + Decimal("0.1"))})
# getreceivedbyaddress should return updates balance
balance = self.nodes[1].getreceivedbyaccount(account)
assert_equal(balance, balance_by_account + Decimal("0.1"))
# getreceivedbylabel should return updated receive total
balance = self.nodes[1].getreceivedbylabel(label)
assert_equal(balance, balance_by_label + Decimal("0.1"))
# Create a new account named "mynewaccount" that has a 0 balance
self.nodes[1].getaccountaddress("mynewaccount")
received_by_account_json = [r for r in self.nodes[1].listreceivedbyaccount(0, True) if r["account"] == "mynewaccount"][0]
# Create a new label named "mynewlabel" that has a 0 balance
self.nodes[1].getlabeladdress("mynewlabel")
received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel(0, True) if r["label"] == "mynewlabel"][0]
# Test includeempty of listreceivedbyaccount
assert_equal(received_by_account_json["amount"], Decimal("0.0"))
# Test includeempty of listreceivedbylabel
assert_equal(received_by_label_json["amount"], Decimal("0.0"))
# Test getreceivedbyaccount for 0 amount accounts
balance = self.nodes[1].getreceivedbyaccount("mynewaccount")
# Test getreceivedbylabel for 0 amount labels
balance = self.nodes[1].getreceivedbylabel("mynewlabel")
assert_equal(balance, Decimal("0.0"))
if __name__ == '__main__':