Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
1. Motivation
Suppose you publish a Sumokoin wallet address for donation on your Twitter/etc profile page. Furthermore, you have an anonymous identity for some secret project where you also want to receive donation in Sumokoin. Because you don't want people to be able to associate you with that anonymous identity by Googling your address, you'd want to use separate wallet addresses for these two purposes. While you can easily do so by simply creating two wallets separately, you're going to spend twice the time for scanning the blockchain and need twice the storage for the wallet cache. And this cost grows in proportion to the number of additional wallet addresses you'd like to have.
Another relevant scenario is when you want to buy Sumokoin anonymously through an instant exchange like ShapeShift (note: you cannot buy SUMO from ShapeShift right now). If you used the same Sumokoin address repeatedly for multiple purchases, ShapeShift would know that the same person bought that much of Sumokoin through those purchases (although they don't know your contact info as they don't require account registration). You can anonymize the process by creating a temporary wallet for every purchase and transferring the fund to your real wallet afterwards. This is doable, but clearly tedious.
The scheme proposed in this PR provides an effective solution for such scenarios, where the recipient can create multiple wallet "subaddresses" that appear unrelated to each other to outside observers, and the recipient can recognize all incoming transfers to those subaddresses with almost no additional cost. Note that this scheme only introduces additional procedures for the wallet software to construct and interpret transactions, while not requiring any change in the consensus rule (thus requiring no hard fork).
2. How it works cryptographically
Suppose Bob's wallet address is
(A, B) = (a*G, b*G)
where a andb
are his view and spend secret keys, respectively. Likewise, Alice's address is(X, Y) = (x*G, y*G)
. Bob is receiving Sumokoin from Alice, but he wants to do so without using his real address(A, B)
in order to prevent Alice from knowing that the recipient is him. In the above example with ShapeShift, Bob would be you and Alice would be ShapeShift.2.1. Generating a subaddress
Bob generates his
i
-th subaddress(i=1,2,...)
as a pair of public keys(C,D)
where:We call
i
the index of the subaddress. Note that(A,B)
and(C,D)
are unlinkable without the knowledge of the view secret keya
. Bob then registersD
to a hash tableT
stored in his wallet cache (akin to the aggregate addresses scheme):To handle his main address in a unified way, Bob also registers
B
to the hash table:In other words, the index 0 is treated as a special case in this scheme representing the original standard address.
2.2. Sending to a subaddress
When Alice constructs a transaction that transfers some fund to a subaddress
(C, D)
, she first chooses a random scalars
and generates a tx pubkey:Note that the tx secret key
r
such thatR = r*G
is unknown to Alice because she doesn't know the secret key ofD
. She then computes an output pubkey for the destination:She finally computes an output pubkey to herself as her change:
Importantly, without the knowledge of the tx secret key
r
as explained above, Alice can include her change output in the transaction only because she knows her own view secret keyx
.Also note that Alice can prove her payment to Bob by using
s
.2.3. Receiving by a subaddress
Bob checks if an output pubkey
P
in a new transaction belongs to him or not by computingand looking for D' in the hash table. If the transaction was indeed bound to Bob's subaddress
(C,D)
,D'
should equal toD
becauseTherefore, Bob should be able to find
D'
in the hash table:and obtain the private key of
P
:2.4. Grouping subaddresses
When sending funds to Bob's subaddress
(C,D)
, Alice can choose to send the change to heri
-th subaddress(X_i,Y_i)
instead of her main address(X,Y)
by replacingY
withY_i
when deriving the change's output pubkey:Using this scheme, it's now possible to maintain balances of subaddresses separately, making them virtually function as separate wallets. In order to maintain subaddresses in an organized manner, we propose a simple scheme of grouping subaddresses as follows: we define the index of subaddresses as a pair of indices
(i,j)
withi
, the major index, representing a group of subaddresses (called an account) andj
, the minor index, representing a particular subaddress within that account. Incoming transfers to subaddresses belonging to the same account(i,0)
,(i,1)
, ... are summed up to form a single balance, and any spending of those outputs will transfer the change to the base subaddress(i,0)
. The index(0,0)
represents the original standard address.3. How it works in the CLI
3.1. Synopsis
Existing commands with updated syntax:
New commands:
3.2. Managing accounts
Initially, the wallet has only one account corresponding to the major index 0. The account index is indicated as the number after / in the command prompt:
You can create a new account by using the command
account new
:Note the change of the address. The command
account switch
lets you switch between accounts:The command
account
with no arguments shows the list of all the accounts along with their balances:At any time, the user operates on the currently selected account and specifies the minor index in various commands when necessary via the
index
parameter; i.e. in the following description, we use the term "index" to mean the minor index.3.3. Generating subaddresses
The command
address
shows the "base" address at index=0 (which corresponds to the original standard address when the currently selected account is at index=0). The commandaddress new
lets you create a new address at one index beyond the currently existing highest index associated with this account. You can assign a label to the address if necessary:Note that this command is intended to be used for generating "throwaway" addresses; i.e. when you just want a fresh new address to receive funds to the currently selected account (e.g. when receiving SUMO from a customer named Bob). If instead you want to maintain a separate balance associated with a new address, create a new account by the command
account new
.The commands
address all
andaddress <index_min> [<index_max>]
show lists of addresses that have been generated so far:The command
address label
lets you set the label of an address:Note that the label of the base address is treated as the label of the currently selected account. You can change account labels by using either
address label
oraccount label
:3.4. Checking funds and balances
Here the wallet finds a few incoming transfers by some of its subaddresses (indicated by the major-minor index pairs):
The command
balance
shows the total balance of all the funds received by all the addresses belonging to the currently selected account. You can see the details about how much funds are received by which address by providing an optional argumentdetail
:Other commands for showing detailed information about funds have some changes of syntax:
3.5. Transferring funds
The apparently unrelated subaddresses might get statistically linked if funds transferred to different subaddresses are used together as inputs in new transactions. For example, suppose Bob published two subaddresses
(C1,D1)
and(C2,D2)
in different places, and Alice repeatedly transferred funds to these addresses not knowing that they both belong to Bob. If Bob repeatedly used outputs received by these subaddresses together as inputs in new transactions, Alice would notice multiple instances of transactions where each of the input ring signatures contains the output she created for the payments to these subaddresses. The probability of such transactions occurring by chance would be fairly low, so she can confidently guess that these subaddresses belong to the same person.To prevent this kind of potential risk of linkability, the wallet tries its best to avoid using outputs belonging to different subaddresses together as inputs in a new transaction. If there exist any subaddresses with enough unlocked balance for the requested transfer, the wallet chooses one randomly. If there is no such subaddress, the wallet uses the minimal number of subaddresses to cover the required amount while showing a warning message. You can also explicitly tell the wallet from which subaddresses the outputs should be picked by using an optional argument
index=<N1>[,<N2>,...]
:The command
sweep_all
has a similar syntax:If you omit the index parameter, one subaddress to be swept is chosen randomly (with index 0 being chosen last). You can use
index=<N>
to specify which subaddress to be swept. You can also either useindex=<N1>,<N2>,...
to sweep balances of multiple or all subaddresses.Note that the change always goes to the base subaddress at index=0:
3.6. Restoring wallet from seed
One slight caveat with this scheme is that when restoring a wallet from the seed, the wallet might miss transfers to subaddresses if they aren't stored in the hashtable yet. To mitigate this issue, for each account, the wallet stores 200 (a constant
SUBADDRESS_LOOKAHEAD_MINOR
defined in wallet2.h) subaddresses of indices beyond the highest index created so far. The wallet also generates 50 (a constantSUBADDRESS_LOOKAHEAD_MAJOR
defined in wallet2.h) accounts beyond the highest index created so far. This means that the wallet restoration process is guaranteed to find incoming transfers to subaddresses as long as the major and minor indices of the used subaddresses differ by less than those predefined numbers. Note that the wallet expands the hashtable by itself as it finds incoming transfers to subaddresses at new higher indices, so normally the user won't need to worry about the management of the hashtable. Even if the differences of indices are bigger than those predifined numbers, you can still make the wallet recognize the incoming transfers by manually expanding the hashtable and rescanning the blockchain.4. Credit
This is fork from Monero subaddress PR monero-project/monero#2056 by @kenshi84 and other contributors.