-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support subaddresses in light wallet API #647
base: master
Are you sure you want to change the base?
Conversation
I have started work on adding this support in MyMonero as well, and it is pretty much in line with your proposal here already. A couple of things:
In order to support "generating" new subaddresses while offline, each client can pre-request a number of them from the server. The provisioning endpoint would have a parameter indicating how many new subaddresses should be returned at once. Thoughts? |
BTW, I don't think there can be any question that the API must be wallet-level. We do not want an API request for each subaddress, users can reasonably expect to have hundreds or thousands of them. Querying an individual subaddress could be supported in addition to that, if there is in fact a use case for that? |
Agree wasn't sure on this, will remove :)
Interesting consideration -- a cross-device wallet (any wallet, not just light) needs to point to a server to protect against this. I agree this is worth protecting here. On the solution:
I like this! At one point I had something along these lines too. I imagine clients would want a getter as well to see their already provisioned subaddresses. Also, I would think if you want to provision additional minor subaddresses, you would do so by account (i.e. let's say I want to provision an additional 10 minor subaddresses, I wouldn't want to do so across all 50 majors, just for the individual account I'm working with). Thus, there would be a different number of minor subaddresses by major, and so both getter and setter endpoints would be built around that. Thoughts on that? Or think it's fine to just assume constant # of provisioned minors in all majors? I would think this is more relevant for MyMonero, considering provisioning constant # of minor subaddresses across all majors would increase the lookahead impact when scanning, which has a more significant memory cost when you have tons and tons of accounts.
Agreed, this is what I was thinking with "Data by subaddress". Doesn't seem to be a necessary thing at this time unless people really want it. The best use case I can see is for users who have tons and tons of transactions across different accounts (major subaddresses), those users could have a better UX navigating the wallet by account. (Edit: but with this approach, you'd restructure wallet flow for all users to first retrieve provisioned subaddresses, then display accounts probably without balances, and allow clicking into each one) |
- /provision_subaddrs server guarantees freshly provisioned subaddrs on every successful call - /upsert_subaddrs enables one-off idempotent creation of subaddrs - /get_subaddrs enables getting subaddresses - also removed base-58 address from `address_meta` object since major and minor index are enough to re-calc address in the client
Added endpoints to create or retrieve subaddresses, and removed Collaborated with @paulshapiro to add 2 endpoints to create subaddresses (
I stuck with the convention to use descriptive endpoint prefixes (e.g. "get_") and kept them as POSTs. I think it's fairly self-explanatory as is, although not technically best practice. Perhaps a PUT |
Thoughts on something like (didn't include all potential query params )?, (also didn't scroll up and read all the way 😄 ):
|
@CryptoGrampy first thing, the reason I didn't follow RESTful convention (e.g. GET Going through your suggestions:
These 2 are covered by The additional proposed
So there is And then I proposed updating the existing I didn't include anything that enables you to retrieve data by subaddress, but would imagine those 3 getters could be updated to support |
Thoughts after having not seen this in a while :) :
|
Hey all, I figured I would mention for the record that I was pinged by jberman about a year ago and we collaborated for a few weeks on the subaddr API design. We ended up making some conclusions. I went and fully implemented everything, and even refined things slightly. My plan was to PR the enhancement to the spec asap but I've been dealing with some blockers so I hadn't gotten around to it yet. I'd be happy to post my work on that shortly. One of the things I've discovered over my past year of work is that we will certainly need to start thinking in versions of LWS - not specifically for the stuff described in this thread but for supporting other things properly. There is a flaw I discovered in the existing API that probably doesn't affect any existing use cases but as we start to bring LWS more in line with mainline, we will start to see those things. I will publish on them shortly. The main thing to know is, we don't need URL based versioning for these things, as I have enhanced the LWS core client code to be able to handle a newer versus older LWS. I believe this is a good plan going forward - no API versioning, where possible, so that new clients can still talk to old servers within the limits possible - and currently, it is just fine. I do not see a reason currently to version /get_address_info specifically - servers and clients are able to simply add and detect fields respectively for the changes we're discussing here. In terms of normalizing endpoint and field names, I will PR some of those suggestions I have. I will get more specific in the PRs but basically, yes, no reason to say "addresses" (addrs), in fact, no reason to say "subaddress" (subaddrs),... no reason to say "major" (maj), no reason to say "idx" (i), etc. This all saves on wire payload size provided no compression is occurring. But we still want clients to be as widely compatible as possible so I do not see a reason to go back and change /get_address_txs to /get_addr_txs for now. Maybe one day if we actually need a new endpoint. But those endpoints likely won't ever get made since /get_address_txs is pretty complete aside from one or two enhancements I will publish on soon. Realistically, we can actually fully deprecate /get_address_info - LWS clients do not really need it afaict. There's lots of stuff I have notes on that I'm waiting to make a tiny bit more progress on before I publish. I would appreciate support in collaborating on integrating these things as I've done a ton of work on the LWS over the past year or two after my years building the core client code and incidentally, a realtime version of the API, etc., I think I have some solid suggestions to share. |
api/lightwallet_rest.md
Outdated
|
||
| | Type | Description | | ||
|-------|-------------------------------|-----------------------------------------| | ||
| Key | `uint32-string` | Subaddress major index | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this uint32-string
? Why not just uint32
? Was this supposed to be a uint64-string
(seems unlikely)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uint32
is fine here. There's no reason to make it a string since it cannot be a uint64
.
|-----------|------------------|-------------------------------------------------| | ||
| address | `base58-address` | Standard address of the wallet | | ||
| view_key | `binary` | View key bytes | | ||
| subaddrs | `subaddrs` | Subaddresses to upsert | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if it already exists? The client can't tell if it's a new insert or an existing one. I suppose this is the difference between the other API call? Or should a HTTP error be returned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Upsert as in update unless exists in which case insert. Server should return its known set of subaddrs so client can know, for various reasons, which I will describe soon in some updates.
Over the past 1-2 years, I have done a massive amount of work on the LWS and discovered that the subaddr support in the LWS API as currently specced and as it even currently behaves is badly insufficient, so I will share a list of things I've discovered with some spec proposals shortly. I'm finishing it up now after collecting everything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
insert unless exists*
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@j-berman is this a single subaddrs
or an array? I'm assuming an array because the other instances (new_subaddrs
and all_subaddrs
) have to be arrays by design.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vtnerd it needs to be what i call a complex range of idxs. that is, a dictionary with maj idx str keys pointing to arrays of arrays of min idxs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds nearly identical to this design. A major index, pointing to an array of index ranges.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, it was your question, not anything major I'm claiming to point out yet, but btw I did intend to type array of arrays. you need support for discontiguous min idx ranges.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This API does support discontiguous min ranges. [10, [0,2],[4,7]]
means 0,1,2,4,5,6,7
minor indexes with major index 10
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but that is a discontiguous min range.
Had a thought today: Today, (I think) LWS can be used for something like a public donation monitor or fundraiser (or more). You could make your address and view key public (or even a list of public addresses/view keys for multiple fundraisers) , and it can be thrown on a static site that can ping a LWS API (front end call direct from browser)) and get live updates for anyone wanting to see the fundraising progress. There's not really any way that a public person visiting this website can do something nefarious even though they have your public address and view key. They can't do anything with these credentials to trigger the LWS server to change its data- even restore height can't be changed from the regular public facing API today. If there is an endpoint to provision subaddresses, I think this is a change to that paradigm in that anyone with access to the public and view key credentials will be able to make a nefarious change to the LWS server database. Not sure if this is a good or bad thing, but I think it's probably bad. |
hi grampy, |
The monero-LWS server will have a runtime configurable limit for subaddresses per account. A value of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@j-berman bumping this review once again, as there are two changes I think necessary:
- The
uint32-string
should beuint32
insubaddrs
dictionary. - The
transaction
object should have areceipient
field added to it. You can infer this via thespent_outputs
which will all mirror the value, but if there are no spent outputs the subaddress cannot be determined.
Thinking on it, it sounds like you want the light wallet client to be able to separate funds by subaddress in the UI via Worth noting Also worth noting |
Without additional changes to this PR, if the UI wants to display transactions by subaddresses (like the CLI wallet), it will have to use both Perhaps an optional filter by major index added to the
Yes, with subaddresses this becomes a total balance over all accounts calculation only. I'm wondering whether this should be updated too, because it makes the endpoint less useful to the typical UI setup. |
Yep makes sense.
SGTM with one nit.. the optional filter seems extra, no? Seems it's fine to just default return the array of objects in each transaction by default
Open to suggestions :) |
I think this is good now. |
UPDATE Apr 11: collaborated with @paulshapiro to add endpoints to create or retrieve subaddresses (
/provision_subaddrs
,/upsert_subaddrs
,/get_subaddrs
). Also in line with @ndorf 's comment to support this here.Overview
Hoping to reach consensus on how lightwallet servers should support subaddresses @vtnerd @ndorf @paulshapiro @moneroexamples. The changes proposed here are not so major, however a server that supports subaddresses as described would not be backwards compatible with a client that does not. I figure we can keep this proposal as a draft until there is a well-tested server-side and client-side implementation in place everyone is happy with.
Key highlights:
address
param in requests (not subaddresses)/get_address_info
,/get_address_txs
, and/get_unspent_outs
return wallet-level data, rather than just the info for the standard addressmaj_i
,min_i
) that enables the client to generate key images without needing to re-determine which subaddress received outputs/provision_subaddrs
/upsert_subaddrs
/get_subaddrs
wallet2
defaults to 50 major * 200 minor e.g.)the client would manage subaddress creation on its end without touching the server (client adds "accounts" and "addresses within accounts" locally, gives them out to counter-parties when requesting payment, then the server will pick up on the received outputs via a default lookahead when scanning for received outs)(UPDATE Apr 11: client can provision subaddresses via the server)Existing OpenMonero/monero-lws implementations
Currently
OpenMonero
supports subaddresses in place of theaddress
param in all requests. I have a PR tomonero-lws
that implements this same thing. However, I don't think this will end up being a great way to work with subaddresses in a lightwallet server in production for 3 main reasons:(1) This comment from @trasherdk:
It does not seem users would care much for subaddress-level data, and thus it just does not seem sensible for the API and backend to build around subaddress-based "accounts" over the
/login
endpoint.(2) it's not entirely clear how the client could smoothly retrieve a wallet's (or account's) entire balance. The default lookahead in wallet2 is 50*200 subaddresses, meaning I would guess the expected default for the wallet would be to make 10,000 requests to the server for its balance, which is unrealistic. Then if there are more subaddresses such that the lookahead should be bumped, the client would make more round trips to the server. This does not seem workable.
(3) it requires a quasi-hacky solution to prevent someone malicious from being able to tell that an honest user's subaddress is stored on a server, even if the malicious party doesn't know the honest user's view key. See the
monero-lws
PR for more discussion on this if you have a lot of time and are into that sorta thing.The goal of this proposal is to support subaddresses in a clean way that avoids the above pitfalls. Thus, I specifically propose not to allow subaddresses in place of the
address
param, and instead propose that API implementers follow this proposal to have a good time.Future extensions
Data by subaddress
If we find that clients actually do want to retrieve granular subaddress data over the endpoints, rather than have the server accept a subaddress in the
address
param of requests, requests can include amajor_index
(andminor_index
if needed) as additional params to thestandard address + view key
. This way the server doesn't need to perform any hacky and potentially unsafe authentication on subaddresses.JAMTIS
The idea that the endpoints should support a wallet-level abstraction rather than an address-level abstraction fits neatly with what is brewing with JAMTIS: the current research on the next gen address scheme for Monero. In JAMTIS, a lightwallet server need not even see user addresses at all, and could only return the wallet's plausible received outputs to the client. See this comment explaining the brewing design further.
Customizable lookahead(UPDATE Apr 11: customizable lookahead not needed anymore. Client can request server create arbitrary counts of new subaddresses)
If there is demand from lots of individual light wallet users to pre-load tons of subaddresses beyond the default lookahead, the server can also support a user-specified lookahead over the/login
endpoint for example. This seems like more of a nice-to-have than a necessity imo. I think we can get away without it until the transition to the next gen protocol.