opencbdc-tx icon indicating copy to clipboard operation
opencbdc-tx copied to clipboard

Explore strong Layer 1 privacy to hide sender, recipient, and amount

Open madars opened this issue 2 years ago • 0 comments

Question

Can strong Layer 1 privacy solutions, currently deployed in cryptocurrencies, be scaled to a level of a major payment system or a CBDC? What are trade-offs and implications for payment workflows, auditability of supply, and regulatory compliance?

Benefit

OpenCBDC is designed to store minimal information at the transaction processor core: the main data structure (UHS) consists of opaque UTXO hashes.

However, this is not enough for policymakers who desire strong privacy: the transactions submitted to OpenCDBC sentinels reveal sender and recipient addresses, as well as the payment amount. In a case of compromised sentinels (e.g., by foreign actors), the system could, in principle, retain the entire transaction graph. Similarly, compromised atomizer or 2PC logs might give limited insight into the transaction graph.

The best way to safeguard information is to never have it in the first place. This proposal describes how to explore strong cryptographic techniques, including, encryption and zero-knowledge proofs, to make sure that system operators can be confident as to never touch sensitive financial information in the clear.

Proposed Solution

The first step of our proposal is to explore the commonalities of successful L1 privacy coin designs, e.g. CryptoNote, Zerocash, Zexe, and others (implemented in Monero, Zcash, Aleo, and others), and their current and proposed extensions.

At the transaction processing level these designs share remarkable commonalities. For example, all of them maintain a list of all transaction outputs ever created. To spend a transaction output, one reveals a value uniquely derived from an output, but unlinkable by an external observer; these values are called nullifiers (also called key images in CryptoNote/Monero or serial numbers in the seminal work of Sander and Ta-Shma, and in Zerocash and Zexe. NB: "serial numbers" in OpenCBDC serve a different purpose, prompting us to adopt the nullifier nomenclature in this description).

To prove that the nullifier was derived from a valid, existing coin, the sender can, for example:

  • pick a set of other prior outputs (called decoys) and create a linkable ring signature involving the sender’s real output plus all the decoys; or
  • compute a Merkle tree of all preceding outputs, and prove that the coin the sender is spending is in this Merkle tree, and its nullifier is consistent with the one sender is revealing.

There are high-quality open-source implementations for the cryptographic components described above. What we propose exploring in the first phase of this work is to see the feasibility to integrate these with OpenCBDC.

At a high level, OpenCBDC uses UTXO hash set (UHS) and the core transaction processing is represented by a swap primitive (see Section 3.3 Transaction format and execution in the whitepaper). The inputs to swap are two lists of hashes: one for existence checks and removal (called input hashes), and one for insertion (called output hashes). To execute a swap, the system atomically checks that all input hashes are present. If an input hash is missing, swap aborts. Otherwise, it obtains an updated UHS by erasing all input hashes and inserting all output hashes.

To support efficient processing of private transactions, we would need to build a system that exposes a different core primitive; let’s call it append (better naming suggestions welcome!). Now, a system maintains a set N of nullifiers, and a list L of outputs. The inputs to append are also a set N’ of nullifiers (for non-existence checks and insertion in the nullifier set), and a list L’ of outputs (for insertion of the global list of outputs). To execute an append, the system atomically checks that none of N’ nullifiers are in N. If a nullifier is already present, append aborts. Otherwise, it updates the system state by adding nullifiers in N’ to N and appending hashes in L’ to L.

The required modifications at a sentinel level depend on the chosen privacy techniques:

  • To support ring signatures, a sentinel would need random access to all prior decoy outputs.
  • To support Merkle tree-based approaches, the system would periodically compute a Merkle tree over all outputs in L and publish it. A sentinel would need random access to all these prior Merkle tree roots.

Note that for the latter scenario, Merkle trees can be updated lazily (not atomically), and there can be a Merkle tree per shard. That is, for the higher level protocols it suffices to prove that a commitment is included in a shard-specific Merkle tree (potentially hiding the tree using tree-of-trees on top); not necessarily one globally maintained MT.

(We remark that, In principle, the non-existence checks can be implemented using the existing swap abstraction in a black-box way by storing unused nullifier intervals in UHS IDs. Then a transaction would consume such interval and produce one or two new intervals for the split. However, this adds application-layer complexity and a non-black-box implementation might be preferred.)

The above description is aimed just to start a discussion about Layer 1 privacy which is a complex topic, and involves many parts not mentioned here. For example, system would need different auxiliary services (similar to how OpenCBDC has watchtowers) to help users create their proofs (as users do not have ability to run a full node), it would require making decisions related to payment discovery (see issue #28), auditability, compliance, and so on.

Possible Difficulties

Successful execution of this project has a significant policy component, and will require clarity from policymakers (esp., related to compliance goals such as deterring illicit finance). This makes the project’s scope open-ended.

Prior Work

Code of Conduct

  • [X] I agree to follow this project's Code of Conduct

madars avatar Apr 20 '22 18:04 madars