Skip to content
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

Subaddresses #2056

Merged
merged 1 commit into from Oct 15, 2017
Merged

Subaddresses #2056

merged 1 commit into from Oct 15, 2017

Conversation

@kenshi84
Copy link
Contributor

@kenshi84 kenshi84 commented May 30, 2017

Note: This PR replaces #1753. Most importantly, the idea of disposable addresses was dropped, due to its potential danger of being misused by uneducated users which leads to on-chain tx linkability. See the relevant discussion here.

GUI version

1. Motivation

Suppose you publish a Monero 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 Monero. 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 Monero anonymously through ShapeShift. If you used the same Monero address repeatedly for multiple purchases, ShapeShift would know that the same person bought that much of Monero 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 and b are his view and spend secret keys, respectively. Likewise, Alice's address is (X, Y) = (x*G, y*G). Bob is receiving Monero 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:

m = Hs(a || i)
M = m*G
D = B + M
C = a*D

We call i the index of the subaddress. Note that (A,B) and (C,D) are unlinkable without the knowledge of the view secret key a. Bob then registers D to a hash table T stored in his wallet cache (akin to the aggregate addresses scheme):

T[D] <- i

To handle his main address in a unified way, Bob also registers B to the hash table:

T[B] <- 0

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 scalar s and generates a tx pubkey:

R = s*D

Note that the tx secret key r such that R = r*G is unknown to Alice because she doesn't know the secret key of D. She then computes an output pubkey for the destination:

P = Hs(s*C)*G + D

She finally computes an output pubkey to herself as her change:

Q = Hs(x*R)*G + Y

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 key x. In other words, only single-destination transfers are possible in this scheme, and multi-destination transfers (e.g. pool payouts) require the use of standard addresses. (2017-08-01) Now this scheme supports multi-destination transfers by introducing additional tx keys.

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 computing

D' = P - Hs(a*R)*G

and looking for D' in the hash table. If the transaction was indeed bound to Bob's subaddress (C,D), D' should equal to D because

a*R = a*s*D
    = s*a*D
    = s*C

Therefore, Bob should be able to find D' in the hash table:

i <- T[D']

and obtain the private key of P:

p = { Hs(a*R) + b                   i == 0
    { Hs(a*R) + b + Hs(a || i)      otherwise

2.4. Grouping subaddresses

When sending funds to Bob's subaddress (C,D), Alice can choose to send the change to her i-th subaddress (X_i,Y_i) instead of her main address (X,Y) by replacing Y with Y_i when deriving the change's output pubkey:

Q = Hs(x*R)*G + Y_i

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) with i, the major index, representing a group of subaddresses (called an account) and j, 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:

address
address new <label text with white spaces allowed>
address all
address <index_min> [<index_max>]
address label <index> <label text with white spaces allowed>

balance [detail]

show_transfers [in|out|pending|failed|pool] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]
incoming_transfers [available|unavailable] [index=<N1>[,<N2>,...]]
unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]

transfer [index=<N1>[,<N2>,...]] [<priority>] [<mixin_count>] <address> <amount> [<payment_id>]
locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<mixin_count>] <addr> <amount> <lockblocks> [<payment_id>]
donate [index=<N1>[,<N2>,...]] [<priority>] [<mixin_count>] <amount> [<payment_id>]

sweep_all [index=<N1>[,<N2>,...]] [<mixin_count>] <address> [<payment_id>]
sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<mixin_count>] address [<payment_id>]

New commands:

account
account new <label text with white spaces allowed>
account switch <index>
account label <index> <label text with white spaces allowed>

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:

[wallet/0 9uPkEU]: 

You can create a new account by using the command account new:

account new <label text with white spaces allowed>
[wallet/0 9uPkEU]: account new eBay selling   
        Account               Balance      Unlocked balance                 Label
       0 9uPkEU        0.000000000000        0.000000000000               Default
       1 Bd2qqa        0.000000000000        0.000000000000          eBay selling
----------------------------------------------------------------------------------
          Total        0.000000000000        0.000000000000

[wallet/1 Bd2qqa]: address
0  Bd2qqam2EKc8NRzTYg9QPJZ81Ey1WRp1Bcnmjzf28K3edRyr1rcLbjiQZdR7tsuTUJBahi32RSDb1cuqsACaEDwGRMZ4Eaa  eBay selling

Note the change of the address. The command account switch lets you switch between accounts:

[wallet/1 Bd2qqa]: account switch 0
Currently selected account: [0] Default
Balance: 0.000000000000, unlocked balance: 0.000000000000

[wallet/0 9uPkEU]: address
0  9uPkEUFcFujaw1YCDHhcvuUqQm9cGBBuu5KkGggFR9XMjQtXtFzUDbRU9oy4XYer9SeaSGAhtoT1xDsBuVDRqfg9GG2ji9U  Default

The command account with no arguments shows the list of all the accounts along with their balances:

[wallet/0 9uPkEU]: account
        Account               Balance      Unlocked balance                 Label
       0 9uPkEU        0.000000000000        0.000000000000               Default
       1 Bd2qqa        0.000000000000        0.000000000000          eBay selling
----------------------------------------------------------------------------------
          Total        0.000000000000        0.000000000000

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 command address 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:

address new <label text with white spaces allowed>
[wallet/0 9uPkEU]: address new ShapeShift purchase on 2017-Jul-08
1  BZ9PfXYboScf2vs4EcnMBdT2dvzmE1hLnZixxnmAP2vs52jQiLTmDzTDhdwVyuBWMhCeiTxD4bVCJipsfmGxtBTwSq4HwAh  ShapeShift purchase on 2017-Jul-08

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 purchasing XMR through ShapeShift). 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 and address <index_min> [<index_max>] show lists of addresses that have been generated so far:

[wallet/0 9uPkEU]: address all
0  9uPkEUFcFujaw1YCDHhcvuUqQm9cGBBuu5KkGggFR9XMjQtXtFzUDbRU9oy4XYer9SeaSGAhtoT1xDsBuVDRqfg9GG2ji9U  Default
1  BZ9PfXYboScf2vs4EcnMBdT2dvzmE1hLnZixxnmAP2vs52jQiLTmDzTDhdwVyuBWMhCeiTxD4bVCJipsfmGxtBTwSq4HwAh  ShapeShift purchase on 2017-Jul-08
2  BfhF533yqmNfxfgoDXK1411DYveWMU1xRQzMPPpHVrZgeoCoVrQ9GCjY1mvDQQaoiCZYhKhGVHLkm51cgZYfDUpt5Bw4ZgB  ShapeShift purchase on 2017-Jul-09
3  BhTfiQY1Xj6AKXPUtDbunJPmqrbkha5qvNgYoipp6L7FQ8KTDciDWjF3YQ5g2dCLNLjJoUEJKVeYt9Rpb8tgDzT36fSoS9D  
4  BYoaHkHjFEY371Jq9fY9x9TJjaMeDXBYS9AfqCJ7SJoe5vuJLDPsyhU3RzDFNXKkEqBDqQMX1bDuUQbXKXctTGXY9ZgdGa1  ShapeShift purchase on 2017-Jul-11
5  BcxioZpKqYaZsSDHP6pmesaV7gMrPYxvHXsnkG5ceVGkasPR83cEJyvGNpckYvs2wP76HsM1KGo1mbEmmiburnCUT4aSEv1  ShapeShift purchase on 2017-Jul-13
6  BZzdxkqM8fJhZLATeyD3bd2cSrehGsANH4CgyJ4DBUamJAdyRTqkZ1W3kvN1UpzLz1iQU8XfEQUi15QyobFyMQVb3LDj4gZ  ShapeShift purchase on 2017-Jul-17
7  BYNUQQfEtg2gshraM8RPGvPUNbWcHHu33Tzhxy8ZrL7r7MYDkdPUCDFYxNawBNSAdjhKUCsiWjg5tQoReRMKGQ1fJS2QuEB  ShapeShift purchase on 2017-Jul-21

[wallet/0 9uPkEU]: address 3 6
3  BhTfiQY1Xj6AKXPUtDbunJPmqrbkha5qvNgYoipp6L7FQ8KTDciDWjF3YQ5g2dCLNLjJoUEJKVeYt9Rpb8tgDzT36fSoS9D  
4  BYoaHkHjFEY371Jq9fY9x9TJjaMeDXBYS9AfqCJ7SJoe5vuJLDPsyhU3RzDFNXKkEqBDqQMX1bDuUQbXKXctTGXY9ZgdGa1  ShapeShift purchase on 2017-Jul-11
5  BcxioZpKqYaZsSDHP6pmesaV7gMrPYxvHXsnkG5ceVGkasPR83cEJyvGNpckYvs2wP76HsM1KGo1mbEmmiburnCUT4aSEv1  ShapeShift purchase on 2017-Jul-13
6  BZzdxkqM8fJhZLATeyD3bd2cSrehGsANH4CgyJ4DBUamJAdyRTqkZ1W3kvN1UpzLz1iQU8XfEQUi15QyobFyMQVb3LDj4gZ  ShapeShift purchase on 2017-Jul-17

The command integrated_address also takes an optional argument to specify the index:
(2017-08-01) The integrated subaddresses format was dropped as subaddresses are expected to be replacing the role of payment IDs

The command address label lets you set the label of an address:

address label <index> <label text with white spaces allowed>
[wallet/0 9uPkEU]: address label 3 test test
3  BhTfiQY1Xj6AKXPUtDbunJPmqrbkha5qvNgYoipp6L7FQ8KTDciDWjF3YQ5g2dCLNLjJoUEJKVeYt9Rpb8tgDzT36fSoS9D  test test

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 or account label:

[wallet/0 9uPkEU]: address label 0 My home account  
0  9uPkEUFcFujaw1YCDHhcvuUqQm9cGBBuu5KkGggFR9XMjQtXtFzUDbRU9oy4XYer9SeaSGAhtoT1xDsBuVDRqfg9GG2ji9U  My home account

[wallet/0 9uPkEU]: account label 1 My secret business account
        Account               Balance      Unlocked balance                 Label
       0 9uPkEU        0.000000000000        0.000000000000       My home account
       1 Bd2qqa        0.000000000000        0.000000000000 My secret business account
----------------------------------------------------------------------------------
          Total        0.000000000000        0.000000000000

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):

Height 949115, txid <2c9d55d65f67243f771729ecd93a089ca0fcde3451f5a7740a4df70d8240b95b>, 0.130000000000 XMR, idx 0/1
Height 949115, txid <68ee8848bbb953e3a0bc8d3175e8724c3fb04cd2e49244c76d26d5b2fa2a6da2>, 0.020000000000 XMR, idx 0/0
Height 949115, txid <01f3865e63613413f6de4d4cd2b2638b679aa604c06299ad4b562364afc8cf5a>, 0.110000000000 XMR, idx 0/1
Height 949115, txid <b3bd13988448ef3bb95e6f3bf007f42ebc4f0d21346b6c4d45e42d53caa3e297>, 0.120000000000 XMR, idx 0/1
Height 949115, txid <05a97709858358ec8bb008cc8ad7f70e35ec07219cc8744138473200bb9922ec>, 0.230000000000 XMR, idx 0/2
Height 949115, txid <9a1527acdce60bbe38c8c7e9d3bfe894108d5b30eb630904a3b3afd5059b3344>, 0.210000000000 XMR, idx 0/2
Height 949115, txid <6716c0707b4c198777adbcda6b630b6248ef07fb86d20ba4469dcf661f5b35ee>, 0.010000000000 XMR, idx 0/0
Height 949115, txid <04bf1d9a5b8b3fe74d26bd068844f4709b22c92f6a3b5594f217c22df88f8184>, 0.030000000000 XMR, idx 0/0
Height 949115, txid <02b76b48467a806c6e45ce6fdf59df3c694f857f450a450537a9769f911fee46>, 0.220000000000 XMR, idx 0/2
Height 949116, txid <92f3b54a3044eddf3fd84742852f8a81b55e92055281c945bd7dee85e589ccc2>, 1.010000000000 XMR, idx 1/0
Height 949117, txid <d2617a1da91203247f8b904cf05906973cc9a64bff8d6ab4d956f22cb5d5271e>, 1.030000000000 XMR, idx 1/0
Height 949117, txid <441b07d91f64adf1b4f8350ca33902ed4aab306df3f66e1673d35a151186140c>, 1.020000000000 XMR, idx 1/0
Height 949126, txid <0f5fcd710b94e49b61fedf93de92b444021292fbffb57c281ee6952190dc0bbf>, 1.110000000000 XMR, idx 1/1
Height 949126, txid <73a83e4de08d84d9d72c707c0406fcba92994a56feb42102e3ca7c3733613c5e>, 1.210000000000 XMR, idx 1/2
Height 949126, txid <9bdba78cd150cf4027282cdabb5893c0c7fb4c0d8ddf0f9096598b4bea87af69>, 1.230000000000 XMR, idx 1/2
Height 949126, txid <22e4ac8469654903da34ce06ab06840648338d9d9dfc3f59714777d7a5a5256b>, 1.120000000000 XMR, idx 1/1
Height 949126, txid <36b14c61ad93c4a8d16f10d88525222391717d87099bb30cd8c3da6c90849818>, 1.130000000000 XMR, idx 1/1
Height 949126, txid <99b7c48a24e44c3336dc0f4b884ad33e89668e4dd9c288c67e60ffea9506ed26>, 1.220000000000 XMR, idx 1/2
[wallet/0 9uPkEU]: account
        Account               Balance      Unlocked balance                 Label
       0 9uPkEU        1.080000000000        1.080000000000       My home account
       1 Bd2qqa       10.080000000000       10.080000000000 My secret business account
----------------------------------------------------------------------------------
          Total       11.160000000000       11.160000000000

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 argument detail:

[wallet/0 9uPkEU]: balance
Currently selected account: [0] My home account
Balance: 1.080000000000, unlocked balance: 1.080000000000

[wallet/0 9uPkEU]: balance detail
Currently selected account: [0] My home account
Balance: 1.080000000000, unlocked balance: 1.080000000000
Balance per address:
        Address               Balance      Unlocked balance                 Label Outputs
       0 9uPkEU        0.060000000000        0.060000000000       My home account       3
       1 BZ9PfX        0.360000000000        0.360000000000 ShapeShift purchase on 2017-Jul-08       3
       2 BfhF53        0.660000000000        0.660000000000 ShapeShift purchase on 2017-Jul-09       3

[wallet/0 9uPkEU]: account switch 1
Currently selected account: [1] My secret business account
Balance: 10.080000000000, unlocked balance: 10.080000000000

[wallet/1 Bd2qqa]: balance detail
Currently selected account: [1] My secret business account
Balance: 10.080000000000, unlocked balance: 10.080000000000
Balance per address:
        Address               Balance      Unlocked balance                 Label Outputs
       0 Bd2qqa        3.060000000000        3.060000000000 My secret business account       3
       1 BcGvNd        3.360000000000        3.360000000000                             3
       2 BgtuJh        3.660000000000        3.660000000000                             3

Other commands for showing detailed information about funds have some changes of syntax:

show_transfers [in|out|pending|failed|pool] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]
incoming_transfers [available|unavailable] [index=<N1>[,<N2>,...]]
[wallet/1 Bd2qqa]: show_transfers index=0
  949116     in      03:34:43 AM       1.010000000000 92f3b54a3044eddf3fd84742852f8a81b55e92055281c945bd7dee85e589ccc2 0000000000000000 0 - 
  949117     in      03:35:30 AM       1.030000000000 d2617a1da91203247f8b904cf05906973cc9a64bff8d6ab4d956f22cb5d5271e 0000000000000000 0 - 
  949117     in      03:35:30 AM       1.020000000000 441b07d91f64adf1b4f8350ca33902ed4aab306df3f66e1673d35a151186140c 0000000000000000 0 - 

[wallet/1 Bd2qqa]: incoming_transfers index=1,2
               amount   spent    unlocked  ringct    global index                                                               tx id      addr index
       1.110000000000       F    unlocked  RingCT          219516  <0f5fcd710b94e49b61fedf93de92b444021292fbffb57c281ee6952190dc0bbf>               1
       1.210000000000       F    unlocked  RingCT          219518  <73a83e4de08d84d9d72c707c0406fcba92994a56feb42102e3ca7c3733613c5e>               2
       1.230000000000       F    unlocked  RingCT          219520  <9bdba78cd150cf4027282cdabb5893c0c7fb4c0d8ddf0f9096598b4bea87af69>               2
       1.120000000000       F    unlocked  RingCT          219522  <22e4ac8469654903da34ce06ab06840648338d9d9dfc3f59714777d7a5a5256b>               1
       1.130000000000       F    unlocked  RingCT          219524  <36b14c61ad93c4a8d16f10d88525222391717d87099bb30cd8c3da6c90849818>               1
       1.220000000000       F    unlocked  RingCT          219526  <99b7c48a24e44c3336dc0f4b884ad33e89668e4dd9c288c67e60ffea9506ed26>               2

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>,...]:

transfer [index=<N1>[,<N2>,...]] [<priority>] [<mixin_count>] <address> <amount> [<payment_id>]
[wallet/1 Bd2qqa]: balance detail
Currently selected account: [1] My secret business account
Balance: 10.080000000000, unlocked balance: 10.080000000000
Balance per address:
        Address               Balance      Unlocked balance                 Label Outputs
       0 Bd2qqa        3.060000000000        3.060000000000 My secret business account       3
       1 BcGvNd        3.360000000000        3.360000000000                             3
       2 BgtuJh        3.660000000000        3.660000000000                             3

[wallet/1 Bd2qqa]: transfer 9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk 3  
Spending from address index 2
Sending 3.000000000000.  The transaction fee is 0.023390640000
Is this okay?  (Y/Yes/N/No): n
Error: transaction cancelled.

[wallet/1 Bd2qqa]: transfer 9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk 3
Spending from address index 1
Sending 3.000000000000.  The transaction fee is 0.023390640000
Is this okay?  (Y/Yes/N/No): n
Error: transaction cancelled.

[wallet/1 Bd2qqa]: transfer 9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk 3.2
Spending from address index 1
Sending 3.200000000000.  The transaction fee is 0.023390640000
Is this okay?  (Y/Yes/N/No): n
Error: transaction cancelled.

[wallet/1 Bd2qqa]: transfer 9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk 3.2
Spending from address index 2
Sending 3.200000000000.  The transaction fee is 0.023390640000
Is this okay?  (Y/Yes/N/No): n
Error: transaction cancelled.

[wallet/1 Bd2qqa]: transfer 9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk 3.5
Spending from address index 2
Sending 3.500000000000.  The transaction fee is 0.023390640000
Is this okay?  (Y/Yes/N/No): n
Error: transaction cancelled.

[wallet/1 Bd2qqa]: transfer 9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk 5
Spending from address index 1
Spending from address index 2
WARNING: Outputs of multiple addresses are being used together.
Sending 5.000000000000.  The transaction fee is 0.025061400000
Is this okay?  (Y/Yes/N/No): n
Error: transaction cancelled.

[wallet/1 Bd2qqa]: transfer index=0,1 9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk 5
Spending from address index 0
Spending from address index 1
WARNING: Outputs of multiple addresses are being used together.
Sending 5.000000000000.  The transaction fee is 0.025061400000
Is this okay?  (Y/Yes/N/No): n
Error: transaction cancelled.

[wallet/1 Bd2qqa]: transfer 9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk 9
Spending from address index 0
Spending from address index 1
Spending from address index 2
WARNING: Outputs of multiple addresses are being used together.
Sending 9.000000000000.  The transaction fee is 0.028402920000
Is this okay?  (Y/Yes/N/No): n
Error: transaction cancelled.

The command sweep_all has a similar syntax:

sweep_all [index=<N1>[,<N2>,...]] [<mixin_count>] <address> [<payment_id>]

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 use index=<N1>,<N2>,... to sweep balances of multiple or all subaddresses.

[wallet/1 Bd2qqa]: sweep_all 9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk
Spending from address index 1
Sweeping 3.360000000000 for a total fee of 0.023390080000.  Is this okay?  (Y/Yes/N/No)n
Error: transaction cancelled.

[wallet/1 Bd2qqa]: sweep_all index=1,2 9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk
Spending from address index 1
Spending from address index 2
WARNING: Outputs of multiple addresses are being used together.
Sweeping 7.020000000000 for a total fee of 0.025060800000.  Is this okay?  (Y/Yes/N/No)n
Error: transaction cancelled.

Note that the change always goes to the base subaddress at index=0:

[wallet/1 Bd2qqa]: transfer 9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk 1
Spending from address index 1
Sending 1.000000000000.  The transaction fee is 0.021719360000
Is this okay?  (Y/Yes/N/No): y
Money successfully sent, transaction <6f6678b4c6b3c60bcb7eb642bc819a1b99e9c75feb7c292b3656ea7b3b4f1a62>

Height 949248, txid <6f6678b4c6b3c60bcb7eb642bc819a1b99e9c75feb7c292b3656ea7b3b4f1a62>, 0.088280640000 XMR, idx 1/0
Height 949248, txid <6f6678b4c6b3c60bcb7eb642bc819a1b99e9c75feb7c292b3656ea7b3b4f1a62>, spent 1.110000000000, account 1

[wallet/1 Bd2qqa]: balance detail
Currently selected account: [1] My secret business account
Balance: 9.058280640000, unlocked balance: 8.970000000000
Balance per address:
        Address               Balance      Unlocked balance                 Label Outputs
       0 Bd2qqa        3.148280640000        3.060000000000 My secret business account       4
       1 BcGvNd        2.250000000000        2.250000000000                             2
       2 BgtuJh        3.660000000000        3.660000000000                             3

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 constant SUBADDRESS_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.

Here's an example: First, we generate 360 new addresses for the 1st account:

[wallet/1 Bd2qqa]: address all
0  Bd2qqam2EKc8NRzTYg9QPJZ81Ey1WRp1Bcnmjzf28K3edRyr1rcLbjiQZdR7tsuTUJBahi32RSDb1cuqsACaEDwGRMZ4Eaa  My secret business account (used)
1  BcGvNdmZj9V9r7NKAKzEELUVVSVfXMmUUbfQrjSBuDt6MU5bdtmYAEC6TR5VuZQv5yZvynzQ3aoJTUjtM9FD5AnP9PCHVSk   (used)
2  BgtuJhWFPi9DABnpdtQs9rWfQN33DZQZi2yzoQucEAP8EoPxNDitssoA14ZVQthSyPHWB8SLV48qAc25A1EykehQ7NzDXDb   (used)
3  BYfn1Fc5xwECX2HbNATqfuQCG82sMZufY46ZsiP1xBbR3S6fVj2P4d4MLRrqVBZcFHUQmdiCsKa3ec5aaNVk35ZYHQnLbu1  
...
150  BgjoPu6qBNVT1E9c3PDaaw9zbehhG29vBQAH8mSeC79K7z2qb12ZtTH1ijo4Tn7DYQPm2aZq68yrnLcoi18baf9JQxmn2bZ  
...
360  BZ1xAbycuHyKwangpgEn7NTWk2geNbcJgJ46iCkRBu8Gc2UXTACeRcUGWneGA1mPfkKmpy7ya4ST9dZex3RN2qi5BLdQRxy  

Next, we generate 70 accounts and print the base addresses of the 15th and 70th accounts:

[wallet/70 BbXfcf]: account
        Account               Balance      Unlocked balance                 Label
       0 9uPkEU        1.080000000000        1.080000000000       My home account
       1 Bd2qqa        9.058280640000        9.058280640000 My secret business account
       2 BaTkbd        0.000000000000        0.000000000000                      
       3 BeaUVm        0.000000000000        0.000000000000                      
...
      70 BbXfcf        0.000000000000        0.000000000000                      
----------------------------------------------------------------------------------
          Total       10.138280640000       10.138280640000

[wallet/0 9uPkEU]: account switch 15
Currently selected account: [15] 
Balance: 0.000000000000, unlocked balance: 0.000000000000

[wallet/15 BhQjPZ]: address
0  BhQjPZ8XUkojSdcB8jhB4uTeqLQVHtfsYTxTm3n32yXM37qXqN8RdTXduKSEH4rrAp4ZuUFbv8Wak3i1QudCJhvwJVUU5dx  

[wallet/15 BhQjPZ]: account switch 70
Currently selected account: [70] 
Balance: 0.000000000000, unlocked balance: 0.000000000000

[wallet/70 BbXfcf]: address
0  BbXfcfWRiUAcG7hoXvdRY8QtVisRPj1EQEbMoWWHGPXqQKVKWoSNNgbPEuSFm6YuDHUVaWwyqu876WKZrUmVMk8DLsrSMSN  

Then we send funds to these addresses (at indices of 1/150, 1/360, 15/0, 70/0) from another wallet:

[wallet/0 9svHk1]: transfer BgjoPu6qBNVT1E9c3PDaaw9zbehhG29vBQAH8mSeC79K7z2qb12ZtTH1ijo4Tn7DYQPm2aZq68yrnLcoi18baf9JQxmn2bZ 1  
Money successfully sent, transaction <ddb1d29f54f4de8e88fddd31379d6bf30ba4d311ae648d05c9962cfb59e494b1>

[wallet/0 9svHk1]: transfer BZ1xAbycuHyKwangpgEn7NTWk2geNbcJgJ46iCkRBu8Gc2UXTACeRcUGWneGA1mPfkKmpy7ya4ST9dZex3RN2qi5BLdQRxy 2
Money successfully sent, transaction <c4292519b328fcfa8f92300d55a33a1d9a2b1788b0988c501adea6a51452f52a>

[wallet/0 9svHk1]: transfer BhQjPZ8XUkojSdcB8jhB4uTeqLQVHtfsYTxTm3n32yXM37qXqN8RdTXduKSEH4rrAp4ZuUFbv8Wak3i1QudCJhvwJVUU5dx 3
Money successfully sent, transaction <8c473e823e6b1262a55b8456c19f1b9fba0090c089222435c3bccdbeac10fc99>

[wallet/0 9svHk1]: transfer BbXfcfWRiUAcG7hoXvdRY8QtVisRPj1EQEbMoWWHGPXqQKVKWoSNNgbPEuSFm6YuDHUVaWwyqu876WKZrUmVMk8DLsrSMSN 4
Money successfully sent, transaction <27d55c3af091cccb9e9fc6eb17f0e3e6ff700af066fb6471a3533c4f19af70c3>

Now we delete the cache of the receiving wallet 9uPkEU and re-generate it by scanning the blockchain again (which gives the same effect as restoring the wallet from the seed):

Monero 'Wolfram Warptangent' (v0.10.3.1-a261d3b)
Logging to /Users/kenshi/dev/github/kenshi84/monero/build/release/bin/monero-wallet-cli.log
Wallet password: *
Opened wallet: 9uPkEUFcFujaw1YCDHhcvuUqQm9cGBBuu5KkGggFR9XMjQtXtFzUDbRU9oy4XYer9SeaSGAhtoT1xDsBuVDRqfg9GG2ji9U
**********************************************************************
Use "help" command to see the list of available commands.
**********************************************************************
Starting refresh...
Height 949115, txid <2c9d55d65f67243f771729ecd93a089ca0fcde3451f5a7740a4df70d8240b95b>, 0.130000000000 XMR, idx 0/1
Height 949115, txid <68ee8848bbb953e3a0bc8d3175e8724c3fb04cd2e49244c76d26d5b2fa2a6da2>, 0.020000000000 XMR, idx 0/0
Height 949115, txid <01f3865e63613413f6de4d4cd2b2638b679aa604c06299ad4b562364afc8cf5a>, 0.110000000000 XMR, idx 0/1
Height 949115, txid <b3bd13988448ef3bb95e6f3bf007f42ebc4f0d21346b6c4d45e42d53caa3e297>, 0.120000000000 XMR, idx 0/1
Height 949115, txid <05a97709858358ec8bb008cc8ad7f70e35ec07219cc8744138473200bb9922ec>, 0.230000000000 XMR, idx 0/2
Height 949115, txid <9a1527acdce60bbe38c8c7e9d3bfe894108d5b30eb630904a3b3afd5059b3344>, 0.210000000000 XMR, idx 0/2
Height 949115, txid <6716c0707b4c198777adbcda6b630b6248ef07fb86d20ba4469dcf661f5b35ee>, 0.010000000000 XMR, idx 0/0
Height 949115, txid <04bf1d9a5b8b3fe74d26bd068844f4709b22c92f6a3b5594f217c22df88f8184>, 0.030000000000 XMR, idx 0/0
Height 949115, txid <02b76b48467a806c6e45ce6fdf59df3c694f857f450a450537a9769f911fee46>, 0.220000000000 XMR, idx 0/2
Height 949116, txid <92f3b54a3044eddf3fd84742852f8a81b55e92055281c945bd7dee85e589ccc2>, 1.010000000000 XMR, idx 1/0
Height 949117, txid <d2617a1da91203247f8b904cf05906973cc9a64bff8d6ab4d956f22cb5d5271e>, 1.030000000000 XMR, idx 1/0
Height 949117, txid <441b07d91f64adf1b4f8350ca33902ed4aab306df3f66e1673d35a151186140c>, 1.020000000000 XMR, idx 1/0
Height 949126, txid <0f5fcd710b94e49b61fedf93de92b444021292fbffb57c281ee6952190dc0bbf>, 1.110000000000 XMR, idx 1/1
Height 949126, txid <73a83e4de08d84d9d72c707c0406fcba92994a56feb42102e3ca7c3733613c5e>, 1.210000000000 XMR, idx 1/2
Height 949126, txid <9bdba78cd150cf4027282cdabb5893c0c7fb4c0d8ddf0f9096598b4bea87af69>, 1.230000000000 XMR, idx 1/2
Height 949126, txid <22e4ac8469654903da34ce06ab06840648338d9d9dfc3f59714777d7a5a5256b>, 1.120000000000 XMR, idx 1/1
Height 949126, txid <36b14c61ad93c4a8d16f10d88525222391717d87099bb30cd8c3da6c90849818>, 1.130000000000 XMR, idx 1/1
Height 949126, txid <99b7c48a24e44c3336dc0f4b884ad33e89668e4dd9c288c67e60ffea9506ed26>, 1.220000000000 XMR, idx 1/2
Height 949248, txid <6f6678b4c6b3c60bcb7eb642bc819a1b99e9c75feb7c292b3656ea7b3b4f1a62>, 0.088280640000 XMR, idx 1/0
Height 949248, txid <6f6678b4c6b3c60bcb7eb642bc819a1b99e9c75feb7c292b3656ea7b3b4f1a62>, spent 1.110000000000, account 1
Height 949344, txid <ddb1d29f54f4de8e88fddd31379d6bf30ba4d311ae648d05c9962cfb59e494b1>, 1.000000000000 XMR, idx 1/150
Height 949344, txid <8c473e823e6b1262a55b8456c19f1b9fba0090c089222435c3bccdbeac10fc99>, 3.000000000000 XMR, idx 15/0
Refresh done, blocks received: 24696                            
        Account               Balance      Unlocked balance                 Label
       0 9uPkEU        1.080000000000        1.080000000000               Default
       1 Bd2qqa       10.058280640000        9.058280640000                      
       2 BaTkbd        0.000000000000        0.000000000000                      
       3 BeaUVm        0.000000000000        0.000000000000                      
       4 BfH7tK        0.000000000000        0.000000000000                      
       5 BfDmWH        0.000000000000        0.000000000000                      
       6 BfwsE7        0.000000000000        0.000000000000                      
       7 Bam7fW        0.000000000000        0.000000000000                      
       8 BePNm3        0.000000000000        0.000000000000                      
       9 BgkPAD        0.000000000000        0.000000000000                      
      10 BYQtBx        0.000000000000        0.000000000000                      
      11 BZEjtF        0.000000000000        0.000000000000                      
      12 BbkUr7        0.000000000000        0.000000000000                      
      13 Bg34Jy        0.000000000000        0.000000000000                      
      14 BYqzux        0.000000000000        0.000000000000                      
      15 BhQjPZ        3.000000000000        0.000000000000                      
----------------------------------------------------------------------------------
          Total       14.138280640000       10.138280640000
Currently selected account: [0] Default
Balance: 1.080000000000, unlocked balance: 1.080000000000
Background refresh thread started

Note that at this point, the wallet was able to successfully recognize incoming transfers to indices of 1/150 and 15/0, but failed to recognize incoming transfers to 1/360 and 70/0 because 360 > 150 + SUBADDRESS_LOOKAHEAD_MINOR and 70 > 15 + SUBADDRESS_LOOKAHEAD_MAJOR. You can make the wallet recognize those incoming transfers by manually expanding the hashtable and rescanning the blockchain:

[wallet/1 Bd2qqa]: address new
151  BaTjSeUuSX4KtS6DmQXRfcN3xkTAyuG2NMg3r6JpR4JujotNw8dvGKBbVehURSoMSpSsKJQTWi4eQCJxsjXuXber9WUKQ53  

...

[wallet/1 Bd2qqa]: address new
161  BbA5zmZzn9ZPvzazMHBjQzXZHaYDjWeWfN258AZufhPK3i2uF6GCdvGMMksA7qMmXxcswEfkwucuuJPDYGvR77Ux7iczJzD  

[wallet/1 Bd2qqa]: account new
        Account               Balance      Unlocked balance                 Label
       0 9uPkEU        1.080000000000        1.080000000000               Default
       1 Bd2qqa       10.058280640000        9.058280640000                      
       2 BaTkbd        0.000000000000        0.000000000000                      
       3 BeaUVm        0.000000000000        0.000000000000                      
       4 BfH7tK        0.000000000000        0.000000000000                      
       5 BfDmWH        0.000000000000        0.000000000000                      
       6 BfwsE7        0.000000000000        0.000000000000                      
       7 Bam7fW        0.000000000000        0.000000000000                      
       8 BePNm3        0.000000000000        0.000000000000                      
       9 BgkPAD        0.000000000000        0.000000000000                      
      10 BYQtBx        0.000000000000        0.000000000000                      
      11 BZEjtF        0.000000000000        0.000000000000                      
      12 BbkUr7        0.000000000000        0.000000000000                      
      13 Bg34Jy        0.000000000000        0.000000000000                      
      14 BYqzux        0.000000000000        0.000000000000                      
      15 BhQjPZ        3.000000000000        0.000000000000                      
      16 BePZ3e        0.000000000000        0.000000000000                      
----------------------------------------------------------------------------------
          Total       14.138280640000       10.138280640000

...

[wallet/20 BgjB9f]: account new
        Account               Balance      Unlocked balance                 Label
       0 9uPkEU        1.080000000000        1.080000000000               Default
       1 Bd2qqa       12.058280640000        9.058280640000                      
       2 BaTkbd        0.000000000000        0.000000000000                      
       3 BeaUVm        0.000000000000        0.000000000000                      
       4 BfH7tK        0.000000000000        0.000000000000                      
       5 BfDmWH        0.000000000000        0.000000000000                      
       6 BfwsE7        0.000000000000        0.000000000000                      
       7 Bam7fW        0.000000000000        0.000000000000                      
       8 BePNm3        0.000000000000        0.000000000000                      
       9 BgkPAD        0.000000000000        0.000000000000                      
      10 BYQtBx        0.000000000000        0.000000000000                      
      11 BZEjtF        0.000000000000        0.000000000000                      
      12 BbkUr7        0.000000000000        0.000000000000                      
      13 Bg34Jy        0.000000000000        0.000000000000                      
      14 BYqzux        0.000000000000        0.000000000000                      
      15 BhQjPZ        3.000000000000        0.000000000000                      
      16 BePZ3e        0.000000000000        0.000000000000                      
      17 BcMMgP        0.000000000000        0.000000000000                      
      18 Bf7N2v        0.000000000000        0.000000000000                      
      19 BbNHEQ        0.000000000000        0.000000000000                      
      20 BgjB9f        0.000000000000        0.000000000000                      
      21 Bd1dhR        0.000000000000        0.000000000000                      
----------------------------------------------------------------------------------
          Total       16.138280640000       10.138280640000


[wallet/21 Bd1dhR]: rescan_bc
Starting refresh...
Height 949115, txid <2c9d55d65f67243f771729ecd93a089ca0fcde3451f5a7740a4df70d8240b95b>, 0.130000000000 XMR, idx 0/1
Height 949115, txid <68ee8848bbb953e3a0bc8d3175e8724c3fb04cd2e49244c76d26d5b2fa2a6da2>, 0.020000000000 XMR, idx 0/0
Height 949115, txid <01f3865e63613413f6de4d4cd2b2638b679aa604c06299ad4b562364afc8cf5a>, 0.110000000000 XMR, idx 0/1
Height 949115, txid <b3bd13988448ef3bb95e6f3bf007f42ebc4f0d21346b6c4d45e42d53caa3e297>, 0.120000000000 XMR, idx 0/1
Height 949115, txid <05a97709858358ec8bb008cc8ad7f70e35ec07219cc8744138473200bb9922ec>, 0.230000000000 XMR, idx 0/2
Height 949115, txid <9a1527acdce60bbe38c8c7e9d3bfe894108d5b30eb630904a3b3afd5059b3344>, 0.210000000000 XMR, idx 0/2
Height 949115, txid <6716c0707b4c198777adbcda6b630b6248ef07fb86d20ba4469dcf661f5b35ee>, 0.010000000000 XMR, idx 0/0
Height 949115, txid <04bf1d9a5b8b3fe74d26bd068844f4709b22c92f6a3b5594f217c22df88f8184>, 0.030000000000 XMR, idx 0/0
Height 949115, txid <02b76b48467a806c6e45ce6fdf59df3c694f857f450a450537a9769f911fee46>, 0.220000000000 XMR, idx 0/2
Height 949116, txid <92f3b54a3044eddf3fd84742852f8a81b55e92055281c945bd7dee85e589ccc2>, 1.010000000000 XMR, idx 1/0
Height 949117, txid <d2617a1da91203247f8b904cf05906973cc9a64bff8d6ab4d956f22cb5d5271e>, 1.030000000000 XMR, idx 1/0
Height 949117, txid <441b07d91f64adf1b4f8350ca33902ed4aab306df3f66e1673d35a151186140c>, 1.020000000000 XMR, idx 1/0
Height 949126, txid <0f5fcd710b94e49b61fedf93de92b444021292fbffb57c281ee6952190dc0bbf>, 1.110000000000 XMR, idx 1/1
Height 949126, txid <73a83e4de08d84d9d72c707c0406fcba92994a56feb42102e3ca7c3733613c5e>, 1.210000000000 XMR, idx 1/2
Height 949126, txid <9bdba78cd150cf4027282cdabb5893c0c7fb4c0d8ddf0f9096598b4bea87af69>, 1.230000000000 XMR, idx 1/2
Height 949126, txid <22e4ac8469654903da34ce06ab06840648338d9d9dfc3f59714777d7a5a5256b>, 1.120000000000 XMR, idx 1/1
Height 949126, txid <36b14c61ad93c4a8d16f10d88525222391717d87099bb30cd8c3da6c90849818>, 1.130000000000 XMR, idx 1/1
Height 949126, txid <99b7c48a24e44c3336dc0f4b884ad33e89668e4dd9c288c67e60ffea9506ed26>, 1.220000000000 XMR, idx 1/2
Height 949248, txid <6f6678b4c6b3c60bcb7eb642bc819a1b99e9c75feb7c292b3656ea7b3b4f1a62>, 0.088280640000 XMR, idx 1/0
Height 949248, txid <6f6678b4c6b3c60bcb7eb642bc819a1b99e9c75feb7c292b3656ea7b3b4f1a62>, spent 1.110000000000, account 1
Height 949344, txid <c4292519b328fcfa8f92300d55a33a1d9a2b1788b0988c501adea6a51452f52a>, 2.000000000000 XMR, idx 1/360
Height 949344, txid <ddb1d29f54f4de8e88fddd31379d6bf30ba4d311ae648d05c9962cfb59e494b1>, 1.000000000000 XMR, idx 1/150
Height 949344, txid <27d55c3af091cccb9e9fc6eb17f0e3e6ff700af066fb6471a3533c4f19af70c3>, 4.000000000000 XMR, idx 70/0
Height 949344, txid <8c473e823e6b1262a55b8456c19f1b9fba0090c089222435c3bccdbeac10fc99>, 3.000000000000 XMR, idx 15/0
Refresh done, blocks received: 24699                            
Currently selected account: [21] 
Balance: 0.000000000000, unlocked balance: 0.000000000000

4. ToDo

  • Review
  • Unit test & performance test
  • Update MRL-0006

5. Related links

Acknowledgements

  • @JollyMort for initial discussions since the very beginning
  • @knaccc for bringing up the concept of sub-addresses with a modified DH key exchange idea, and also for pointing out the fact that the change can be sent to any subaddresses
  • @RandomRun for the second generation modified DH key exchange idea that solves the problem of discovery of the main wallet keys
  • @luigi1111 for various suggestions and guidance
@ghost
Copy link

@ghost ghost commented May 30, 2017

Is there a possibility of sweeping all subaddress funds back into my main address? Is that how bitcoin's HD wallet works? Or would that be more like the 'disposable address' scheme...?

@JollyMort
Copy link
Contributor

@JollyMort JollyMort commented May 30, 2017

Remember, funds never reside on some address. Fundamentally, all the outputs belong to you because you can recover the individual outputs' private keys and they're not associated with an address. If the sender lost the relevant TX key, nobody can ever prove an output was sent to some address unless you give them the view key. Even if you know from your records you sent x monero to address y in TX z, you can't ever prove it without TX key or view key. If the sender sent to your sub A, and you spend it together with something from sub B, he can't tell that's from a different address because they're never linked to an address (unless he was the one who sent to B, in which case he'd have the TX key in local records). He can't even tell you're spending at all since you're using a ring signature :)

So, grouping them into addresses or sub-addresses is kind of arbitrary.
But yeah, it could be useful to draw borders and avoid combining some together, and have the sweep_all to collect from all subs (does it do that already?)

@xmrgit
Copy link

@xmrgit xmrgit commented May 30, 2017

just registered to ask if we can have a sweep_all for every sub-address instead of just move_sub2main

so more something like sweep_all_sub 2 0 which will move all funds from sub-address 2 to sub-address 0
and sweep_all_sub 2 2 will move all funds from sub-address 2 to sub-address 2 to create just 1 input.

I like to sweep my current sub-wallets usually more than 1 time before I send them to my main-wallet(s) for privacy reasons.

otherwise great pr, will save me some time to create and backup a new wallet every time.

@knaccc
Copy link

@knaccc knaccc commented May 30, 2017

I second @xmrgit 's suggestion. Fantastic stuff, @kenshi84, very elegant.

@Jaqueeee
Copy link
Contributor

@Jaqueeee Jaqueeee commented May 31, 2017

Good work! What's the impact on performance when rescanning blockchain? Has it been benchmarked?

@dan-da
Copy link

@dan-da dan-da commented Jun 1, 2017

Error: No outputs found, or daemon is not ready

Can this error message be improved? Even as a technical user, this would mislead me.

Perhaps something like: "Error: No single sub-address contains adequate funds although wallet balance is sufficient. Suggestion: aggregate funds to a fresh address."

@kenshi84 kenshi84 force-pushed the kenshi84:subaddress-v2 branch 3 times, most recently from f84bd34 to b4bd69b Jun 2, 2017
@kenshi84
Copy link
Contributor Author

@kenshi84 kenshi84 commented Jun 3, 2017

Thank you guys for your feedback!

@xmrgit @knaccc
I agree with your point, so I dropped the command move_sub2main now and instead modified the command sweep_all such that it optionally accepts the index of subaddress to be swept. Please check the updated OP above, I hope it achieves what you had in mind.

@NanoAkron

Is there a possibility of sweeping all subaddress funds back into my main address?

Yes, it's possible to sweep all the balance of each subaddress to the main address individually. Alternatively, although not recommended for privacy reasons, it's also possible to sweep all the balances of all the subaddresses to the main address in a single transaction if you switch off the safeguard by doing set allow-combine-subaddresses 1.

Is that how bitcoin's HD wallet works?

Conceptually it's kind of similar to Bitcoin's HD wallet, but the actual problem being addressed here is vastly different because in Monero, wallet addresses never appear in the blockchain as @JollyMort stressed.

Or would that be more like the 'disposable address' scheme...?

I couldn't understand your question...

@JollyMort

unless he was the one who sent to B

This is the concern addressed by the "not combining subaddresses" scheme. If Alice sent some funds to two different subaddresses and saw a transaction later that included those two outputs in the input ring signatures, she would guess that the two subaddresses belong to the same person.

it could be useful to draw borders and avoid combining some together, and have the sweep_all to collect from all subs (does it do that already?)

It's not recommended (but still possible) to sweep all the funds in all the subaddresses to the main address at once, due to the above reason.

@Jaqueeee

What's the impact on performance when rescanning blockchain? Has it been benchmarked?

No, it's not benchmarked yet. But my guess is that the additional cost for blockchain scanning will be really small (i.e. 1 hashtable lookup, 1 hash computation, and 1 scalar addition, for each incoming tranfer).

@dan-da

Thanks a lot for your suggestion! I changed the error message following your suggestion, please see the updated OP.

@JollyMort
Copy link
Contributor

@JollyMort JollyMort commented Jun 3, 2017

This is the concern addressed by the "not combining subaddresses" scheme. If Alice sent some funds to two different subaddresses and saw a transaction later that included those two outputs in the input ring signatures, she would guess that the two subaddresses belong to the same person.

Yup. But I could by chance make a ring signature using some 2 inputs to which Alice sent funds (to someone else, not me) in my ring and she could come to the same (but wrong) conclusion :) Plausible deniability FTW

@kenshi84
Copy link
Contributor Author

@kenshi84 kenshi84 commented Jun 3, 2017

Yup. But I could by chance make a ring signature using some 2 inputs to which Alice sent funds (to someone else, not me) in my ring and she could come to the same (but wrong) conclusion :) Plausible deniability FTW

The point is that the likelihood of such an event is fairly low considering the enormous number of outputs in the blockchain, and if this happens repeatedly, the plausible deniability starts to diminish pretty quickly.

@xmrgit
Copy link

@xmrgit xmrgit commented Jun 4, 2017

would it also make sense to modify the command transfer such that it optionally accepts the index of a subaddress instead of only be able to randomly choosing a subaddress to be spent?

@kenshi84 kenshi84 force-pushed the kenshi84:subaddress-v2 branch 4 times, most recently from 8b10d8c to eb6efbe Jun 7, 2017
@kenshi84
Copy link
Contributor Author

@kenshi84 kenshi84 commented Jun 8, 2017

@xmrgit @knaccc
The CLI has been updated quite a bit, please check the edited OP (after section 3).

@knaccc
Copy link

@knaccc knaccc commented Jun 8, 2017

Minor note: I think it might help if the command syntax:
address [ <index_min> [<index_max>]|fresh ]
was instead written as:
address [ [<index_min> [<index_max>]] | fresh ]

@knaccc
Copy link

@knaccc knaccc commented Jun 8, 2017

I'm a little concerned that the wallet implementation you've proposed could diverge enormously from the GUI implementation, and lead to confusion.

If the GUI were to present just a single wallet to users with a main balance and then a list of subaddresses, then that would match perfectly with what you've implemented.

However, if the GUI presents the user with the ability to create different subwallets each with their own main balance, and with the abilitiy to create additional aliases per subwallet, then the terminology and UI will be completely different between the GUI and CLI.

Therefore I think that before proceeding, it needs to be decided whether the GUI will actually provide users with just subaddresses, or instead subwallets and aliases.

@xmrgit
Copy link

@xmrgit xmrgit commented Jun 8, 2017

@kenshi84
I love your work.

does it make sense to have the <index=n> in sweep_all as currently optional instead of required?

and would it make sense if <index=n> can have more than one n as input? so something like this index=1,2,5,10050?

@kenshi84 kenshi84 force-pushed the kenshi84:subaddress-v2 branch 3 times, most recently from 139937d to 70d6b8f Jun 8, 2017
@kenshi84
Copy link
Contributor Author

@kenshi84 kenshi84 commented Jun 10, 2017

@knaccc
I see your point, and I'm aware that this PR needs to go hand in hand with the rest of the software ecosystem including GUI & RPC & probably also MyMonero etc.

However, if the GUI presents the user with the ability to create different subwallets each with their own main balance, and with the abilitiy to create additional aliases per subwallet, then the terminology and UI will be completely different between the GUI and CLI.

I'm still unsure about the meaning of those terms: Is an "alias" something different from a subaddress? Is a "subwallet" something like a full wallet whose seed is deterministically generated by some "master" seed? I haven't seen any detailed discussion on their exact definitions and merits.

There seems to be a general interest in managing different wallet files, either for the purpose of not revealing your identity through address reuse (which is addressed by this PR) or for the protection against KYC/AML demanding your viewkey (which isn't addressed by this PR).

monero-project/monero-gui#394

To me, opening different wallet files in GUI each having subtotal balances for their subaddresses doesn't seem too complicated or confusing, as long as we get the terminology right.

@xmrgit
Thanks a lot for your suggestion! I updated the syntax for transfer and sweep_all as you suggested, please see section 3.4.

does it make sense to have the <index=n> in sweep_all as currently optional instead of required?

The index parameter is already optional in both transfer and sweep_all.

@xmrgit
Copy link

@xmrgit xmrgit commented Jun 10, 2017

@kenshi84

The index parameter is already optional in both transfer and sweep_all.

I mean, that I don't know any example where sweep_all needs index parameter as optional, because you usually know exactly which amount/sub-address you like to sweep. that's why I would change index in sweep_all to required. but maybe there are good examples where a random sub-address selection in sweep_all command makes sense?

Sorry for my bad english.

@kenshi84
Copy link
Contributor Author

@kenshi84 kenshi84 commented Jun 10, 2017

@xmrgit
Oh, I took your comment the opposite way. My intention of having the index parameter as optional is to allow the user to easily move funds from subaddresses to your main address by simply repeating

sweep_all <your main address>

until there's no subaddress with unlocked balance.

In general, I designed the CLI such that funds in subaddresses tend to accumulate in the main address (by randomly choosing non-zero index first before index=0), because all the change outputs go to the main address, regardless of whether the destination is a standard address or a subaddress. In other words, my intention was that as an "average" user keeps transferring funds without specifying the index parameter, the wallet will eventually have all the outputs in the main address.

Does this make sense?

@knaccc
Copy link

@knaccc knaccc commented Jun 10, 2017

@kenshi84 I'm referring to the terms defined in the previous PR in this comment: #1753 (comment)

Regardless of what terms are used, the two approaches are:

Option 1. The user sees a dropdown selection in the GUI to switch between 'bank accounts', like they would with online banking. Each 'bank account' has its own main balance. You can create alternate receiving addresses for each bank account. I've referred to these 'bank accounts' as subwallets and the 'alternate receiving addresses' as aliases. Both subwallets and aliases are implemented by simply issuing a subaddress per subwallet and a subaddress per subwallet alias.

Option 2. The user does NOT see multiple 'bank accounts' or 'subwallets'. They just see one wallet with a main balance, and then additional different receiving addresses for this wallet. This is how you've implemented things for the CLI so far.

The advantage of Option 1 is that it is less messy.

For example, a user might want to use their wallet for two different purposes: one for personal use (PERSONAL) and another for an eBay like scenario where they are selling things online (SELLING).

For both the PERSONAL and SELLING purposes, the user would create multiple receiving addresses for privacy reasons.

Under Option 1, the user sees two subwallets each with several receiving addresses (aliases).

Under Option 2, the user sees just a big list of different receiving addresses, and unless the user carefully labels them, it will be difficult for them to remember which receiving addresses were used to receive funds for PERSONAL vs SELLING purposes. They may see 20 different receiving addresses, and then have to recall which receiving addresses were given out for PERSONAL vs SELLING.

Option 1 allows the user to easily see a different balance for their PERSONAL vs SELLING subwallets. This is possible because the GUI knows which receiving addresses (aliases) were associated with each subwallet. To see the total funds related to PERSONAL vs SELLING, they would have to not only remember which receiving addresses were used for each PERSONAL/SELLING purpose, but also manually add up funds to figure out the total funds for their PERSONAL vs SELLING activity.

Edit: I've just thought of an Option 3:
Have just one wallet and lots of different receiving addresses (subaddresses). Assign a 'tag' to each subaddress created, e.g. PERSONAL or SELLING. Then the wallet can easily display a subtotal for PERSONAL vs SELLING activity. This would mean there would be no need for 'subwallet' or 'alias' terminology, and we can just call everything a subaddress.

On reflection, I like Option 3 since:
a) No extra terminology (subwallets, aliases)
b) Easier to re-label subaddresses when a wallet is restored form seed and labels have been lost
c) Since Option 3 is so similar to Option 2, and since Option 2 can be converted to Option 3 pretty easily in the future, it means that we could just go for Option 2 (which is exactly what you've already implemented), and Option 3 can be a "maybe" for the future. If we go for Option 2, it'd be nice to have an unused "tag" field available per subaddress for future possible use.

So, in summary, I think we should choose Option 2 or 3 depending on how important we think it is to let users see subtotals for PERSONAL vs SELLING without having to add it up themselves with a calculator.

@kenshi84
Copy link
Contributor Author

@kenshi84 kenshi84 commented Jun 10, 2017

@knaccc
Thanks a lot for your input.

I'm still not so convinced by your proposal, and in my opinion, this is where I start to feel that having separate wallet files makes more sense. The sole purpose of this PR is to solve the off-chain metadata linkability problem due to address reuse. I didn't intend to improve the user experience of managing multiple accounts in a way similar to today's online banking.

So, in summary, I think we should choose Option 2 or 3 depending on how important we think it is to let users see subtotals for PERSONAL vs SELLING without having to add it up themselves with a calculator.

I think you have one misunderstanding: subtotals in subaddresses are NOT useful for keeping track of balances in a per-purpose basis (e.g. PERSONAL vs SELLING), because whenever you spend funds in those subaddresses, the change always goes to the main address. In other words, you can only keep track of the total incoming transfers to those subaddresses, but not the remaining balances of those subaddresses due to the changes being moved to the main address.

Therefore, I'd rather vote for letting users simply open multiple wallet files separately (either in a new window or a new tab), both for the CLI and GUI (and probably MyMonero).

moneroexamples added a commit to moneroexamples/openmonero that referenced this pull request Nov 13, 2017
moneroexamples added a commit to moneroexamples/openmonero that referenced this pull request Nov 13, 2017
@kenshi84
Copy link
Contributor Author

@kenshi84 kenshi84 commented Nov 13, 2017

@moneroexamples
Nice work! I gave some comments in the commit. I'm looking forward to it working :)

@moneroexamples
Copy link
Contributor

@moneroexamples moneroexamples commented Nov 13, 2017

@kenshi84
Thanks. onion explorer with help of @stoffu and yours seems to work well with decoding and proving subaddress (testnet link: http://139.162.32.245:8082/). So slowly can start looking into open monero.

@moneroexamples
Copy link
Contributor

@moneroexamples moneroexamples commented Jan 25, 2018

@kenshi84

Have new question.

How do I prove change returned to myself using private_tx_key? Let me explain what I mean using example.

When A makes tx to regular address of B, he will receiver a private tx key (obtained using get_tx_key in A's wallet). Using the key, A can prove to B (using his public address) that he sent to B what he said. Also, A can use his own public address with the same key and see the change returned to A.

Now, when A makes tx to sub-address of B, the second part (i.e., checking the change returned to A using the private tx key) does not seem to work. I know that A can use his regular address + his private viewkey to check the change. But I am curious why it does not work with tx private key when sending to a sub-address of B, but it works when sending to regular address of B.

@kenshi84
Copy link
Contributor Author

@kenshi84 kenshi84 commented Jan 25, 2018

@moneroexamples

I know that A can use his regular address + his private viewkey to check the change. But I am curious why it does not work with tx private key when sending to a sub-address of B, but it works when sending to regular address of B.

This is due to the particular way tx public keys are computed when sending funds to subaddresses. As in the OP, when Alice's wallet (view, spend) = (X, Y) sends funds to Bob's subaddress (C, D), the output key P for Bob is computed as

P = Hs(s*C)*G + D

where s is the tx secret key, while the change output key Q back to Alice is computed as

Q = Hs(x*R)*G + Y

where R = s*D is the tx public key. This formulation makes it impossible to use the traditional way of proving payments using the tx secret key which is to use

Q' = Hs(s*X)*G + Y

because x*R == x*s*D != s*X and thus Q != Q'.

@moneroexamples
Copy link
Contributor

@moneroexamples moneroexamples commented Jan 25, 2018

@kenshi84

Thanks. But proving change using tx_private_key(s) works when making tx to multiple addresses/subaddress. It only does not work when sending to a single subaddress. My guess is that when sending to multiple subaddresses/addresses in a single tx, extra tx_public_keys and extra private_tx_keys are generated and saved. This does not happen when sending to a single subaddress. Shouldn't there be some extra tx_private_key or tx_public_key also used in this case?

@kenshi84
Copy link
Contributor Author

@kenshi84 kenshi84 commented Jan 25, 2018

@moneroexamples

Your understanding is correct.

Shouldn't there be some extra tx_private_key or tx_public_key also used in this case?

That would make a 2-output tx to a subaddress distinguishable from other typical 2-output txes to standard addresses, so no.

@moneroexamples
Copy link
Contributor

@moneroexamples moneroexamples commented Jan 25, 2018

@kenshi84

Thanks. Makes sense for 2-output. But what about a 3-output tx with at least one subaddress? Such txs also stand out, due to the additional tx public keys stored in the extra field, dont they?.

@kenshi84
Copy link
Contributor Author

@kenshi84 kenshi84 commented Jan 26, 2018

@moneroexamples

You're right that transfers to multiple destinations including subaddresses will stand out due to additional tx keys. Between the following two options:

  1. Don't allow sending to multiple destinations that include subaddresses.

    • Pros: No need for additional tx keys (= txes are perfectly indistinguishable).
    • Cons: Limited ease of use (particularly for mining pools, where people can't use their subaddresses for payouts)
  2. Enable sending to multiple destinations that include subaddresses by introducing additional tx keys.

    • Pros: Ease of use.
    • Cons: Those txes with multiple destinations including subaddresses become distinguishable from other multi-destination txes that are only going to standard addresses.

we decided to choose the latter after a thorough discussion (credit to @knaccc), anticipating that the use of subaddresses will be the norm in the future.

@moneroexamples
Copy link
Contributor

@moneroexamples moneroexamples commented Jan 26, 2018

@kenshi84

Thanks. Its clear now. One more argument for option 2, would be that multiple-output txs are much less common than 2-outputs one. Also in e.g., 10-output tx, its unclear if there are 1 or 9 subaddress, whereas in 2-output, one would immediately know that you are dealing with a subadddress due to extra keys.

@Technohacker
Copy link
Contributor

@Technohacker Technohacker commented Apr 13, 2018

Just wanted to make my understanding clear,

  1. Why is the view secret key (and not the spend secret key) used in subaddress generation?
  2. Can the spend secret key be used instead?

Thanks :)

@kenshi84
Copy link
Contributor Author

@kenshi84 kenshi84 commented Apr 13, 2018

  1. Why is the view secret key used in subaddress generation?

Because we'd like watch-only wallets to be able to recognize incoming transfers to subaddresses.

  1. Can the spend secret key be used instead?

That would mean only full wallets can see incoming transfers to subaddresses. If that's what you want, sure you can do so by customizing the code yourself. But your idea is unlikely to be adopted because it doesn't seem useful.

@Technohacker
Copy link
Contributor

@Technohacker Technohacker commented Apr 13, 2018

@kenshi84 Ah okay, thanks for the clarification :)

@krtschmr
Copy link

@krtschmr krtschmr commented Apr 22, 2018

we need a function to explicit generate a subadress with given i.

since whitespaces are allowed in the label i would suggest

address index 18034
@moneroexamples
Copy link
Contributor

@moneroexamples moneroexamples commented Nov 20, 2018

@kenshi84
Hi. I'm developing new output identifier for openmonero, to be later probably used for the explorer, and other projects, and I got confused about sub-addresseses again :-(.

Why do we need to add additional_tx_pub_keys to extra field in a multi-output tx with sub-addresses? At the moment openmonero does not check for any additional_tx_pub_keys and yet it seems to correctly recognizes outputs (for regular addresses) in multi-output txs with those extra keys. So, are these extra tx public keys only useful for identification of outputs addressed to sub-addresses?

@kenshi84
Copy link
Contributor Author

@kenshi84 kenshi84 commented Nov 20, 2018

@moneroexamples
When no subaddresses are used, the sender can reuse one single tx key to generate output keys for all the recipients. Once subaddresses are involved, the same tx key cannot be reused for multiple recipients because each tx pubkey R is recipient-specific:

R = s*D

where s is the secret scalar and D is the recipient's subaddress spend pubkey. Therefore, multi-destination transactions (i.e. 3 or more outs) involving subaddresses inevitably requires additional tx pubkeys stored in the tx extra field.

The number of the additional tx pubkeys is the same as the number of outs; i.e. the recipient with a standard address is assigned a dummy additional tx pubkey, in order to hide which recipient is of which address type. The standard tx pubkey (which is always included) is used for decoding outputs destined to standard addresses.

At the moment openmonero does not check for any additional_tx_pub_keys and yet it seems to correctly recognizes outputs (for regular addresses) in multi-output txs with those extra keys.

So, outputs destined to standard addresses will be recognized without using the additional tx pubkeys. Outputs destined to subaddresses will be recognized only when using the additional tx pubkeys.

@moneroexamples
Copy link
Contributor

@moneroexamples moneroexamples commented Nov 20, 2018

@kenshi84
Ok. Now i understand. So the extra public keys for the recipient with a standard address are just dummy, and regular tx public key is enough.

But for sub-addresses, the corresponding extra keys are not dummies, and they should be used in the decoding procedure.

Thanks.

@moneroexamples
Copy link
Contributor

@moneroexamples moneroexamples commented May 2, 2019

Regarding 3.6. Restoring wallet from seed.

Hi. Quick question. If an online service, external wallet, hardware wallet, or anything, wants to import an existing account, each output in each tx needs to be checked against 10'000 (=50 accounts x 200 sub-addresses in each) sub-addresses?

For example, if I wanted to add sub-addresses to OpenMonero, the backend would need to scan (for each OM's account) each output in each tx 10'000 times to cover the initial set of potential sub-addresses?

@vtnerd
Copy link
Contributor

@vtnerd vtnerd commented May 2, 2019

Hi. Quick question. If an online service, external wallet, hardware wallet, or anything, wants to import an existing account, each output in each tx needs to be checked against 10'000 (=50 accounts x 200 sub-addresses in each) sub-addresses?

Most of the crypto costs are amortized per primary account (view key). The computation of the view key/output pair must compared against the list of subaddresses for the primary account. So this usually is faster than having 10000 unique view keys to scan against but still has additional storage/memory/time requirements.

The major negative is when more than 3 outputs are used in a subaddress transaction. N-2 (where N is number of outputs) ECDH ops are required instead of a fixed single ECDH op for all outputs. If all transactions did this, subaddresses would be more computation for OpenMonero.

@moneroexamples
Copy link
Contributor

@moneroexamples moneroexamples commented May 2, 2019

@vtnerd

Thanks.

usually is faster than having 10000 unique view keys to scan against but still has additional storage/memory/time requirements.

Yes. For a single user it is not a major drama, but multiply this by 100, 1000, or more concurrent users for which you have to do all these extra calculations, and it can add up to some serious extra loading on a server. Anyway, it will be interesting task to do this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

You can’t perform that action at this time.