mirror of https://github.com/zcash/zips.git
501 lines
19 KiB
ReStructuredText
501 lines
19 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.
|
|
|
|
Opportunistic shielding
|
|
The process of transferring previously received transparent funds to the most
|
|
recent shielded pool as part of a user-initiated transaction.
|
|
|
|
Auto-migration
|
|
The process of automatically transferring shielded funds from older pools to the
|
|
most preferred (usually the most recent) pool on receipt.
|
|
|
|
Opportunistic migration
|
|
The process of transferring previously received shielded funds from older pools
|
|
to the most preferred (usually the most recent) pool as part of a user-initiated
|
|
transaction.
|
|
|
|
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
|
|
==========
|
|
|
|
Zcash wallets have to serve two purposes as a user agent:
|
|
|
|
* to manage the economic actions dictated by the user;
|
|
* to manage the privacy implications of these actions.
|
|
|
|
The goal of this ZIP is to document security and privacy best practices when handling
|
|
funds and transactions as the user's agent. These best practices are intended to
|
|
provide as much privacy as is feasible by default, while still enabling the user's
|
|
desired transactions to occur, and exposing any privacy implications to the user so
|
|
that they have enough information to assess the consequences of their economic actions.
|
|
|
|
This ZIP covers best practices for:
|
|
|
|
* what information to display to the user about transactions and balances;
|
|
* 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
|
|
------------
|
|
|
|
A wallet typically reveals some information in the process of creating a transaction.
|
|
Which information is revealed depends on the configured wallet privacy policy.
|
|
The guiding principle of these requirements is that users must explicitly consent
|
|
to each instance of information being revealed by the wallet in a transaction.
|
|
|
|
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 information 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
|
|
confirmed-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, if a wallet supports receiving transparent funds (or supports
|
|
importing a seed from another wallet that may have done so), then it SHOULD
|
|
either auto-shield or opportunistically shield such funds by default.
|
|
|
|
Following this recommendation improves both collective privacy and the user's
|
|
individual privacy, by maximizing the size of the note anonymity set over time.
|
|
|
|
The remainder of this specification assumes a wallet that follows the above
|
|
recommendation, except where explicitly noted.
|
|
|
|
A wallet MAY allow users to disable auto-shielding.
|
|
|
|
|
|
Trusted and untrusted TXOs
|
|
--------------------------
|
|
|
|
A wallet SHOULD treat received TXOs that are outputs of transactions created
|
|
by the same wallet, as trusted TXOs. Wallets MAY enable users to mark specific
|
|
external transactions as trusted, allowing their received TXOs also to be
|
|
classified as trusted 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.
|
|
|
|
Rationale for the given numbers of confirmations
|
|
''''''''''''''''''''''''''''''''''''''''''''''''
|
|
|
|
The rationale for choosing three confirmations for trusted TXOs is that
|
|
empirically, reorgs are usually less than three blocks.
|
|
|
|
The consequences of attempting to spend an untrusted note in the case of a
|
|
rollback may be less severe than attempting to spend a trusted note. The value
|
|
received from a trusted note should always be recoverable, whereas recovering
|
|
value received from an untrusted note may require the user to request that
|
|
funds are re-sent.
|
|
|
|
|
|
Categories of TXOs according to spendability
|
|
--------------------------------------------
|
|
|
|
A TXO is *spendable*, relative to a given block chain and wallet state,
|
|
if and only if all of the following are true in that state:
|
|
|
|
* the TXO is unspent;
|
|
* the TXO is not committed to be spent in another transaction created
|
|
by this wallet; and
|
|
* the wallet has the TXO's spending key.
|
|
|
|
A TXO is *confirmed-spendable*, relative to a given block chain and
|
|
wallet state, if and only if all of the following are true in that state:
|
|
|
|
* the wallet is synchronized; and
|
|
* the TXO is spendable; and
|
|
* either auto-shielding is disabled or the TXO is shielded; and
|
|
* the TXO is trusted and has at least the required confirmations for
|
|
trusted TXOs, or it is untrusted and has at least the required
|
|
confirmations for untrusted TXOs.
|
|
|
|
A TXO is *unconfirmed-spendable*, relative to a given block chain and
|
|
wallet state, if and only if the TXO is spendable but is not
|
|
confirmed-spendable in that state.
|
|
|
|
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.
|
|
|
|
A wallet MUST NOT attempt to spend a TXO that is not confirmed-spendable.
|
|
|
|
Note: the definition of a TXO includes outputs in mempool transactions
|
|
that are unconflicted from the perspective of the wallet.
|
|
|
|
|
|
Reporting of balances
|
|
---------------------
|
|
|
|
Wallets SHOULD report:
|
|
|
|
* Confirmed-spendable balance.
|
|
* Pending balance, *or* total balance.
|
|
|
|
These are calculated as follows:
|
|
|
|
* The confirmed-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 confirmed-spendable balance plus the pending
|
|
balance.
|
|
|
|
Note: the definition of "confirmed-spendable" above ensures that:
|
|
|
|
* if auto-shielding is enabled, transparent funds will be reported in
|
|
the pending or total balance, but not in the confirmed-spendable
|
|
balance;
|
|
* if the wallet is not synchronized, the confirmed-spendable balance
|
|
will be zero.
|
|
|
|
If auto-shielding is disabled, the wallet MAY report shielded and
|
|
transparent balances separately. If it does so, it MUST make clear
|
|
whether each reported balance corresponds to a confirmed-spendable,
|
|
pending, or total subset of funds.
|
|
|
|
Rationale for reporting of balances
|
|
'''''''''''''''''''''''''''''''''''
|
|
|
|
If auto-shielding is disabled, then separate shielded and transparent
|
|
balances (and potentially, for expert users, separate shielded balances
|
|
per pool) can constitute useful information. If auto-shielding is enabled
|
|
then the wallet can and will automatically spend transparent TXOs in
|
|
order to shield them, and so transparent TXOs need to be presented as
|
|
pending, not as part of the balance spendable by the user.
|
|
|
|
TODO: The specification of balance reporting is intended to give the user
|
|
visibility into the operation of auto-shielding, opportunistic shielding,
|
|
and pool migration/usage. (Does the spec satisfy this?)
|
|
|
|
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-spendable 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`_.
|
|
|
|
|
|
Transaction creation
|
|
--------------------
|
|
|
|
Anchor selection
|
|
''''''''''''''''
|
|
|
|
A wallet SHOULD choose an anchor a number of blocks back from the head of the
|
|
chain equal to the trusted confirmation depth. That is, if the current block
|
|
is at height H, the anchor SHOULD reflect the final treestate of the block at
|
|
height H-3.
|
|
|
|
|
|
Rationale for anchor selection
|
|
''''''''''''''''''''''''''''''
|
|
|
|
* If the chain rolls back past the block at which the anchor is chosen, then
|
|
the anchor and the transaction will be invalidated. This is undesirable
|
|
both for reliability, and because the nullifiers of spent notes will have
|
|
been revealed, linking this transaction to any future transactions that
|
|
spend those notes.
|
|
* On the other hand, it is undesirable to choose an anchor too many blocks
|
|
back, because that prevents more recently received notes from being spent.
|
|
* Using a fixed anchor depth (as opposed to a different depth depending on
|
|
whether or not we are spending trusted notes) avoids leaking information
|
|
about whether or not we spent trusted notes.
|
|
|
|
|
|
Note selection
|
|
''''''''''''''
|
|
|
|
TODO: consider what we should do when nullifiers are revealed in a transaction
|
|
that is then invalidated. Should those notes be prioritized to be spent soon,
|
|
or should they be used in a note management tx?
|
|
|
|
|
|
Expiration height
|
|
'''''''''''''''''
|
|
|
|
A wallet SHOULD create transactions using the default expiration height of
|
|
40 blocks from the current height, as specified in [#zip-0203]_.
|
|
|
|
|
|
Linkability of transactions or addresses
|
|
----------------------------------------
|
|
|
|
TODO: dusting attack mitigation
|
|
|
|
|
|
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 release height and End-of-Service 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 time 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). TODO: express this in a less
|
|
zcashd-specific way.
|
|
|
|
TODO: Mention recommendations (not requirements) of receiver types based on
|
|
settled ('accepted') network upgrades, as defined in §3.3 of the
|
|
Zcash Protocol Specification, at the time of the release of the wallet.
|
|
|
|
TODO: Rationale subsection explaining why earliest block height at detection
|
|
and the rules/recommendations in place at that block height are preferred
|
|
over showing different UAs at different heights
|
|
|
|
References
|
|
==========
|
|
|
|
.. [#RFC2119] `RFC 2119: Key words for use in RFCs to Indicate Requirement Levels <https://www.rfc-editor.org/rfc/rfc2119.html>`_
|
|
.. [#zip-0203] `ZIP 203: Transaction Expiry <zip-0203.rst>`_
|