mirror of https://github.com/zcash/zips.git
423 lines
15 KiB
ReStructuredText
423 lines
15 KiB
ReStructuredText
::
|
|
|
|
ZIP: 315
|
|
Title: Best Practices for Wallet Handling of Multiple Pools
|
|
Owners: Daira Emma Hopwood <daira@electriccoin.co>
|
|
Jack Grigg <jack@electriccoin.co>
|
|
Status: Proposed
|
|
Category: Wallet
|
|
Discussions-To: <https://github.com/zcash/zips/issues/447>
|
|
Pull-Request: <https://github.com/zcash/zips/issues/>
|
|
|
|
|
|
Terminology
|
|
===========
|
|
|
|
The key words "MUST", "SHOULD", "SHOULD NOT", and "MAY" in this document are to be
|
|
interpreted as described in RFC 2119. [#RFC2119]_
|
|
|
|
The terms below are to be interpreted as follows:
|
|
|
|
Auto-shielding
|
|
The process of automatically transferring transparent funds to the most recent
|
|
shielded pool on receipt.
|
|
|
|
Auto-migration
|
|
The process of automatically transferring shielded funds from older pools to the
|
|
most preferred (usually the most recent) pool on receipt.
|
|
|
|
Opportunistic shielding
|
|
TODO
|
|
|
|
Opportunistic migration
|
|
TODO
|
|
|
|
Confirmed
|
|
TODO
|
|
|
|
Partially confirmed
|
|
TODO
|
|
|
|
Transaction output (TXO)
|
|
An output (transparent TXO or note) of a transaction on the consensus block chain
|
|
or in the mempool visible to a wallet.
|
|
|
|
|
|
Motivation
|
|
==========
|
|
|
|
The aim of this ZIP is to provide wallet developers with a set of best practices by
|
|
which they can minimize the leakage of information when constructing transactions.
|
|
|
|
This includes best practices for:
|
|
|
|
* how to handle interactions between the ZIP 32 key tree and Unified Addresses;
|
|
* when to use external or internal keys/addresses;
|
|
* sharing addresses and viewing keys;
|
|
* sending and receiving funds;
|
|
* migrating funds between pools.
|
|
|
|
|
|
Requirements
|
|
============
|
|
|
|
User consent
|
|
------------
|
|
|
|
The guiding principle of these requirements is that users must explicitly consent
|
|
to each instance of information being revealed by the wallet.
|
|
|
|
A wallet typically reveals some information in the process of creating a transaction.
|
|
Which information is revealed depends on the configured wallet privacy policy.
|
|
A user may give blanket consent to reveal a particular kind of information, and
|
|
must also be able to change the configured wallet privacy policy to avoid the
|
|
wallet creating new privacy leaks of a given type.
|
|
|
|
The specifications below describe some situations in which blanket consent may be
|
|
inappropriate.
|
|
|
|
Some varieties of consent may not be revocable, for example if a user chooses to
|
|
share some of their keys.
|
|
|
|
|
|
Prompt accessibility of funds
|
|
-----------------------------
|
|
|
|
Wallets need to take account of two concerns:
|
|
|
|
* enabling funds to be spent as quickly as possible to reduce latency;
|
|
* waiting for long enough before spending TXOs to ensure that the spendable
|
|
balance is not overestimated, and so can be trusted by the user.
|
|
|
|
To enable this we define two kinds of TXOs:
|
|
|
|
* An untrusted TXO is a TXO received from a party that may try to double-spend.
|
|
* A trusted TXO is a TXO received from a party where the wallet trusts a
|
|
double-spend not to occur, e.g. TXOs created by the wallet's internal TXO
|
|
handling.
|
|
|
|
Wallets can then require that untrusted TXOs need more confirmations before
|
|
they become spendable than trusted TXOs. This provides an improved trade-off
|
|
between latency on the one hand, and reliability and safety on the other.
|
|
|
|
|
|
Specification
|
|
=============
|
|
|
|
Long-term storage of funds
|
|
--------------------------
|
|
|
|
It is RECOMMENDED that wallets only hold funds as shielded in the long-term;
|
|
that is, they automatically shield incoming transparent funds.
|
|
|
|
The remainder of this specification assumes a wallet that follows this
|
|
recommendation, except where explicitly noted.
|
|
|
|
|
|
Trusted and untrusted TXOs
|
|
--------------------------
|
|
|
|
A wallet SHOULD have a policy that is clearly communicated to the user for
|
|
the number of confirmations needed to spend untrusted and trusted TXOs
|
|
respectively. The following confirmation policy is RECOMMENDED:
|
|
|
|
* 10 confirmations, for untrusted TXOs;
|
|
* 3 confirmations, for trusted TXOs.
|
|
|
|
Wallets MAY enable users to mark specific external transactions as trusted,
|
|
allowing their received TXOs to be treated as trusted TXOs even if they
|
|
do not have the required number of confirmations.
|
|
|
|
|
|
Categories of TXOs according to spendability
|
|
--------------------------------------------
|
|
|
|
A TXO is *confirmed spendable* if and only if the wallet has its spending
|
|
key, and:
|
|
|
|
* it is a trusted TXO that has at least the required confirmations for trusted
|
|
TXOs; or
|
|
* it is an untrusted TXO that has at least the required confirmations for
|
|
untrusted TXOs.
|
|
|
|
A TXO is *unconfirmed spendable* if and only if the wallet has its spending
|
|
key, and it is not confirmed spendable.
|
|
|
|
A TXO is *watch-only* if and only if the wallet has its full viewing key
|
|
(or address in the case of a transparent TXO) but not its spending key.
|
|
|
|
|
|
Reporting of balances
|
|
---------------------
|
|
|
|
Wallets SHOULD report:
|
|
|
|
* Spendable balance.
|
|
* Pending balance, *or* total balance.
|
|
|
|
These are calculated as follows:
|
|
|
|
* The spendable balance is the sum of values of confirmed spendable TXOs.
|
|
* The pending balance is the sum of values of unconfirmed spendable TXOs.
|
|
* The total balance is the spendable balance plus the pending balance.
|
|
|
|
|
|
Reporting of transactions
|
|
-------------------------
|
|
|
|
If a transaction contains both spendable and watch-only TXOs, its spendable
|
|
and watch-only incoming balances MUST be reported separately.
|
|
|
|
Incoming transactions
|
|
'''''''''''''''''''''
|
|
|
|
A transaction is incoming if it contains unconfirmed TXOs. Incoming
|
|
transactions SHOULD be reported with their number of confirmations and
|
|
their balances as described in `Reporting of balances`_.
|
|
|
|
Sent transactions
|
|
'''''''''''''''''
|
|
|
|
A transaction is sent if it was either:
|
|
* created by the wallet, or
|
|
* detected using the wallet's outgoing viewing keys, or
|
|
* detected as having been sent from one of the wallet's watch-only addresses.
|
|
|
|
Sent transactions SHOULD be reported with their number of confirmations,
|
|
how long until they expire, and their balances as described in `Reporting of balances`_.
|
|
|
|
|
|
|
|
Auto-shielding
|
|
--------------
|
|
|
|
If they use auto-shielding, then any transparent balance should be treated as
|
|
pending.
|
|
|
|
For wallets that allow long-term storage of transparent funds, they SHOULD also
|
|
show spendable transparent and pending (or total) transparent according to the
|
|
same policy.
|
|
|
|
|
|
Unedited
|
|
--------
|
|
|
|
Wallets MUST report at least separate shielded and transparent balance.
|
|
|
|
If auto-shielding or auto-migration is off, then wallets MAY report separate
|
|
balances for each shielded pool and for transparent balance.
|
|
|
|
If the wallet never supports a given pool, it can obviously omit balances for that
|
|
pool.
|
|
|
|
If auto-shielding is on, transparent funds should be reported in "balance unavailable
|
|
to spend".
|
|
|
|
Wallets SHOULD separately report the balances of funds that are immediately
|
|
spendable, and any remaining funds that are expected from unconfirmed or
|
|
partially confirmed transfers.
|
|
|
|
TODO: make this more precise in terms of the following categories:
|
|
|
|
* Funds at rest (not involved in any not-fully-confirmed transfer)
|
|
* Outgoing funds to an external source (might come back if the tx doesn't go through)
|
|
* Incoming funds from an external source
|
|
* Funds we are sending to ourself.
|
|
|
|
|
|
Rationale
|
|
'''''''''
|
|
|
|
The specification of balance reporting is intended to give the user visibility
|
|
into the operation of auto-shielding, opportunistic shielding, and pool migration/usage.
|
|
|
|
|
|
|
|
Linkability of transactions or addresses
|
|
----------------------------------------
|
|
|
|
|
|
|
|
Network-layer privacy
|
|
---------------------
|
|
|
|
|
|
Viewing keys
|
|
------------
|
|
|
|
What they are supposed to reveal; see ZIP 310 for Sapling (needs updating for
|
|
Orchard). https://github.com/zcash/zips/issues/606
|
|
|
|
|
|
|
|
Allowed transfers
|
|
-----------------
|
|
|
|
* Sprout -> transparent or Sapling
|
|
* Sapling -> transparent or Sapling or Orchard
|
|
* Orchard -> transparent or Sapling or Orchard
|
|
* if auto-shielding is off:
|
|
* transparent -> transparent or Sapling or Orchard
|
|
* if auto-shielding is on:
|
|
* transparent -> internal Orchard or Sapling
|
|
|
|
Note: wallets MAY further restrict the set of transfers they perform.
|
|
|
|
|
|
Auto-shielding
|
|
--------------
|
|
|
|
Wallets SHOULD NOT spend funds from a transparent address to an external address,
|
|
unless the user gives explicit consent for this on a per-transaction basis.
|
|
|
|
In order to support this policy, wallets SHOULD implement a system of auto-shielding
|
|
with the following characteristics.
|
|
|
|
|
|
If auto-shielding functionality is available in a wallet, then users MUST be able
|
|
to explicitly consent to one of the following possibilities:
|
|
|
|
* auto-shielding is always on;
|
|
* auto-shielding is always off;
|
|
* the user specifies a policy...
|
|
|
|
Auto-shielding MUST be one of:
|
|
|
|
* "must opt in or out" (zcashd will do this -- i.e. refuse to start unless the option
|
|
is configured), or
|
|
* always on.
|
|
|
|
|
|
Auto-migration
|
|
--------------
|
|
|
|
|
|
Information leakage for transfers between pools
|
|
-----------------------------------------------
|
|
|
|
|
|
If no auto-migration, if you can satisfy a transfer request to Sapling from your
|
|
Sapling funds, do so.
|
|
|
|
The user's consent is needed to reveal amounts. Therefore, there should be
|
|
per-transaction opt-in for any amount-revealing transfer.
|
|
|
|
* there may be a compatibility issue for amount-revealing cross-pool txns that were
|
|
previously allowed without opt-in
|
|
|
|
Don't automatically combine funds across pools to satisfy a transfer (since that
|
|
could reveal the total funds in some pool).
|
|
|
|
In order to maintain the integrity of IVK guarantees, wallets should not generate
|
|
unified addresses that contain internal receivers, nor expose internal receivers
|
|
(such as those used for auto-shielding and change outputs) in any way.
|
|
|
|
Open questions:
|
|
|
|
* should there be an auto-migration option from Sapling to Orchard?
|
|
|
|
# str4d notes
|
|
|
|
If we want to have both automatic and opportunistic shielding, and keep the two
|
|
indistinguishable, then we can't auto-shield when the transparent balance reaches
|
|
some threshold (otherwise opportunistic would either never be used, or would be
|
|
identifiable when it uses the balance below the threshold).
|
|
|
|
Instead, a proposition: we define a distribution of "time since last payment to the
|
|
address" from which we sample the time at which the auto-shielding transaction will
|
|
be created. This distribution is weighted by the balance in the address, so as more
|
|
funds accrue, the auto-shielding transaction is more likely to be created.
|
|
|
|
- It ensures that all funds will eventually be auto-shielded, while preventing
|
|
fee-dusting attacks (where dust is sent in order to repeatedly consume fees from
|
|
the wallet), as the auto-shielding transaction is not directly triggered by payment
|
|
receipt.
|
|
|
|
- If the user makes a shielding transaction in the meantime, we opportunistically
|
|
shield, without it being clearly not an auto-shielding transaction.
|
|
|
|
- If a wallet is offline for a long time, then it would likely auto-shield as soon as
|
|
it finishes syncing. This maybe isn't enough to reveal that the wallet came online,
|
|
except that it _might_ result in auto-shielding transactions for multiple
|
|
transparent addresses being created at the same time. So we might want to
|
|
special-case this?
|
|
|
|
Properties we want from auto-shielding:
|
|
|
|
- Auto-shielding transactions MUST NOT shield from multiple transparent receivers in
|
|
the same transaction.
|
|
- Doing so would trivially link diversified UAs containing transparent receivers.
|
|
|
|
Properties we want from auto-migration:
|
|
|
|
- Receipt of a shielded payment MUST NOT trigger any on-chain behaviour (as that
|
|
reveals transaction linkability).
|
|
|
|
Both auto-shielding and auto-migration are time-triggered actions, not
|
|
receipt-triggered actions. An auto-shielding or auto-migration transaction MUST NOT
|
|
be created as a direct result of a payment being received.
|
|
|
|
Both of these are opportunistic: if the user's wallet is making a transaction in
|
|
which one of these actions would occur anyway, then the wallet takes the opportunity
|
|
to migrate as much as it would do if it were generating an autoshielding transaction.
|
|
This both saves on a transaction, and removes the need for any kind of transparent
|
|
change address within UAs.
|
|
|
|
TODO: what pool should change go to?
|
|
|
|
* Proposal: the most recent pool already involved in the transaction.
|
|
|
|
Wallet Recovery
|
|
---------------
|
|
|
|
In the case where we are recovering a wallet from a backed-up mnemonic phrase,
|
|
and not from a wallet.dat, we don't have enough information to figure out what
|
|
receiver types the user originally used when deriving each UA under an account.
|
|
We have a similar issue if someone exports a UFVK, derives an address from it,
|
|
and has a payment sent to the address: zcashd will detect the payment, but has
|
|
no way to figure out what address it should display in the UI. A wallet could
|
|
store this information in the memo field of change outputs, but that adds a
|
|
bunch of coordination to the problem, and assumes ongoing on-chain state
|
|
storage.
|
|
|
|
- If the receiver matches an address that the wallet knows was derived via
|
|
z_getaddressforaccount, show that UA as expected (matching the receiver
|
|
types the user selected).
|
|
- If the receiver matches a UFVK in the wallet, and we are looking it up
|
|
because we detected a received note in some block, show the UA with the
|
|
default receiver types that zcashd was using as of that block height
|
|
(ideally the earliest block height we detect), and cache this for future
|
|
usage.
|
|
- For zcashd's current policy of "best and second-best shielded pools, plus
|
|
transparent pool", that would mean Orchard, Sapling, and transparent for
|
|
current block heights.
|
|
- For each release of a wallet, the wallet should specify a set of receiver
|
|
types and an associated range of block heights during which the wallet
|
|
will, by default, generate unified addresses using that set of receiver
|
|
types.
|
|
- For zcashd we know how the policy evolves because each zcashd release has
|
|
an approximate relase height and EoS height that defines the window.
|
|
- Subsequent releases of a wallet should not retroactively change their
|
|
policies for previously defined block height ranges.
|
|
- If the receiver type for a note received at a given type is not a member
|
|
of the set of receiver types expected for the range of block heights, the
|
|
policy corresponding to the nearest block height range that includes that
|
|
receiver type should be used.
|
|
- If the receiver matches a UFVK in the wallet, and we have no information
|
|
about when this receiver may have been first used, show the UA
|
|
corresponding to the most recent receiver types policy that includes the
|
|
receiver's type.
|
|
- As part of this, we're also going to change the "Sapling receiver to
|
|
UfvkId" logic to trial-decrypt after trying internal map, so that we will
|
|
detect all receivers linked to UFVKs in the wallet, not just receivers in
|
|
addresses generated via z_getaddressforaccount. The internal map lookup
|
|
is then just an optimisation (and a future refactor to have Orchard do
|
|
the same is possible, but for now we will only trial-decrypt so we don't
|
|
need to refactor to access the Rust wallet).
|
|
|
|
|
|
References
|
|
==========
|
|
|
|
.. [#RFC2119] `RFC 2119: Key words for use in RFCs to Indicate Requirement Levels <https://www.rfc-editor.org/rfc/rfc2119.html>`_
|