ZIP: 315 Title: Best Practices for Wallet Implementations Owners: Daira-Emma Hopwood <daira-emma@electriccoin.co> Jack Grigg <jack@electriccoin.co> Kris Nuttycombe <kris@electriccoin.co> Credits: Francisco Gindre Status: Draft Category: Wallet Discussions-To: <https://github.com/zcash/zips/issues/447> Pull-Request: <https://github.com/zcash/zips/pull/607>
The key words "MUST", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", and "MAY" in this document are to be interpreted as described in BCP 14 1 when, and only when, they appear in all capitals.
The terms below are to be interpreted as follows:
TODO: Add informal definitions of: * known-spendable and confirmed-spendable TXOs; * trusted and untrusted TXOs.
These should forward-reference the specification section with the formal definitions.
Zcash wallets have to serve two purposes as a user agent:
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:
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.
Wallets need to take account of two main concerns with respect to accessibility of funds:
These concerns affect the way that balances should be computed, which notes are selected for spending, and how the wallet should ensure that sufficient notes are available to cover multiple spends within a short period.
To support this we define two kinds of TXOs:
Wallets can then require that untrusted TXOs need more confirmations before they become confirmed-spendable than trusted TXOs. This provides an improved trade-off between latency on the one hand, and reliability and safety on the other.
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 might have done so), then it SHOULD auto-shield such funds by default.
A shielding transaction is always linked to the transparent addresses it spends from. This can cause undesirable information leaks:
Despite the fact that it is not possible to achieve strong privacy guarantees from any use of transparent addresses, it is undesirable to reveal this additional information. In particular, issue b) motivates ruling out the use of "opportunistic shielding", i.e. shielding previously received transparent funds as part of a user-initiated transaction.
Wallets SHOULD NOT auto-shield from multiple transparent addresses in the same transaction, and SHOULD NOT use opportunistic shielding.
Following all of the recommendations in this section improves both collective privacy and the user's individual privacy, by maximizing the size of the note anonymity set over time and minimizing the potential linkage of shielding transactions to other transactions.
The remainder of this specification assumes a wallet that follows all of these recommendations, except where explicitly noted.
A wallet MAY allow users to disable auto-shielding, auto-migration, and/or opportunistic migration. If it does so, this need not be via three independent settings.
Automatic shielding and automatic/opportunistic migration SHOULD NOT be applied to inputs where the cost of shielding or migrating them will exceed their economic value. If these transactions are paying the ZIP 317 conventional fee 5, that will be the case if the amount of the UTXO to be shielded/migrated exceeds the marginal fee, and cannot be accommodated by an input that would be present in any case due to padding of the number of inputs from a given shielded pool.
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:
The rationale for choosing three confirmations for trusted TXOs is that anecdotally, reorgs are usually less than three blocks.
The consequences of attempting to spend a trusted TXO may be less severe in the case of a rollback than the consequences of attempting to spend an untrusted TXO. The value received from a trusted TXO should always be recoverable, whereas recovering value received from an untrusted TXO may require the user to request that funds are re-sent.
A TXO is known-spendable, relative to a given block chain and wallet state, if and only if all of the following are true in that state:
TODO: consider undoing the up-to-date part, as when combined with the definition of balance below, it causes wallet balance to drop to zero in the short window between opening and updating the wallet's chain tip view.
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:
A TXO is unconfirmed-spendable, relative to a given block chain and wallet state, if and only if the TXO is known-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 in a user-initiated transaction that is not confirmed-spendable.
Open question: what should be specified about which TXOs can be spent in non-user-initiated transactions?
Note: the definition of a TXO includes outputs in mempool transactions that are unconflicted from the perspective of the wallet.
Wallets SHOULD report:
These are calculated as follows:
Note: the definition of "confirmed-spendable" above ensures that:
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.
If auto-shielding is disabled, then separate shielded and transparent balances 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.
Potentially, for expert users, separate shielded balances per pool could also be useful.
Open question: Does the specification of balance reporting give the user sufficient visibility into the operation of auto-shielding and pool migration/usage?
If a transaction includes watch-only received TXOs, its watch-only incoming balance MUST be reported separately to any potentially known-spendable balance.
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.
A transaction is sent if it was either:
Sent transactions SHOULD be reported with their number of confirmations, an estimate of how long until they expire (if they are unmined and have an expiry height), and their balances as described in Reporting of balances.
The privacy provided by a Zcash transaction depends on the information leaked in the creation of that transaction and the process of it being broadcast for inclusion in the block chain.
The requirements in this section are intended to minimize the leakage of such information where possible, and to ensure that the user is informed of any remaining information that would be leaked, and consents to such leakage.
The list below describes the kinds of information that might be leaked. After a candidate transaction has been created and prior to it being revealed outside a trusted path to the user, the wallet user interface SHOULD obtain the user's consent for all of the leaked information.
Assumption: There is always a transaction confirmation step for transactions that send funds out of the wallet.
It is RECOMMENDED to use v5 transactions unless Sprout funds are being spent.
SHOULD be zero.
See Anchor selection below.
These give information about what block height the creator was synced to, and some policy information.
See Linkability of transactions or addresses.
TODO: we SHOULD try to create fully shielded transactions where possible.
We want to support creating unlinkable addresses, in order that a user can give different addresses to different counterparties, in such a way that the counterparties (even if they collude) cannot tell that the addresses were provided by the same or distinct users.
If multiple UTXOs are received at the same transparent address, it is safe to shield them all in the same transaction, because that is not leaking additional information.
UTXOs received on different transparent receivers SHOULD NOT be shielded in the same transaction. Ideally, when they are shielded in separate transactions, this should be done in such a way that the timing of those transactions is not linkable.
TODO: move this. Daira-Emma thinks that if we only document leakage rather than explicitly say in the specification of how to construct transactions how to mitigate it, then implementors will ignore it.
When spending transparent UTXOs, they SHOULD only be sent to an internal shielded receiver belonging to the wallet, except when they are generated and spent ephemerally as part of a ZIP 320 transfer 6.
A wallet MUST NOT send funds to a transparent address unless all of the source funds come from shielded pools, and this SHOULD be a single shielded pool.
We want to minimize pool crossing.
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.
TODO: Define a parameter for this depth, and then RECOMMEND a value of 3.
TODO: Reword this given the proposed note management mitigation below.
TODO: recommend that when nullifiers are revealed in a transaction that is then invalidated (or revealed in some other detectable way), they SHOULD be used in a note management tx to avoid linking the invalidated tx with some arbitrary future tx. Provided that there are no transparent outputs leaving this wallet's control, the same arities and transparent outputs SHOULD be preserved, which also avoids revealing whether the user changed their mind about whether to send the original semantic transaction.
A wallet SHOULD create transactions using the default expiration height of 40 blocks from the current height, as specified in 3.
How should wallet developers time transactions to avoid linkability?
TODO: dusting attack mitigation
What they are supposed to reveal; see ZIP 310 for Sapling (needs updating for Orchard). https://github.com/zcash/zips/issues/606
Note: wallets MAY further restrict the set of transfers they perform.
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.
TODO: Reword this to define the source transparent address as any transparent address (external, internal, or ephemeral), except in the case of ephemeral as allowed for implementing ZIP 320.
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 MUST be one of:
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 publically (as opposed to revealing them to the holder of a viewing key authorized to see that amount). Therefore, there should be per-transaction opt-in for any transfer that publically reveals amounts on chain.
Wallets MUST NOT automatically combine funds across pools to satisfy a transfer (since that could reveal the total funds the user holds 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:
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.
Properties we want from auto-shielding:
Properties we want from auto-migration:
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?
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.
z_getaddressforaccount
, show that UA as expected (matching the receiver types the user selected).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
1 | Information on BCP 14 — "RFC 2119: Key words for use in RFCs to Indicate Requirement Levels" and "RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words" |
---|
2 | ZIP 32: Shielded Hierarchical Deterministic Wallets |
---|
3 | ZIP 203: Transaction Expiry |
---|
4 | ZIP 316: Unified Addresses and Unified Viewing Keys |
---|
5 | ZIP 317: Proportional Transfer Fee Mechanism |
---|
6 | ZIP 320: Defining an Address Type to which funds can only be sent from Transparent Addresses |
---|