Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
rpc: introduce 'label' API for wallet #7729
Conversation
laanwj
added Wallet RPC
labels
Mar 21, 2016
|
Concept ACK. My idea was it to duplicate the current wallet implementation ( The second wallet could come without API stableness (for the first two releases or so) and could be marked as experimental. |
I disagree:
The point here is to give a non-deprecated equivalent to the 'label' system as used in the GUI, so the subset of the 'account system' that people are still allowed to use. This is a required, but up to now missing part of deprecating the account system. I'm not trying to rule out any other work that is being done such as multi-wallet support. I think this is pretty much orthogonal. As for alternative wallets, they've been proposed since at least 2012 - but none have materialized yet. And none of this change rules them out. |
Yes. I agree. |
|
Note getaccountaddress does not presently get a "default" address, it gets an unused address with the label, creating one if necessary. This seems useful only for mining, since no other context can guarantee an address hasn't been "used" but not sent to yet. I can't think of a good way to deprecate this, however. |
I wonder if we can find a better (or at least simpler, the |
I thought of the following: you could use two labels, one for the 'active' address, one for the 'normal'. Say When the miner needs an address it will:
This is a little bit more involved at the user side, but it avoids special administration (needing to keep around |
This was referenced Mar 24, 2016
|
That looks like a lot of overhead, and this is a rather time-sensitive call, as the miner is working on stale work until it's done. Also, why are there no getreceivedbylabel/listreceivedbylabel? These don't have anything to do with balances. |
jonasschnelli
referenced
this pull request
Apr 7, 2016
Closed
[Wallet] Add cloned wallet, remove accounts, reset version #7830
I'd suggest to try it. It shouldn't be much slower.
Looks like you're right.
|
|
@luke-jr Wouldn't it be feasible to instead generate a sequence of deterministic addresses for mining, for example using BIP32 derivation with the block height as index? |
@laanwj getreceivedbyaddress at least would loop over all the wtx... and then there's the additional latency from the back and forth of multiple calls. I haven't tried it yet, though.
@sipa Perhaps, if the wallet had a way to do this. Using the height seems incompatible with gap limits, though? |
|
@luke-jr I mean the mining software can do derivation, and import the keys into the wallet when a block is found. |
|
The functional test coverage for accounts is minimal or not existent, I think we should move forward with this pull. Needs rebase. |
|
Re-Concept ACK. |
wallclockbuilder
commented
May 22, 2016
|
Needs tests. |
|
@wallclockbuilder No shit, have you seen the TODOs at the bottom of the opening post? |
|
To be clear I posted this to get comments on the API, is there anything left to be done there? I'm going to write tests when it is clear that this is what we want at all. |
laanwj
added this to the
Future
milestone
Jun 13, 2016
I would not consider this optional. User will always do what you not want them to do. |
This was referenced Aug 18, 2016
|
Concept ACK |
andrewbaine
commented
Sep 24, 2016
•
|
listtransactions has an "account" argument where you now you would pass "*" if you need to supply non-default args for count, from, and includeWatchOnly. Will there be a way to query for transactions affecting any address with a given label? Could we tack on a "label" argument to listtransactions? |
|
I think the account argument of listtransactions could simply be re-used as a label argument. As listing transactions to a label has nothing to do with per-label balances there is no need to drop that particular functionality, |
andrewbaine
commented
Oct 3, 2016
|
repurposing the "account" argument to be "label" makes sense to me |
jonasschnelli
referenced
this pull request
Oct 18, 2016
Closed
[Qt][WIP] allow possibility to add a comment to a WalletTx #5905
|
Fast review ACK (besides needing rebase and the promised tests).
I'm not sure it's worth to bother. I think we should go ahead and completely remove account functionality within 0.14 instead. But whatever we do, it can be done in a later PR. |
Description nit: not sure I catch the distinction here.
I'd say no unless there is a compelling use-case that can't be replicated another way. |
instagibbs
reviewed
Nov 18, 2016
concept ACK, with some nits/questions.
I am not an expert on how accounts exactly work as-is.
It also may be helpful to give a brief motivation in the OP. What's wrong with accounts, and what label fixes about that. Currently it's a list of differences without clear motivation.
| @@ -2497,6 +2503,216 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) | ||
| return result; | ||
| } | ||
| +UniValue getlabeladdress(const UniValue& params, bool fHelp) |
instagibbs
Nov 18, 2016
Member
would getnewlabeladdress be wrong? As-is gave me the impression that this was some static address.
| + "\nArguments:\n" | ||
| + "1. \"label\" (string, required) The label for the address. It can also be set to the empty string \"\" to represent the default label.\n" | ||
| + "\nResult:\n" | ||
| + "\"bitcoinaddress\" (string) The 'label address' for the label\n" |
| + "listlabels ( \"purpose\" )\n" | ||
| + "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n" | ||
| + "\nArguments:\n" | ||
| + "1. \"purpose\" (string, optional) Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument.\n" |
instagibbs
Nov 18, 2016
Member
I'm not as familiar with accounts as others, but is this type of parameter actually going to be used? When would you want to list labels that go certain directions? I have a feeling labels people choose will reflect this already.
laanwj
Nov 21, 2016
Owner
The goal of this API is to expose exactly the same functionality that the GUI uses. The GUI distinguishes between different kind of labels (to show sending/receiving addresses), so it should be offered here as well.
| + if (fHelp || params.size() < 1 || params.size() > 2) | ||
| + throw runtime_error( | ||
| + "setlabel \"bitcoinaddress\" \"label\"\n" | ||
| + "\nSets the label associated with the given address.\n" |
laanwj
Nov 21, 2016
Owner
Indeed, that is what setting a label does. If there is no labeling information associated with an address it will create that. We don't want to use the term "address book" here I think.
See #3816 |
|
utACK a2557ff needs (trivial) rebase for the new |
|
@laanwj yes I read the issue, but there are some disagreements in that thread about what the actual issue is(malleability being the first issue noted?). I assume it's along the lines of "people want to watermark addresses, but bitcoind wallet shouldn't try to be an accounting system for those labels". |
motatoes
commented
Nov 22, 2016
|
Hi. When is this new feature expected to be rolled out? |
|
@motatoes Like everything, when it's ready. That may be in 0.14.0 or later. |
| + if (pwalletMain->mapAddressBook.count(address.Get())) | ||
| + { | ||
| + string strOldLabel = pwalletMain->mapAddressBook[address.Get()].name; | ||
| + if (address == GetAccountAddress(strOldLabel)) |
ryanofsky
Dec 12, 2016
Contributor
This condition should be changed to if (strOldLabel != strLabel && address == GetAccountAddress(strOldLabel)), so calling setlabel on an address which already has the same label will just be a no-op, instead of creating an unexpected side effect where the label's default label address gets discarded.
|
@laanwj , I created an RPC test for this change here: ryanofsky/bitcoin@2dac8eb Feel free to incorporate it in this PR, or I could create a separate one. |
|
Needs rebase |
jonasschnelli
referenced
this pull request
Feb 8, 2017
Open
Feature request: getnewaddress 'count' #9723
This was referenced Feb 23, 2017
|
Needs rebase again, sorry for not reviewing after the last rebase. |
|
Concept reACK |
laanwj
modified the milestone: 0.15.0, Future
Mar 16, 2017
laanwj
added
to Blockers in High-priority for review
Mar 24, 2017
|
Needs rebase. Concept ACK |
str4d
referenced
this pull request
in zcash/zcash
Mar 27, 2017
Open
Add the ability to label addresses in the wallet #2215
| + "\nSets the label associated with the given address.\n" | ||
| + "\nArguments:\n" | ||
| + "1. \"bitcoinaddress\" (string, required) The bitcoin address to be associated with an label.\n" | ||
| + "2. \"label\" (string, required) The label to assign to the address.\n" |
JeremyRubin
Mar 28, 2017
Contributor
Is the label optional or required?
If it's required update L2675 to if (fHelp || params.size() != 2) and L2693-2695 to std::string strLabel = AccountFromValue(params[1]);, if optional update the docstring.
JeremyRubin
approved these changes
Mar 28, 2017
utack!
Overall, looks good. Just nits from me really. Only thing I was concerned about is the proper behavior for watchonly addresses, perhaps some added documentation for that would be good.
| { | ||
| UniValue ret(UniValue::VOBJ); | ||
| - if (includeName) | ||
| + if (verbose) |
JeremyRubin
Mar 28, 2017
Contributor
Does the order of push_back matter here? Might be cleaner to lump all if (verbose) under one branch...
| + UniValue ret(UniValue::VSTR); | ||
| + | ||
| + ret = GetAccountAddress(strLabel).ToString(); | ||
| + return ret; |
JeremyRubin
Mar 28, 2017
Contributor
It seems like L2529-2534 could be a one or two liner, rather than 4 (and maybe get rid of the whitespace).
| + ret.push_back(Pair("purpose", data.purpose)); | ||
| + if (includeDestData) { | ||
| + UniValue ddata(UniValue::VOBJ); | ||
| + BOOST_FOREACH(const PAIRTYPE(std::string, std::string) &item, data.destdata) |
| + | ||
| + // Find all addresses that have the given label | ||
| + UniValue ret(UniValue::VOBJ); | ||
| + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) |
| + | ||
| + std::string purpose; | ||
| + if (params.size() > 0) | ||
| + purpose = params[0].get_str(); |
| + purpose = params[0].get_str(); | ||
| + | ||
| + std::set<std::string> setLabels; | ||
| + BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) { |
| + setLabels.insert(entry.second.name); | ||
| + } | ||
| + UniValue ret(UniValue::VARR); | ||
| + BOOST_FOREACH(const std::string &name, setLabels) |
| + if (params.size() > 1) | ||
| + strLabel = AccountFromValue(params[1]); | ||
| + | ||
| + if (IsMine(*pwalletMain, address.Get())) |
| + | ||
| + if (IsMine(*pwalletMain, address.Get())) | ||
| + { | ||
| + // Detect when changing the label of an address that is the 'label address' of another label: |
JeremyRubin
Mar 28, 2017
Contributor
This behavior is probably just to mirror prior behavior, but perhaps a better alternative would be to create a fresh address for the account & allow deleting of account via another mechanism.
| + pwalletMain->SetAddressBook(address.Get(), strLabel, "receive"); | ||
| + } | ||
| + else | ||
| + pwalletMain->SetAddressBook(address.Get(), strLabel, "send"); |
NicolasDorier
referenced
this pull request
Mar 29, 2017
Open
listreceivedbyaddress Filter Address #9991
|
@laanwj, do you want to cherry pick my unit test into this PR? (ryanofsky/bitcoin@2dac8eb) In IRC recently, you requested that people "please review the API, not the code," and I think the unit test can help with this, because it demonstrates the API in action, covers various subtleties, and is also well commented. |
Yes, I will, thank you! |
|
API ACK. Still needs rebase :( |
|
I expected to be able to attach multiple labels to a single address. |
That is not possible in the GUI either, in the OP I've defined the scope as:
Such functionality could be added in the future. |
|
Nobody seems to have big problems with the API for a while. Perhaps it's time to rebase and start reviewing the code itself |
|
I wanted to test this so I rebased on master and cherry-picked Russ's test. I've pushed it here: https://github.com/jnewbery/bitcoin/tree/pr7729 . Feel free to reset onto that commit. Note that there were quite a few refactors (multiwallet, namespaces, database wrapper, JSONRPC request, rpc names args), so someone else should probably review the rebase to make sure I haven't missed anything. Note that the final test in Russ's test script currently fails. I added some trace to the script and see that the address returned by
I haven't yet dug into why that's the case. |
|
I've made a few more changes to rpcwallet.cpp to reflect more recent style guidelines (no Once you've taken the changes I'll add my review comments. |
|
@jnewbery Thanks so much! Updated this pull to that branch. |
|
needs rebase again! (although this one should be easier - just fixing the |
jnewbery
reviewed
Jun 14, 2017
•
A few comments inline. More general feedback:
-
I really don't like the concept of a 'label address'. It seems to go against the philosophy of 'labels are associated with addresses, instead of addresses associated with labels', and makes the distinction between labels and accounts more confusing.
You also mentioned in the OP this would greatly simplify the code though - it would be impossible to get rid of the CAccount structure due to this..
So it sounds to me like removing label addresses makes the interface clearer for users and makes the implementation simpler, so let's just do it. #7729 (comment) seems to be a sensible solution for miners using account addresses. -
I agree that users should be able to add multiple labels to an address. That can be done as a follow-up PR.
-
You say in the OP Do not use the deprecated account system and the label system with the same wallet at the same time.. There doesn't appear to be any discussion yet on how to achieve this, or how to handle migrating from accounts to labels. One suggestion: hide these new RPCs with old wallet versions, add a new
enablelabelsRPC which upgrades the wallet to a new version, and on the new version, hide the account RPCs. TheenablelabelsRPC should describe the implications of moving from accounts to labels. I think this requires a bit more thought and discussion.
| @@ -204,6 +204,7 @@ class CWalletDB | ||
| bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry); | ||
| bool ReadAccount(const std::string& strAccount, CAccount& account); | ||
| bool WriteAccount(const std::string& strAccount, const CAccount& account); | ||
| + bool EraseAccount(const std::string& strAccount); |
jnewbery
Jun 14, 2017
Member
I think the comment above may need updating to:
- remove comments about accounting entries (or say they're deprecated)
- comment that 'acc' database entries are for labels.
| + LOCK2(cs_main, pwallet->cs_wallet); | ||
| + | ||
| + // Parse the label first so we don't generate a key if there's an error | ||
| + std::string strLabel = AccountFromValue(request.params[0]); |
jnewbery
Jun 14, 2017
Member
It'd be nice to eventually rename this function LabelFromValue(). That can be done in a follow-up PR.
| + "\nResult:\n" | ||
| + " { (json object with information about address)\n" | ||
| + " \"name\": \"labelname\" (string) The label\n" | ||
| + " \"purpose\": \"string\" (string) Purpose of address (\"send\" for sending address, \"receive\" for receiving address)\n" |
jnewbery
Jun 14, 2017
Member
If I'm understanding AddressBookDataToJSON() correctly, then the result will also include an array of destdata.
| + "listlabels ( \"purpose\" )\n" | ||
| + "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n" | ||
| + "\nArguments:\n" | ||
| + "1. \"purpose\" (string, optional) Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument.\n" |
jnewbery
Jun 14, 2017
Member
nit: I don't think An empty string is the same as not providing this argument. is required
promag
Jul 7, 2017
Contributor
BTW, why is this argument needed? I mean, the client can filter the result. IMO pagination is more interesting.
| + } | ||
| + | ||
| + std::string strLabel; | ||
| + if (request.params.size() > 1){ |
jnewbery
Jun 14, 2017
Member
What happens if request.params.size() == 1? Are you supposed to be able to remove a label using this rpc without a second argument? If so, I think you want to call pwallet->DelAddressBook.
| + | ||
| + if (IsMine(*pwallet, address.Get())) | ||
| + { | ||
| + // Detect when changing the label of an address that is the 'label address' of another label: |
jnewbery
Jun 14, 2017
Member
I think we should remove the concept of 'label address' and change this section to delete the label when removing the final address from that label.
| @@ -3146,7 +3146,7 @@ UniValue setlabel(const JSONRPCRequest& request) | ||
| if (pwallet->mapAddressBook.count(address.Get())) | ||
| { | ||
| std::string strOldLabel = pwallet->mapAddressBook[address.Get()].name; | ||
| - if (address == GetAccountAddress(pwallet, strOldLabel)) { | ||
| + if (strOldLabel != strLabel && address == GetAccountAddress(pwallet, strOldLabel)) { |
ryanofsky
Jun 15, 2017
Contributor
Note: This change makes calling setlabel on an address which already has the same label a no-op, see #7729 (comment).
|
So to summarize above discussion, the following changes should be made:
Agree re: versioning. We already have a command line argument to upgrade the wallet version, |
| @@ -152,6 +152,11 @@ UniValue getnewaddress(const JSONRPCRequest& request) | ||
| return CBitcoinAddress(keyID).ToString(); | ||
| } | ||
| +void DeleteAccount(CWallet * const pwallet, std::string strAccount) |
promag
Jul 7, 2017
Contributor
Nit, shouldn't we follow the convention in new code in favor of consistency? In that case rename strAccount to account_name for instance?
| + } | ||
| + ret.push_back(Pair("purpose", data.purpose)); | ||
| + if (verbose) { | ||
| + UniValue ddata(UniValue::VOBJ); |
| + "listlabels ( \"purpose\" )\n" | ||
| + "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n" | ||
| + "\nArguments:\n" | ||
| + "1. \"purpose\" (string, optional) Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument.\n" |
promag
Jul 7, 2017
Contributor
BTW, why is this argument needed? I mean, the client can filter the result. IMO pagination is more interesting.
ACK - yes this is enough. The wallet startup command line arguments always seemed strange to me, and if we were building this from scratch we might implement upgradewallet as an RPC or an external tool. However, it makes sense to use the existing infrastructure.
Two additional points:
It feels like we've missed the boat for 0.15. @laanwj - it'd be great to get this in early in the next cycle. I'll have some spare time in the next few weeks. Let me know if there's anything I can do to help here. I'm happy to help write and test the upgrade code. |
laanwj
removed
from Blockers in High-priority for review
Jul 11, 2017
laanwj
modified the milestone: 0.16.0, 0.15.0
Jul 11, 2017
mmgen
commented
Jul 23, 2017
•
|
@laanwj |
mmgen
commented
Jul 29, 2017
•
|
@jnewbery Since I got no reponse from @laanwj, I'll refer you to my above comment. The behavior I mentioned can be easily demonstrated with the following:
|
laanwj commentedMar 21, 2016
•
edited
Add label API to wallet RPC.
This is one step towards #3816 ("Remove bolt-on account system") although it doesn't actually remove anything yet (that would be a follow-up pull).
These initially mirror the account functions, with the following differences:
getreceivedbylabellistlabelslistreceivedbylabelcan show received transactions to addresses with a label, not use the account tally (currently it is removed, but according to discussion that goes too far as this doesn't inherently have to do with balance)listlabelshas no minconf or watchonly argumentmovesetlabel, but an explicit calldeletelabelwhich assigns all address to the default label may make sense.accountargument renamed tolabel:importaddressimportprivkeyimportpubkeyAPI
Short description of every RPC call: for detailed information check RPC help. The general idea is to offer the same functionality as the GUI label system. Labels are simply a name for an address, or a group of addresses.
Do not use the deprecated account system and the label system with the same wallet at the same time. These APIs use the same underlying data in the database for (slightly) different purposes, using them interchangeably will give unexpected results. (Just like using the GUI labels and account system at the same time. Using the GUI labels and the label API at the same time, however, is no problem)
getlabel: returns the label (and other address book data) associated with an addressgetaddressesbylabel: get addresses labelled with one labellistlabels: list all labels (or labels with a certain purpose, such as receive/send)setlabel: assign a label to an addressgetlabeladdress: get the 'label address' for the specified label. This gets an unused address with the label, creating one if necessary should be removed according to discussionThese calls have a deprecated account parameter, which can be turned into a label-parameter as is:
listtransactionsOpen questions
'no', labels are just a name for one or more addresses, intuitively there is no "default address",
and it also isn't a GUI feature.
CAccountstructure due to this.TODO