Best Practices for Shielded Note Management and Networking Considerations

I am unable to create a new topic, so I am sharing this OPSEC research here instead. Moderators: please feel free to move this to its own topic if you wish.

Best Practices for Shielded Note Management and Networking Considerations

The following are some recommendations from my independent research into best practices for shielded note management. Feedback is appreciated. I’m posting anon for this one.

Networking Considerations

I mention this first because, while it doesn’t have anything to do with note management, if you get this wrong then nothing below matters and your transactions will be linked to your IP address.

A recently leaked Chainalysis video revealed that Chainalysis runs many nodes and attempts to make connections to all other nodes on the network. Then for each new transaction they see, they log the IP address of the first node that gossiped that transaction to one of Chainalysis’ nodes. This allows Chainalysis to identify through which node a given transaction was first submitted with a high level of certainty.

It is thus of critical importance to your privacy that you DO NOT use your own personal node to broadcast your transactions, because doing so reveals that the transaction was initiated from your (node’s) IP address.

The leaked video also reveals that Chainalysis runs many public RPC servers and records the IP addresses and RPC requests of computers that use them. This means that if you unknowingly connect to one of these malicious RPCs and use it to broadcast your transaction, they will link your IP address to your transaction.

These are not hypothetical or rare attacks. These are two of the primary methods that adversaries like Chainalysis use to weaken user privacy. For nearly every transaction that takes place on every major cryptocurrency network, Chainalysis (and likely others) can do a DB query and see an associated IP address of either the node or the RPC user that submitted that transaction.

The takeaway: to best protect your privacy you should use a public, third-party node and interact with it only over a VPN or Tor.

Advanced users can use their own personal nodes to read from the chain while using public third-party nodes over VPN/Tor to write to it.

In the future perhaps we’ll see Zcash nodes implement a gossiping protocol like Dandelion, which introduces delays between gossips in order to thwart these kinds of gossip timing attacks. The leaked video suggested that such protocols are effective at thwarting these kinds of attacks in practice.

Note Management

The overarching principle behind good note management is to accomplish your task (sending, receiving, and storing money) while leaking as little information as possible. All of the following recommendations stem from this principle.

All transactions should use a standard fee chosen by a popular wallet.

Receiving money

  • Ideally always receive money to a shielded address if possible.

  • Preferably receive money to a unique shielded address.
    So you do not unnecessarily reveal to a subset of people who paid you that they paid the same person.

If you MUST us a transparent address then consider the following recommendations:

  • Preferably receive money to a unique transparent address.
    So you do not unnecessarily reveal that a subset of people who paid you have paid the same person.

  • The only thing you ever do with a transparent UTXO is sweep it into one of your own shielded accounts.

  • When sweeping transparent UTXOs into your your shielded account, the sweep transaction should follow these recommendations.

    • A sweep transaction’s inputs should have ONLY UTXOs for a SINGLE transparent address.
      So you do not unnecessarily reveal that two or more of your transparent addresses are linked.

    • A sweep transaction’s inputs should have NO shielded inputs.
      So you do not unnecessarily reveal that the owner of the transparent address already has a non-zero amount of shielded funds. That is, so you do not unnecessarily reveal that you are not a first-time user of the Zcash network.

    • A sweep transaction’s input should have EXACTLY ONE shielded output, one that is controlled by you.
      So an observer cannot know that you have more than one shielded note. This is the minimum amount of information possible for such a transaction.

Internal note management

Do these actions after receiving any value to your wallet and before sending any value out of your wallet.

  • Make sure all transparent UTXOs have been swept into a shielded address as outlined above.

  • Consolidation: If you have more than one shielded note then consolidate them all into a single note as follows.

    • Each consolidation transaction’s input should have EXACTLY TWO notes and its output should have EXACTLY ONE note that has the combined value (less the transaction fee).
      This prevents leaking any unnecessary information about how many notes the transaction sender owns.

    • Repeat until you have only one note that holds all of your shielded funds.

  • Note value truncation: If your single note has any non-zero decimals after the gas-price decimals, then truncate the note value as follows.

    • The truncation transaction’s input should have EXACTLY ONE note in (your only note) and it’s output should have EXACTLY TWO notes out: a new primary note with the sub-gas-decimals zeroed out, and the rest in an undependable note that holds the dust. The “dust note” should be discarded.
      In practice the dust can simply be sent to a shielded address in a throw-away wallet that you delete. This thwarts any dust-tracking attacks.

Sending money to other people

Ideally you will always send money to a receiver’s shielded address. When you send money to a receiver’s shielded address:

  • The spend transaction’s input should contain EXACTLY ONE input: your one and only shielded note.
    So you do not unnecessarily reveal any more information about the number of notes you have than is necessary.

  • The spend transaction’s output should contain EXACTLY TWO outputs: the receiver’s shielded note, and a shielded change address for you. The change address is important: you MUST NOT spend the entirety of your input note to the receiver. You MUST send yourself a non-zero amount of change.
    So you do not unnecessarily reveal what your exact shielded balance was to your receiver.

If you must send money to a receiver’s transparent address:

  • The spend transaction’s input should contain EXACTLY ONE input: your one and only shielded note.
    So you do not unnecessarily reveal any more information about the number of notes you have than is necessary.

  • The spend transaction’s output should contain EXACTLY TWO outputs: the receiver’s new transparent UTXO, and a shielded change address for you. As above, the change address is important.

In both cases, do not use the memo field unless absolutely necessary, as it leaks info the receiver. Retrieving memo info can also leak info to third-party RPC providers, so it best to only retrieve memo data if you need to.

After each spend of money, redo any internal note management steps that are required.

Considerations for More Advanced Threats

Ephemeral wallet hops

If it is important to you that there is no way to prove that you sent someone money, even if you are wrench-attacked into revealing your wallet private seed, the following procedure works:

  • Be sure you are following recommendations from the Networking Considerations section.
  • Create a new, ephemeral wallet.
  • Send some funds from your existing shielded address to the shielded address in the ephemeral wallet.
  • Send the funds from the ephemeral wallet to the final receiver with an empty memo field.
  • Securely delete all key material related to the ephemeral wallet.
  • After that, nobody, not even you, can prove that you sent money to the receiver.

Unknowns

I have not been able to wrap my head around the implications of moving funds from one shielded pool to another in an optimal information-preserving way. Any insights here would be greatly appreciated.

Tip jar

In case you’d like to support this kind of research.

orchard:
u1h3cevhydssr6crutxr4enur99l8gedg7a48wmqxua59z0j8fpfyhpjpxe5hh5f90x07desgenudgustr3fypn52qh9zxa92hzgfmt9ck

sapling:
zs1a570qynmfxh80qfnsptx2407utwk3cs7qwqnzyk8uq78udwrvwwk3q0lkv8mhjqnvw6ckwkfnwh

I appreciate any thoughts, criticisms, and other ideas you have related to note management.

Thank you.

10 Likes

I agree with most of this. However, I disagree with the following:

Consolidating to a single note means that you can only make one payment, without incurring significant latency. To make multiple payments, you must wait for change to be received. In fact the change note(s) must have several confirmations: to avoid metadata leakage via anchor depth it’s necessary for anchors to be chosen at a fixed depth. That depth cannot be too shallow because it would result in unreliability due to orphans and short rollbacks (if the anchor block is rolled back then there is another kind of metadata leakage via repeated nullifiers, when you try to spend the same notes again). Change notes newer than the anchor block can’t be spent, which results in significant spend-to-spend latency unless you have multiple notes. This is a serious practical problem (one that we’ll be addressing soon in Zashi, and making recommendations about in ZIP 315).

I understand the privacy problem you’re trying to solve: if you need “too many” notes to fund a transaction then it will be distinguishable from other transactions. An adversary can send many small notes to an address (sometimes called a “dusting attack”) and observe transactions that are more likely to be spending from that address, assuming the target has to dip into those notes in order to fund a given payment. Under the right conditions when there are few transactions on the network in the relevant timeframe, that can result in deanonymization.

However, it is not necessary to consolidate to a single note in order to make transactions indistinguishable. For example, suppose that it is common for users to want to make up to 6 concurrent payments. There could be a convention that (assuming Orchard for simplicity), transactions are padded to 6 actions. Then, if your note management targets always having 6 notes (with value distribution tailored to make it more likely that you’ll be able to fund any given payment using few notes), then you would still be able to spend all your wealth in a single transaction. But in the common case where you’re only spending a small proportion of your wealth, you will need fewer notes (usually only one) for each payment, and therefore will be able to make up to 6 concurrent payments.

By itself that policy would significantly increase the cost of output scanning. However, it’s also possible to assign some of the 6 outputs of a “standard” transaction to external payments, and some to change or note management. Then, the former would only need to be scanned with an external IVK and the latter only with an internal IVK, halving the scanning cost. (This is an incompatible protocol change and so needs to be coordinated, whereas the “standard transactions have 6 actions” change could be adopted independently by wallets even without an NU.)

Having 6 actions also allows consolidation (and splitting, if you end up with fewer than 6 notes) to be done more efficiently when it is needed.

This is unnecessary. I’m not aware of any dust-tracking attacks that it would be needed for, in the case of fully shielded transactions. Shielded change values are private, and dust-tracking via leakage of the number of actions was fully addressed above by making transactions indistinguishable.

(On the other hand, notes with value less than the marginal fee are economically useless in any case, so you should discard those. Ideally the protocol would prohibit them, although that isn’t strictly necessary. This isn’t the same as discarding sub-marginal-fee decimals.)

It’s more efficient to do this in the same transaction as far as possible, which would be facilitated by padding to (e.g.) 6 actions.

Incidentally, making transactions indistinguishable also simplifies aggregation: Shielded transaction aggregates · Issue #4946 · zcash/zcash · GitHub

It might be preferable for the standard number of actions to be smaller than the target number of notes (say, pad to 4 actions, and set the note management target to 7 notes). This doesn’t allow you to spend all of your wealth at once unless you do a note management transaction first, but you only need one such extra transaction (4 notes → 1 and then spend that and the other 3 notes), and it’s more efficient in the common case for proving, verification, and output scanning.

6 Likes

This is incorrect; notes with value less than the marginal value can always be included instead of dummy inputs.

3 Likes

True, I was slightly oversimplifying. I still think it would be preferable for the protocol to just prohibit notes with value greater than zero and less than or equal to the marginal fee (or possibly, less than double the marginal fee, so that each note contributes a nonnegligable amount after paying the fee for it).

Note that zcash_client_backend already avoids creating “dust” notes less than or equal to the marginal fee [1], so it already incurs that complexity. (The marginal fee shouldn’t be baked into circuits, but it doesn’t necessarily need to be done at that layer in order to be prohibited by the protocol in the sense I mean.)

[1] “Dust” means two different things for the shielded and transparent protocols. We should fix this by adopting the marginal fee (or double the marginal fee) as the uniform dust threshold.

2 Likes

Ywallet has supported diversified (called them snap) addresses for a long time. Shielded addresses never appear on-chain, but I understand the use for being able to generate new ones.

1 Like

I don’t see how this can be done in the protocol without revealing amounts. Also, smaller denominations of ZEC may become relevant if scalability enhancements eventually cause fees to decrease.

Effectively, what you’re suggesting would be equivalent to limiting ZEC values to only 4 decimal places instead of 8. My hope is that some day we’ll need to add decimal places instead!

1 Like

(or if the price increases, so that the ZEC-denominated fee can decrease)

One way is to have the dust threshold as a circuit input. The dust threshold itself doesn’t need to be hidden, so it would be accessible to consensus rules outside the proof. (Since note values are already decomposed into 10-bit chunks in the Orchard circuit, the number of rows needed to do a range check against a public input is very small.)

Only temporarily, and yes I agree.

Honestly it frustrates me as well. I’ve been arguing the need to generate different addresses to give to different counterparties for years; literally every time address generation or UX comes up at ECC, I raise the issue again. Prior to becoming Engineering Manager I didn’t get much traction, but it’s on the near-term roadmap now for Zashi.

7 Likes

Thank you for the feedback. Great insights. I agree with all of that.
I hadn’t considered anchor blocks at all. Some great points there.

Re the “dust attacks”: what I had in mind there was the case where an attacker learns that one of your notes holds an amount of ZEC with identifiable sub-marginal-fee decimals – which may occur if they sent such an amount to one of your shielded addresses, or observed such an amount being shielded. If you then spent the entirety of that note (no shielded change note in this case), then the receiver (in the case of a spend to a shielded address) or everyone (in the case of a spend to a transparent address) may observe that the spender is probably the owner of the dusted-note.

If we always follow the rule that every spending transaction needs at least one shielded change note, then this is a non-issue, as you pointed out.

For the extra paranoid, they may want to do the note-value-truncation step I mentioned, so they don’t leak that dust info in the event that they accidentally spend an entire note in a transaction without having a shielded change note.

If wallets eventually enforce that shielded spend transactions must have at least one shielded change note, then there will be no benefit to considering that truncation step.

1 Like

Right, I was assuming that payments are always for a specified amount that is not decided based on the note values. That is not necessarily the case when migrating funds between pools, but that should be the only such case, and a wallet should only do that using fully shielded transfers to itself.

2 Likes

I agree, it should be rare.

I could also see it happening when someone is transferring their entire balance to an exchange that only uses transparent addresses. Like Coinbase or Binance.

2 Likes